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