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 <ui/DisplayInfo.h> 24#include <ui/GraphicBuffer.h> 25#include <ui/GraphicBufferAllocator.h> 26#include <ui/GraphicBufferMapper.h> 27 28 29using namespace android; 30 31 32// TODO: Consider dropping direct use of GraphicsBufferAllocator and Mapper? 33using android::GraphicBuffer; 34using android::GraphicBufferAllocator; 35using android::GraphicBufferMapper; 36using android::sp; 37 38 39const char vertexShaderSource[] = "" 40 "#version 300 es \n" 41 "layout(location = 0) in vec4 pos; \n" 42 "layout(location = 1) in vec2 tex; \n" 43 "out vec2 uv; \n" 44 "void main() \n" 45 "{ \n" 46 " gl_Position = pos; \n" 47 " uv = tex; \n" 48 "} \n"; 49 50const char pixelShaderSource[] = 51 "#version 300 es \n" 52 "precision mediump float; \n" 53 "uniform sampler2D tex; \n" 54 "in vec2 uv; \n" 55 "out vec4 color; \n" 56 "void main() \n" 57 "{ \n" 58 " vec4 texel = texture(tex, uv); \n" 59 " color = texel; \n" 60 "} \n"; 61 62 63static const 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 101// Given shader source, load and compile it 102static GLuint loadShader(GLenum type, const char *shaderSrc) { 103 // Create the shader object 104 GLuint shader = glCreateShader (type); 105 if (shader == 0) { 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 ALOGE("Error compiling shader\n"); 118 119 GLint size = 0; 120 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size); 121 if (size > 0) 122 { 123 // Get and report the error message 124 char *infoLog = (char*)malloc(size); 125 glGetShaderInfoLog(shader, size, nullptr, infoLog); 126 ALOGE(" msg:\n%s\n", infoLog); 127 free(infoLog); 128 } 129 130 glDeleteShader(shader); 131 return 0; 132 } 133 134 return shader; 135} 136 137 138// Create a program object given vertex and pixels shader source 139static GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) { 140 GLuint program = glCreateProgram(); 141 if (program == 0) { 142 ALOGE("Failed to allocate program object\n"); 143 return 0; 144 } 145 146 // Compile the shaders and bind them to this program 147 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc); 148 if (vertexShader == 0) { 149 ALOGE("Failed to load vertex shader\n"); 150 glDeleteProgram(program); 151 return 0; 152 } 153 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc); 154 if (pixelShader == 0) { 155 ALOGE("Failed to load pixel shader\n"); 156 glDeleteProgram(program); 157 glDeleteShader(vertexShader); 158 return 0; 159 } 160 glAttachShader(program, vertexShader); 161 glAttachShader(program, pixelShader); 162 163 // Link the program 164 glLinkProgram(program); 165 GLint linked = 0; 166 glGetProgramiv(program, GL_LINK_STATUS, &linked); 167 if (!linked) 168 { 169 ALOGE("Error linking program.\n"); 170 GLint size = 0; 171 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size); 172 if (size > 0) 173 { 174 // Get and report the error message 175 char *infoLog = (char*)malloc(size); 176 glGetProgramInfoLog(program, size, nullptr, infoLog); 177 ALOGE(" msg: %s\n", infoLog); 178 free(infoLog); 179 } 180 181 glDeleteProgram(program); 182 glDeleteShader(vertexShader); 183 glDeleteShader(pixelShader); 184 return 0; 185 } 186 187 return program; 188} 189 190 191// Main entry point 192bool GlWrapper::initialize() { 193 // 194 // Create the native full screen window and get a suitable configuration to match it 195 // 196 status_t err; 197 198 mFlinger = new SurfaceComposerClient(); 199 if (mFlinger == nullptr) { 200 ALOGE("SurfaceComposerClient couldn't be allocated"); 201 return false; 202 } 203 err = mFlinger->initCheck(); 204 if (err != NO_ERROR) { 205 ALOGE("SurfaceComposerClient::initCheck error: %#x", err); 206 return false; 207 } 208 209 // Get main display parameters. 210 sp <IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay( 211 ISurfaceComposer::eDisplayIdMain); 212 DisplayInfo mainDpyInfo; 213 err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); 214 if (err != NO_ERROR) { 215 ALOGE("ERROR: unable to get display characteristics"); 216 return false; 217 } 218 219 if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 && 220 mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) { 221 // rotated 222 mWidth = mainDpyInfo.h; 223 mHeight = mainDpyInfo.w; 224 } else { 225 mWidth = mainDpyInfo.w; 226 mHeight = mainDpyInfo.h; 227 } 228 229 mFlingerSurfaceControl = mFlinger->createSurface( 230 String8("Evs Display"), mWidth, mHeight, 231 PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque); 232 if (mFlingerSurfaceControl == nullptr || !mFlingerSurfaceControl->isValid()) { 233 ALOGE("Failed to create SurfaceControl"); 234 return false; 235 } 236 mFlingerSurface = mFlingerSurfaceControl->getSurface(); 237 238 239 // Set up our OpenGL ES context associated with the default display 240 mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 241 if (mDisplay == EGL_NO_DISPLAY) { 242 ALOGE("Failed to get egl display"); 243 return false; 244 } 245 246 EGLint major = 3; 247 EGLint minor = 0; 248 if (!eglInitialize(mDisplay, &major, &minor)) { 249 ALOGE("Failed to initialize EGL: %s", getEGLError()); 250 return false; 251 } 252 253 254 const EGLint config_attribs[] = { 255 // Tag Value 256 EGL_RED_SIZE, 8, 257 EGL_GREEN_SIZE, 8, 258 EGL_BLUE_SIZE, 8, 259 EGL_DEPTH_SIZE, 0, 260 EGL_NONE 261 }; 262 263 // Pick the default configuration without constraints (is this good enough?) 264 EGLConfig egl_config = {0}; 265 EGLint numConfigs = -1; 266 eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs); 267 if (numConfigs != 1) { 268 ALOGE("Didn't find a suitable format for our display window"); 269 return false; 270 } 271 272 // Create the EGL render target surface 273 mSurface = eglCreateWindowSurface(mDisplay, egl_config, mFlingerSurface.get(), nullptr); 274 if (mSurface == EGL_NO_SURFACE) { 275 ALOGE("gelCreateWindowSurface failed."); 276 return false; 277 } 278 279 // Create the EGL context 280 // NOTE: Our shader is (currently at least) written to require version 3, so this 281 // is required. 282 const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; 283 mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs); 284 if (mContext == EGL_NO_CONTEXT) { 285 ALOGE("Failed to create OpenGL ES Context: %s", getEGLError()); 286 return false; 287 } 288 289 290 // Activate our render target for drawing 291 if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) { 292 ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError()); 293 return false; 294 } 295 296 297 // Create the shader program for our simple pipeline 298 mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource); 299 if (!mShaderProgram) { 300 ALOGE("Failed to build shader program: %s", getEGLError()); 301 return false; 302 } 303 304 // Create a GL texture that will eventually wrap our externally created texture surface(s) 305 glGenTextures(1, &mTextureMap); 306 if (mTextureMap <= 0) { 307 ALOGE("Didn't get a texture handle allocated: %s", getEGLError()); 308 return false; 309 } 310 311 312 return true; 313} 314 315 316void 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 // Let go of our SurfaceComposer resources 334 mFlingerSurface.clear(); 335 mFlingerSurfaceControl.clear(); 336 mFlinger.clear(); 337} 338 339 340void GlWrapper::showWindow() { 341 if (mFlingerSurfaceControl != nullptr) { 342 SurfaceComposerClient::openGlobalTransaction(); 343 mFlingerSurfaceControl->setLayer(0x7FFFFFFF); // always on top 344 mFlingerSurfaceControl->show(); 345 SurfaceComposerClient::closeGlobalTransaction(); 346 } 347} 348 349 350void GlWrapper::hideWindow() { 351 if (mFlingerSurfaceControl != nullptr) { 352 SurfaceComposerClient::openGlobalTransaction(); 353 mFlingerSurfaceControl->hide(); 354 SurfaceComposerClient::closeGlobalTransaction(); 355 } 356} 357 358 359bool GlWrapper::updateImageTexture(const BufferDesc& buffer) { 360 361 // If we haven't done it yet, create an "image" object to wrap the gralloc buffer 362 if (mKHRimage == EGL_NO_IMAGE_KHR) { 363 // create a temporary GraphicBuffer to wrap the provided handle 364 sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer( 365 buffer.width, 366 buffer.height, 367 buffer.format, 368 1, /* layer count */ 369 buffer.usage, 370 buffer.stride, 371 const_cast<native_handle_t*>(buffer.memHandle.getNativeHandle()), 372 false /* keep ownership */ 373 ); 374 if (pGfxBuffer.get() == nullptr) { 375 ALOGE("Failed to allocate GraphicsBuffer to wrap our native handle"); 376 return false; 377 } 378 379 380 // Get a GL compatible reference to the graphics buffer we've been given 381 EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; 382 EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer()); 383// TODO: If we pass in a context, we get "bad context" back 384#if 0 385 mKHRimage = eglCreateImageKHR(mDisplay, mContext, 386 EGL_NATIVE_BUFFER_ANDROID, cbuf, 387 eglImageAttributes); 388#else 389 mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, 390 EGL_NATIVE_BUFFER_ANDROID, cbuf, 391 eglImageAttributes); 392#endif 393 if (mKHRimage == EGL_NO_IMAGE_KHR) { 394 ALOGE("error creating EGLImage: %s", getEGLError()); 395 return false; 396 } 397 398 399 // Update the texture handle we already created to refer to this gralloc buffer 400 glActiveTexture(GL_TEXTURE0); 401 glBindTexture(GL_TEXTURE_2D, mTextureMap); 402 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage)); 403 404 } 405 406 return true; 407} 408 409 410void GlWrapper::renderImageToScreen() { 411 // Set the viewport 412 glViewport(0, 0, mWidth, mHeight); 413 414 // Clear the color buffer 415 glClearColor(0.1f, 0.5f, 0.1f, 1.0f); 416 glClear(GL_COLOR_BUFFER_BIT); 417 418 // Select our screen space simple texture shader 419 glUseProgram(mShaderProgram); 420 421 // Bind the texture and assign it to the shader's sampler 422 glActiveTexture(GL_TEXTURE0); 423 glBindTexture(GL_TEXTURE_2D, mTextureMap); 424 GLint sampler = glGetUniformLocation(mShaderProgram, "tex"); 425 glUniform1i(sampler, 0); 426 427 // We want our image to show up opaque regardless of alpha values 428 glDisable(GL_BLEND); 429 430 431 // Draw a rectangle on the screen 432 // TODO: We pulled in from the edges for now for diagnostic purposes... 433#if 0 434 GLfloat vertsCarPos[] = { -1.0, 1.0, 0.0f, // left top in window space 435 1.0, 1.0, 0.0f, // right top 436 -1.0, -1.0, 0.0f, // left bottom 437 1.0, -1.0, 0.0f // right bottom 438 }; 439#else 440 GLfloat vertsCarPos[] = { -0.8, 0.8, 0.0f, // left top in window space 441 0.8, 0.8, 0.0f, // right top 442 -0.8, -0.8, 0.0f, // left bottom 443 0.8, -0.8, 0.0f // right bottom 444 }; 445#endif 446 // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image 447 GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top 448 1.0f, 0.0f, // right top 449 0.0f, 1.0f, // left bottom 450 1.0f, 1.0f // right bottom 451 }; 452 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos); 453 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex); 454 glEnableVertexAttribArray(0); 455 glEnableVertexAttribArray(1); 456 457 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 458 459 460 // Clean up and flip the rendered result to the front so it is visible 461 glDisableVertexAttribArray(0); 462 glDisableVertexAttribArray(1); 463 464 glFinish(); 465 466 eglSwapBuffers(mDisplay, mSurface); 467} 468 469