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