1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "RenderBase.h"
18 
19 #include "glError.h"
20 
21 #include <log/log.h>
22 #include <ui/GraphicBuffer.h>
23 
24 namespace android {
25 namespace automotive {
26 namespace evs {
27 namespace support {
28 
29 // Eventually we shouldn't need this dependency, but for now the
30 // graphics allocator interface isn't fully supported on all platforms
31 // and this is our work around.
32 using ::android::GraphicBuffer;
33 
34 // OpenGL state shared among all renderers
35 EGLDisplay RenderBase::sDisplay = EGL_NO_DISPLAY;
36 EGLContext RenderBase::sContext = EGL_NO_CONTEXT;
37 EGLSurface RenderBase::sMockSurface = EGL_NO_SURFACE;
38 GLuint RenderBase::sFrameBuffer = -1;
39 GLuint RenderBase::sColorBuffer = -1;
40 GLuint RenderBase::sDepthBuffer = -1;
41 EGLImageKHR RenderBase::sKHRimage = EGL_NO_IMAGE_KHR;
42 unsigned RenderBase::sWidth = 0;
43 unsigned RenderBase::sHeight = 0;
44 float RenderBase::sAspectRatio = 0.0f;
45 
prepareGL()46 bool RenderBase::prepareGL() {
47     // Just trivially return success if we're already prepared
48     if (sDisplay != EGL_NO_DISPLAY) {
49         return true;
50     }
51 
52     // Hardcoded to RGBx output display
53     const EGLint config_attribs[] = {// Tag                  Value
54                                      EGL_RENDERABLE_TYPE,
55                                      EGL_OPENGL_ES2_BIT,
56                                      EGL_RED_SIZE,
57                                      8,
58                                      EGL_GREEN_SIZE,
59                                      8,
60                                      EGL_BLUE_SIZE,
61                                      8,
62                                      EGL_NONE};
63 
64     // Select OpenGL ES v 3
65     const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
66 
67     // Set up our OpenGL ES context associated with the default display (though we won't be visible)
68     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
69     if (display == EGL_NO_DISPLAY) {
70         ALOGE("Failed to get egl display");
71         return false;
72     }
73 
74     EGLint major = 0;
75     EGLint minor = 0;
76     if (!eglInitialize(display, &major, &minor)) {
77         ALOGE("Failed to initialize EGL: %s", getEGLError());
78         return false;
79     } else {
80         ALOGI("Intiialized EGL at %d.%d", major, minor);
81     }
82 
83     // Select the configuration that "best" matches our desired characteristics
84     EGLConfig egl_config;
85     EGLint num_configs;
86     if (!eglChooseConfig(display, config_attribs, &egl_config, 1, &num_configs)) {
87         ALOGE("eglChooseConfig() failed with error: %s", getEGLError());
88         return false;
89     }
90 
91     // Create a placeholder pbuffer so we have a surface to bind -- we never intend to draw to this
92     // because attachRenderTarget will be called first.
93     EGLint surface_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
94     sMockSurface = eglCreatePbufferSurface(display, egl_config, surface_attribs);
95     if (sMockSurface == EGL_NO_SURFACE) {
96         ALOGE("Failed to create OpenGL ES Mock surface: %s", getEGLError());
97         return false;
98     } else {
99         ALOGI("Mock surface looks good!  :)");
100     }
101 
102     //
103     // Create the EGL context
104     //
105     EGLContext context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attribs);
106     if (context == EGL_NO_CONTEXT) {
107         ALOGE("Failed to create OpenGL ES Context: %s", getEGLError());
108         return false;
109     }
110 
111     // Activate our render target for drawing
112     if (!eglMakeCurrent(display, sMockSurface, sMockSurface, context)) {
113         ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError());
114         return false;
115     } else {
116         ALOGI("We made our context current!  :)");
117     }
118 
119     // Report the extensions available on this implementation
120     const char* gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
121     ALOGI("GL EXTENSIONS:\n  %s", gl_extensions);
122 
123     // Reserve handles for the color and depth targets we'll be setting up
124     glGenRenderbuffers(1, &sColorBuffer);
125     glGenRenderbuffers(1, &sDepthBuffer);
126 
127     // Set up the frame buffer object we can modify and use for off screen rendering
128     glGenFramebuffers(1, &sFrameBuffer);
129     glBindFramebuffer(GL_FRAMEBUFFER, sFrameBuffer);
130 
131     // Now that we're assured success, store object handles we constructed
132     sDisplay = display;
133     sContext = context;
134 
135     return true;
136 }
137 
attachRenderTarget(const BufferDesc & tgtBuffer)138 bool RenderBase::attachRenderTarget(const BufferDesc& tgtBuffer) {
139     // Hardcoded to RGBx for now
140     if (tgtBuffer.format != HAL_PIXEL_FORMAT_RGBA_8888) {
141         ALOGE("Unsupported target buffer format");
142         return false;
143     }
144 
145     // create a GraphicBuffer from the existing handle
146     sp<GraphicBuffer> pGfxBuffer =
147             new GraphicBuffer(tgtBuffer.memHandle, GraphicBuffer::CLONE_HANDLE, tgtBuffer.width,
148                               tgtBuffer.height, tgtBuffer.format, 1,  // layer count
149                               GRALLOC_USAGE_HW_RENDER, tgtBuffer.stride);
150     if (pGfxBuffer.get() == nullptr) {
151         ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
152         return false;
153     }
154 
155     // Get a GL compatible reference to the graphics buffer we've been given
156     EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
157     EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
158     sKHRimage = eglCreateImageKHR(sDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf,
159                                   eglImageAttributes);
160     if (sKHRimage == EGL_NO_IMAGE_KHR) {
161         ALOGE("error creating EGLImage for target buffer: %s", getEGLError());
162         return false;
163     }
164 
165     // Construct a render buffer around the external buffer
166     glBindRenderbuffer(GL_RENDERBUFFER, sColorBuffer);
167     glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(sKHRimage));
168     if (eglGetError() != EGL_SUCCESS) {
169         ALOGI("glEGLImageTargetRenderbufferStorageOES => %s", getEGLError());
170         return false;
171     }
172 
173     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sColorBuffer);
174     if (eglGetError() != EGL_SUCCESS) {
175         ALOGE("glFramebufferRenderbuffer => %s", getEGLError());
176         return false;
177     }
178 
179     GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
180     if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
181         ALOGE("Offscreen framebuffer not configured successfully (%d: %s)", checkResult,
182               getGLFramebufferError());
183         return false;
184     }
185 
186     // Store the size of our target buffer
187     sWidth = tgtBuffer.width;
188     sHeight = tgtBuffer.height;
189     sAspectRatio = (float)sWidth / sHeight;
190 
191     // Set the viewport
192     glViewport(0, 0, sWidth, sHeight);
193 
194 #if 1  // We don't actually need the clear if we're going to cover the whole screen anyway
195     // Clear the color buffer
196     glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
197     glClear(GL_COLOR_BUFFER_BIT);
198 #endif
199 
200     return true;
201 }
202 
detachRenderTarget()203 void RenderBase::detachRenderTarget() {
204     // Drop our external render target
205     if (sKHRimage != EGL_NO_IMAGE_KHR) {
206         eglDestroyImageKHR(sDisplay, sKHRimage);
207         sKHRimage = EGL_NO_IMAGE_KHR;
208     }
209 }
210 
211 }  // namespace support
212 }  // namespace evs
213 }  // namespace automotive
214 }  // namespace android
215