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