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