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