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