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