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 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 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 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 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 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 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 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 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 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 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