1 /*
2 * Copyright (C) 2017 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 <stdio.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22
23 #include <utility>
24
25 #include <ui/DisplayConfig.h>
26 #include <ui/DisplayState.h>
27 #include <ui/GraphicBuffer.h>
28
29
30 using namespace android;
31
32
33 using android::GraphicBuffer;
34 using android::sp;
35
36
37 const char vertexShaderSource[] = ""
38 "#version 300 es \n"
39 "layout(location = 0) in vec4 pos; \n"
40 "layout(location = 1) in vec2 tex; \n"
41 "out vec2 uv; \n"
42 "void main() \n"
43 "{ \n"
44 " gl_Position = pos; \n"
45 " uv = tex; \n"
46 "} \n";
47
48 const char pixelShaderSource[] =
49 "#version 300 es \n"
50 "precision mediump float; \n"
51 "uniform sampler2D tex; \n"
52 "in vec2 uv; \n"
53 "out vec4 color; \n"
54 "void main() \n"
55 "{ \n"
56 " vec4 texel = texture(tex, uv); \n"
57 " color = texel; \n"
58 "} \n";
59
60
getEGLError(void)61 static 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
99 // Given shader source, load and compile it
loadShader(GLenum type,const char * shaderSrc)100 static 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 {
121 // Get and report the error message
122 char *infoLog = (char*)malloc(size);
123 glGetShaderInfoLog(shader, size, nullptr, infoLog);
124 LOG(ERROR) << " msg:" << std::endl << infoLog;
125 free(infoLog);
126 }
127
128 glDeleteShader(shader);
129 return 0;
130 }
131
132 return shader;
133 }
134
135
136 // Create a program object given vertex and pixels shader source
buildShaderProgram(const char * vtxSrc,const char * pxlSrc)137 static GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
138 GLuint program = glCreateProgram();
139 if (program == 0) {
140 LOG(ERROR) << "Failed to allocate program object";
141 return 0;
142 }
143
144 // Compile the shaders and bind them to this program
145 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
146 if (vertexShader == 0) {
147 LOG(ERROR) << "Failed to load vertex shader";
148 glDeleteProgram(program);
149 return 0;
150 }
151 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
152 if (pixelShader == 0) {
153 LOG(ERROR) << "Failed to load pixel shader";
154 glDeleteProgram(program);
155 glDeleteShader(vertexShader);
156 return 0;
157 }
158 glAttachShader(program, vertexShader);
159 glAttachShader(program, pixelShader);
160
161 // Link the program
162 glLinkProgram(program);
163 GLint linked = 0;
164 glGetProgramiv(program, GL_LINK_STATUS, &linked);
165 if (!linked)
166 {
167 LOG(ERROR) << "Error linking program";
168 GLint size = 0;
169 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
170 if (size > 0)
171 {
172 // Get and report the error message
173 char *infoLog = (char*)malloc(size);
174 glGetProgramInfoLog(program, size, nullptr, infoLog);
175 LOG(ERROR) << " msg: " << infoLog;
176 free(infoLog);
177 }
178
179 glDeleteProgram(program);
180 glDeleteShader(vertexShader);
181 glDeleteShader(pixelShader);
182 return 0;
183 }
184
185 return program;
186 }
187
188
189 // Main entry point
initialize(sp<IAutomotiveDisplayProxyService> pWindowProxy,uint64_t displayId)190 bool GlWrapper::initialize(sp<IAutomotiveDisplayProxyService> pWindowProxy,
191 uint64_t displayId) {
192 LOG(DEBUG) << __FUNCTION__;
193
194 if (pWindowProxy == nullptr) {
195 LOG(ERROR) << "Could not get IAutomotiveDisplayProxyService.";
196 return false;
197 }
198
199 // We will use the first display in the list as the primary.
200 pWindowProxy->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
201 DisplayConfig *pConfig = (DisplayConfig*)dpyConfig.data();
202 mWidth = pConfig->resolution.getWidth();
203 mHeight = pConfig->resolution.getHeight();
204
205 ui::DisplayState* pState = (ui::DisplayState*)dpyState.data();
206 if (pState->orientation != ui::ROTATION_0 &&
207 pState->orientation != ui::ROTATION_180) {
208 // rotate
209 std::swap(mWidth, mHeight);
210 }
211
212 LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
213 });
214
215 mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
216 if (mGfxBufferProducer == nullptr) {
217 LOG(ERROR) << "Failed to get IGraphicBufferProducer from IAutomotiveDisplayProxyService.";
218 return false;
219 }
220
221 mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
222 if (mSurfaceHolder == nullptr) {
223 LOG(ERROR) << "Failed to get a Surface from HGBP.";
224 return false;
225 }
226
227 mWindow = getNativeWindow(mSurfaceHolder.get());
228 if (mWindow == nullptr) {
229 LOG(ERROR) << "Failed to get a native window from Surface.";
230 return false;
231 }
232
233
234 // Set up our OpenGL ES context associated with the default display
235 mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
236 if (mDisplay == EGL_NO_DISPLAY) {
237 LOG(ERROR) << "Failed to get egl display";
238 return false;
239 }
240
241 EGLint major = 3;
242 EGLint minor = 0;
243 if (!eglInitialize(mDisplay, &major, &minor)) {
244 LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
245 return false;
246 }
247
248
249 const EGLint config_attribs[] = {
250 // Tag Value
251 EGL_RED_SIZE, 8,
252 EGL_GREEN_SIZE, 8,
253 EGL_BLUE_SIZE, 8,
254 EGL_DEPTH_SIZE, 0,
255 EGL_NONE
256 };
257
258 // Pick the default configuration without constraints (is this good enough?)
259 EGLConfig egl_config = {0};
260 EGLint numConfigs = -1;
261 eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
262 if (numConfigs != 1) {
263 LOG(ERROR) << "Didn't find a suitable format for our display window";
264 return false;
265 }
266
267 // Create the EGL render target surface
268 mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
269 if (mSurface == EGL_NO_SURFACE) {
270 LOG(ERROR) << "eglCreateWindowSurface failed.";
271 return false;
272 }
273
274 // Create the EGL context
275 // NOTE: Our shader is (currently at least) written to require version 3, so this
276 // is required.
277 const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
278 mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
279 if (mContext == EGL_NO_CONTEXT) {
280 LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
281 return false;
282 }
283
284
285 // Activate our render target for drawing
286 if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
287 LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
288 return false;
289 }
290
291
292 // Create the shader program for our simple pipeline
293 mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
294 if (!mShaderProgram) {
295 LOG(ERROR) << "Failed to build shader program: " << getEGLError();
296 return false;
297 }
298
299 // Create a GL texture that will eventually wrap our externally created texture surface(s)
300 glGenTextures(1, &mTextureMap);
301 if (mTextureMap <= 0) {
302 LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
303 return false;
304 }
305
306 // Turn off mip-mapping for the created texture surface
307 // (the inbound camera imagery doesn't have MIPs)
308 glBindTexture(GL_TEXTURE_2D, mTextureMap);
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
310 glBindTexture(GL_TEXTURE_2D, 0);
311
312 return true;
313 }
314
315
shutdown()316 void GlWrapper::shutdown() {
317
318 // Drop our device textures
319 if (mKHRimage != EGL_NO_IMAGE_KHR) {
320 eglDestroyImageKHR(mDisplay, mKHRimage);
321 mKHRimage = EGL_NO_IMAGE_KHR;
322 }
323
324 // Release all GL resources
325 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
326 eglDestroySurface(mDisplay, mSurface);
327 eglDestroyContext(mDisplay, mContext);
328 eglTerminate(mDisplay);
329 mSurface = EGL_NO_SURFACE;
330 mContext = EGL_NO_CONTEXT;
331 mDisplay = EGL_NO_DISPLAY;
332
333 // Release the window
334 mSurfaceHolder = nullptr;
335 }
336
337
showWindow(sp<IAutomotiveDisplayProxyService> & pWindowProxy,uint64_t id)338 void GlWrapper::showWindow(sp<IAutomotiveDisplayProxyService>& pWindowProxy, uint64_t id) {
339 if (pWindowProxy != nullptr) {
340 pWindowProxy->showWindow(id);
341 } else {
342 LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
343 }
344 }
345
346
hideWindow(sp<IAutomotiveDisplayProxyService> & pWindowProxy,uint64_t id)347 void GlWrapper::hideWindow(sp<IAutomotiveDisplayProxyService>& pWindowProxy, uint64_t id) {
348 if (pWindowProxy != nullptr) {
349 pWindowProxy->hideWindow(id);
350 } else {
351 LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
352 }
353 }
354
355
updateImageTexture(const BufferDesc_1_0 & buffer)356 bool GlWrapper::updateImageTexture(const BufferDesc_1_0& buffer) {
357 BufferDesc_1_1 newBuffer = {};
358 AHardwareBuffer_Desc* pDesc =
359 reinterpret_cast<AHardwareBuffer_Desc *>(&newBuffer.buffer.description);
360 pDesc->width = buffer.width;
361 pDesc->height = buffer.height;
362 pDesc->layers = 1;
363 pDesc->format = buffer.format;
364 pDesc->usage = buffer.usage;
365 pDesc->stride = buffer.stride;
366 newBuffer.buffer.nativeHandle = buffer.memHandle;
367 newBuffer.pixelSize = buffer.pixelSize;
368 newBuffer.bufferId = buffer.bufferId;
369
370 return updateImageTexture(newBuffer);
371 }
372
373
updateImageTexture(const BufferDesc_1_1 & aFrame)374 bool GlWrapper::updateImageTexture(const BufferDesc_1_1& aFrame) {
375
376 // If we haven't done it yet, create an "image" object to wrap the gralloc buffer
377 if (mKHRimage == EGL_NO_IMAGE_KHR) {
378 // create a temporary GraphicBuffer to wrap the provided handle
379 const AHardwareBuffer_Desc* pDesc =
380 reinterpret_cast<const AHardwareBuffer_Desc *>(&aFrame.buffer.description);
381 sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
382 pDesc->width,
383 pDesc->height,
384 pDesc->format,
385 pDesc->layers,
386 pDesc->usage,
387 pDesc->stride,
388 const_cast<native_handle_t*>(aFrame.buffer.nativeHandle.getNativeHandle()),
389 false /* keep ownership */
390 );
391 if (pGfxBuffer.get() == nullptr) {
392 LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
393 return false;
394 }
395
396 // Get a GL compatible reference to the graphics buffer we've been given
397 EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
398 EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
399 mKHRimage = eglCreateImageKHR(mDisplay,
400 EGL_NO_CONTEXT,
401 EGL_NATIVE_BUFFER_ANDROID,
402 cbuf,
403 eglImageAttributes);
404 if (mKHRimage == EGL_NO_IMAGE_KHR) {
405 LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
406 return false;
407 }
408
409
410 // Update the texture handle we already created to refer to this gralloc buffer
411 glActiveTexture(GL_TEXTURE0);
412 glBindTexture(GL_TEXTURE_2D, mTextureMap);
413 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
414
415 }
416
417 return true;
418 }
419
420
renderImageToScreen()421 void GlWrapper::renderImageToScreen() {
422 // Set the viewport
423 glViewport(0, 0, mWidth, mHeight);
424
425 // Clear the color buffer
426 glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
427 glClear(GL_COLOR_BUFFER_BIT);
428
429 // Select our screen space simple texture shader
430 glUseProgram(mShaderProgram);
431
432 // Bind the texture and assign it to the shader's sampler
433 glActiveTexture(GL_TEXTURE0);
434 glBindTexture(GL_TEXTURE_2D, mTextureMap);
435 GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
436 glUniform1i(sampler, 0);
437
438 // We want our image to show up opaque regardless of alpha values
439 glDisable(GL_BLEND);
440
441
442 // Draw a rectangle on the screen
443 GLfloat vertsCarPos[] = { -0.8, 0.8, 0.0f, // left top in window space
444 0.8, 0.8, 0.0f, // right top
445 -0.8, -0.8, 0.0f, // left bottom
446 0.8, -0.8, 0.0f // right bottom
447 };
448
449 // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
450 GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top
451 1.0f, 0.0f, // right top
452 0.0f, 1.0f, // left bottom
453 1.0f, 1.0f // right bottom
454 };
455 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
456 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
457 glEnableVertexAttribArray(0);
458 glEnableVertexAttribArray(1);
459
460 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
461
462
463 // Clean up and flip the rendered result to the front so it is visible
464 glDisableVertexAttribArray(0);
465 glDisableVertexAttribArray(1);
466
467 glFinish();
468
469 eglSwapBuffers(mDisplay, mSurface);
470 }
471
472