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