• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 "GlWrapper.h"
18 
19 #include <ui/DisplayMode.h>
20 #include <ui/DisplayState.h>
21 #include <ui/GraphicBuffer.h>
22 
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <sys/ioctl.h>
26 #include <utility>
27 
28 using android::GraphicBuffer;
29 using android::sp;
30 
31 namespace {
32 
33 // Defines a default color to clear the screen in RGBA format
34 constexpr float kDefaultColorInRgba[] = {0.1f, 0.5f, 0.1f, 1.0f};
35 
36 // Defines the size of the preview area relative to the entire display
37 constexpr float kDisplayAreaRatio = 0.8f;
38 
39 constexpr const char vertexShaderSource[] =
40         ""
41         "#version 300 es                    \n"
42         "layout(location = 0) in vec4 pos;  \n"
43         "layout(location = 1) in vec2 tex;  \n"
44         "out vec2 uv;                       \n"
45         "void main()                        \n"
46         "{                                  \n"
47         "   gl_Position = pos;              \n"
48         "   uv = tex;                       \n"
49         "}                                  \n";
50 
51 constexpr const char pixelShaderSource[] =
52         "#version 300 es                    \n"
53         "precision mediump float;           \n"
54         "uniform sampler2D tex;             \n"
55         "in vec2 uv;                        \n"
56         "out vec4 color;                    \n"
57         "void main()                        \n"
58         "{                                  \n"
59         "    vec4 texel = texture(tex, uv); \n"
60         "    color = texel;                 \n"
61         "}                                  \n";
62 
getEGLError(void)63 const char* getEGLError(void) {
64     switch (eglGetError()) {
65         case EGL_SUCCESS:
66             return "EGL_SUCCESS";
67         case EGL_NOT_INITIALIZED:
68             return "EGL_NOT_INITIALIZED";
69         case EGL_BAD_ACCESS:
70             return "EGL_BAD_ACCESS";
71         case EGL_BAD_ALLOC:
72             return "EGL_BAD_ALLOC";
73         case EGL_BAD_ATTRIBUTE:
74             return "EGL_BAD_ATTRIBUTE";
75         case EGL_BAD_CONTEXT:
76             return "EGL_BAD_CONTEXT";
77         case EGL_BAD_CONFIG:
78             return "EGL_BAD_CONFIG";
79         case EGL_BAD_CURRENT_SURFACE:
80             return "EGL_BAD_CURRENT_SURFACE";
81         case EGL_BAD_DISPLAY:
82             return "EGL_BAD_DISPLAY";
83         case EGL_BAD_SURFACE:
84             return "EGL_BAD_SURFACE";
85         case EGL_BAD_MATCH:
86             return "EGL_BAD_MATCH";
87         case EGL_BAD_PARAMETER:
88             return "EGL_BAD_PARAMETER";
89         case EGL_BAD_NATIVE_PIXMAP:
90             return "EGL_BAD_NATIVE_PIXMAP";
91         case EGL_BAD_NATIVE_WINDOW:
92             return "EGL_BAD_NATIVE_WINDOW";
93         case EGL_CONTEXT_LOST:
94             return "EGL_CONTEXT_LOST";
95         default:
96             return "Unknown error";
97     }
98 }
99 
100 // Given shader source, load and compile it
loadShader(GLenum type,const char * shaderSrc)101 GLuint loadShader(GLenum type, const char* shaderSrc) {
102     // Create the shader object
103     GLuint shader = glCreateShader(type);
104     if (shader == 0) {
105         LOG(ERROR) << "glCreateSharder() failed with error = " << glGetError();
106         return 0;
107     }
108 
109     // Load and compile the shader
110     glShaderSource(shader, 1, &shaderSrc, nullptr);
111     glCompileShader(shader);
112 
113     // Verify the compilation worked as expected
114     GLint compiled = 0;
115     glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
116     if (!compiled) {
117         LOG(ERROR) << "Error compiling shader";
118 
119         GLint size = 0;
120         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
121         if (size > 0) {
122             // Get and report the error message
123             char infoLog[size];
124             glGetShaderInfoLog(shader, size, nullptr, infoLog);
125             LOG(ERROR) << "  msg:" << std::endl << infoLog;
126         }
127 
128         glDeleteShader(shader);
129         return 0;
130     }
131 
132     return shader;
133 }
134 
135 // Create a program object given vertex and pixels shader source
buildShaderProgram(const char * vtxSrc,const char * pxlSrc)136 GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
137     GLuint program = glCreateProgram();
138     if (program == 0) {
139         LOG(ERROR) << "Failed to allocate program object";
140         return 0;
141     }
142 
143     // Compile the shaders and bind them to this program
144     GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
145     if (vertexShader == 0) {
146         LOG(ERROR) << "Failed to load vertex shader";
147         glDeleteProgram(program);
148         return 0;
149     }
150     GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
151     if (pixelShader == 0) {
152         LOG(ERROR) << "Failed to load pixel shader";
153         glDeleteProgram(program);
154         glDeleteShader(vertexShader);
155         return 0;
156     }
157     glAttachShader(program, vertexShader);
158     glAttachShader(program, pixelShader);
159 
160     // Link the program
161     glLinkProgram(program);
162     GLint linked = 0;
163     glGetProgramiv(program, GL_LINK_STATUS, &linked);
164     if (!linked) {
165         LOG(ERROR) << "Error linking program";
166         GLint size = 0;
167         glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
168         if (size > 0) {
169             // Get and report the error message
170             char* infoLog = (char*)malloc(size);
171             glGetProgramInfoLog(program, size, nullptr, infoLog);
172             LOG(ERROR) << "  msg:  " << infoLog;
173             free(infoLog);
174         }
175 
176         glDeleteProgram(program);
177         glDeleteShader(vertexShader);
178         glDeleteShader(pixelShader);
179         return 0;
180     }
181 
182     return program;
183 }
184 
185 }  // namespace
186 
187 namespace android::hardware::automotive::evs::V1_1::implementation {
188 
189 // Main entry point
initialize(const sp<IAutomotiveDisplayProxyService> & service,uint64_t displayId)190 bool GlWrapper::initialize(const sp<IAutomotiveDisplayProxyService>& service, uint64_t displayId) {
191     LOG(DEBUG) << __FUNCTION__;
192 
193     if (!service) {
194         LOG(WARNING) << "IAutomotiveDisplayProxyService is invalid.";
195         return false;
196     }
197 
198     // We will use the first display in the list as the primary.
199     service->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
200         ui::DisplayMode* pConfig = reinterpret_cast<ui::DisplayMode*>(dpyConfig.data());
201         mWidth = pConfig->resolution.getWidth();
202         mHeight = pConfig->resolution.getHeight();
203 
204         ui::DisplayState* pState = reinterpret_cast<ui::DisplayState*>(dpyState.data());
205         if (pState->orientation != ui::ROTATION_0 && pState->orientation != ui::ROTATION_180) {
206             // rotate
207             std::swap(mWidth, mHeight);
208         }
209 
210         LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
211     });
212 
213     mGfxBufferProducer = service->getIGraphicBufferProducer(displayId);
214     if (mGfxBufferProducer == nullptr) {
215         LOG(ERROR) << "Failed to get IGraphicBufferProducer from IAutomotiveDisplayProxyService.";
216         return false;
217     }
218 
219     mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
220     if (mSurfaceHolder == nullptr) {
221         LOG(ERROR) << "Failed to get a Surface from HGBP.";
222         return false;
223     }
224 
225     mWindow = getNativeWindow(mSurfaceHolder.get());
226     if (mWindow == nullptr) {
227         LOG(ERROR) << "Failed to get a native window from Surface.";
228         return false;
229     }
230 
231     // Set up our OpenGL ES context associated with the default display
232     mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
233     if (mDisplay == EGL_NO_DISPLAY) {
234         LOG(ERROR) << "Failed to get egl display";
235         return false;
236     }
237 
238     EGLint major = 3;
239     EGLint minor = 0;
240     if (!eglInitialize(mDisplay, &major, &minor)) {
241         LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
242         return false;
243     }
244 
245     const EGLint config_attribs[] = {
246             // Tag                  Value
247             EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE};
248 
249     // Pick the default configuration without constraints (is this good enough?)
250     EGLConfig egl_config = {0};
251     EGLint numConfigs = -1;
252     eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
253     if (numConfigs != 1) {
254         LOG(ERROR) << "Didn't find a suitable format for our display window";
255         return false;
256     }
257 
258     // Create the EGL render target surface
259     mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
260     if (mSurface == EGL_NO_SURFACE) {
261         LOG(ERROR) << "eglCreateWindowSurface failed: " << getEGLError();
262         ;
263         return false;
264     }
265 
266     // Create the EGL context
267     // NOTE:  Our shader is (currently at least) written to require version 3, so this
268     //        is required.
269     const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
270     mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
271     if (mContext == EGL_NO_CONTEXT) {
272         LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
273         return false;
274     }
275 
276     // Activate our render target for drawing
277     if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
278         LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
279         return false;
280     }
281 
282     // Create the shader program for our simple pipeline
283     mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
284     if (!mShaderProgram) {
285         LOG(ERROR) << "Failed to build shader program: " << getEGLError();
286         return false;
287     }
288 
289     // Create a GL texture that will eventually wrap our externally created texture surface(s)
290     glGenTextures(1, &mTextureMap);
291     if (mTextureMap <= 0) {
292         LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
293         return false;
294     }
295 
296     // Turn off mip-mapping for the created texture surface
297     // (the inbound camera imagery doesn't have MIPs)
298     glBindTexture(GL_TEXTURE_2D, mTextureMap);
299     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
300     glBindTexture(GL_TEXTURE_2D, 0);
301 
302     return true;
303 }
304 
shutdown()305 void GlWrapper::shutdown() {
306     // Drop our device textures
307     if (mKHRimage != EGL_NO_IMAGE_KHR) {
308         eglDestroyImageKHR(mDisplay, mKHRimage);
309         mKHRimage = EGL_NO_IMAGE_KHR;
310     }
311 
312     // Release all GL resources
313     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
314     eglDestroySurface(mDisplay, mSurface);
315     eglDestroyContext(mDisplay, mContext);
316     eglTerminate(mDisplay);
317     mSurface = EGL_NO_SURFACE;
318     mContext = EGL_NO_CONTEXT;
319     mDisplay = EGL_NO_DISPLAY;
320 
321     // Release the window
322     mSurfaceHolder = nullptr;
323 }
324 
showWindow(sp<IAutomotiveDisplayProxyService> & service,uint64_t id)325 void GlWrapper::showWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
326     if (service != nullptr) {
327         service->showWindow(id);
328     } else {
329         LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
330     }
331 }
332 
hideWindow(sp<IAutomotiveDisplayProxyService> & service,uint64_t id)333 void GlWrapper::hideWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
334     if (service != nullptr) {
335         service->hideWindow(id);
336     } else {
337         LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
338     }
339 }
340 
updateImageTexture(const V1_0::BufferDesc & buffer)341 bool GlWrapper::updateImageTexture(const V1_0::BufferDesc& buffer) {
342     BufferDesc newBuffer = {
343             .buffer =
344                     {
345                             .nativeHandle = buffer.memHandle,
346                     },
347             .pixelSize = buffer.pixelSize,
348             .bufferId = buffer.bufferId,
349     };
350     AHardwareBuffer_Desc* pDesc =
351             reinterpret_cast<AHardwareBuffer_Desc*>(&newBuffer.buffer.description);
352     *pDesc = {
353             .width = buffer.width,
354             .height = buffer.height,
355             .layers = 1,
356             .format = buffer.format,
357             .usage = buffer.usage,
358     };
359     return updateImageTexture(newBuffer);
360 }
361 
updateImageTexture(const BufferDesc & aFrame)362 bool GlWrapper::updateImageTexture(const BufferDesc& aFrame) {
363     // If we haven't done it yet, create an "image" object to wrap the gralloc buffer
364     if (mKHRimage == EGL_NO_IMAGE_KHR) {
365         // create a temporary GraphicBuffer to wrap the provided handle
366         const AHardwareBuffer_Desc* pDesc =
367                 reinterpret_cast<const AHardwareBuffer_Desc*>(&aFrame.buffer.description);
368         sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
369                 pDesc->width, pDesc->height, pDesc->format, pDesc->layers, pDesc->usage,
370                 pDesc->stride,
371                 const_cast<native_handle_t*>(aFrame.buffer.nativeHandle.getNativeHandle()),
372                 false /* keep ownership */
373         );
374         if (pGfxBuffer.get() == nullptr) {
375             LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
376             return false;
377         }
378 
379         // Get a GL compatible reference to the graphics buffer we've been given
380         EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
381         EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
382         mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf,
383                                       eglImageAttributes);
384         if (mKHRimage == EGL_NO_IMAGE_KHR) {
385             LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
386             return false;
387         }
388 
389         // Update the texture handle we already created to refer to this gralloc buffer
390         glActiveTexture(GL_TEXTURE0);
391         glBindTexture(GL_TEXTURE_2D, mTextureMap);
392         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
393     }
394 
395     return true;
396 }
397 
renderImageToScreen()398 void GlWrapper::renderImageToScreen() {
399     // Set the viewport
400     glViewport(0, 0, mWidth, mHeight);
401 
402     // Clear the color buffer
403     glClearColor(kDefaultColorInRgba[0], kDefaultColorInRgba[1],
404                  kDefaultColorInRgba[2], kDefaultColorInRgba[3]);
405     glClear(GL_COLOR_BUFFER_BIT);
406 
407     // Select our screen space simple texture shader
408     glUseProgram(mShaderProgram);
409 
410     // Bind the texture and assign it to the shader's sampler
411     glActiveTexture(GL_TEXTURE0);
412     glBindTexture(GL_TEXTURE_2D, mTextureMap);
413     GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
414     glUniform1i(sampler, 0);
415 
416     // We want our image to show up opaque regardless of alpha values
417     glDisable(GL_BLEND);
418 
419     // Draw a rectangle on the screen
420     GLfloat vertsCarPos[] = {
421             -kDisplayAreaRatio,  kDisplayAreaRatio, 0.0f,  // left top in window space
422              kDisplayAreaRatio,  kDisplayAreaRatio, 0.0f,  // right top
423             -kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f,  // left bottom
424              kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f   // right bottom
425     };
426 
427     // NOTE:  We didn't flip the image in the texture, so V=0 is actually the top of the image
428     GLfloat vertsCarTex[] = {
429             0.0f, 0.0f,  // left top
430             1.0f, 0.0f,  // right top
431             0.0f, 1.0f,  // left bottom
432             1.0f, 1.0f   // right bottom
433     };
434     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
435     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
436     glEnableVertexAttribArray(0);
437     glEnableVertexAttribArray(1);
438 
439     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
440 
441     // Clean up and flip the rendered result to the front so it is visible
442     glDisableVertexAttribArray(0);
443     glDisableVertexAttribArray(1);
444 
445     glFinish();
446 
447     eglSwapBuffers(mDisplay, mSurface);
448 }
449 
450 }  // namespace android::hardware::automotive::evs::V1_1::implementation
451