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