| /* | 
 |  * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. | 
 |  * Not a Contribution. | 
 |  * | 
 |  * Copyright 2015 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "glengine.h" | 
 | #include <utils/Log.h> | 
 | #include "engine.h" | 
 |  | 
 | void checkGlError(const char *, int); | 
 | void checkEglError(const char *, int); | 
 |  | 
 | class EngineContext { | 
 |     public: | 
 |     EGLDisplay eglDisplay; | 
 |     EGLContext eglContext; | 
 |     EGLSurface eglSurface; | 
 |     EngineContext() | 
 |     { | 
 |         eglDisplay = EGL_NO_DISPLAY; | 
 |         eglContext = EGL_NO_CONTEXT; | 
 |         eglSurface = EGL_NO_SURFACE; | 
 |     } | 
 | }; | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | // Make Current | 
 | void engine_bind(void* context) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   EngineContext* engineContext = (EngineContext*)(context); | 
 |   EGL(eglMakeCurrent(engineContext->eglDisplay, engineContext->eglSurface, engineContext->eglSurface, engineContext->eglContext)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | // store the current context(caller) | 
 | void* engine_backup() | 
 | { | 
 |   EngineContext* callerContext = new EngineContext(); | 
 |   // store the previous display/context | 
 |   callerContext->eglDisplay = eglGetCurrentDisplay(); | 
 |   callerContext->eglContext = eglGetCurrentContext(); | 
 |   callerContext->eglSurface = eglGetCurrentSurface(EGL_DRAW); | 
 |  | 
 |   return (void*)callerContext; | 
 | } | 
 | //----------------------------------------------------------------------------- | 
 | // frees the backed up caller context | 
 | void engine_free_backup(void* context) | 
 | { | 
 |   EngineContext* callerContext = (EngineContext*)(context); | 
 |  | 
 |   delete callerContext; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | // initialize GL | 
 | // | 
 | void* engine_initialize(bool isSecure) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   EngineContext* engineContext = new EngineContext(); | 
 |  | 
 |   // display | 
 |   engineContext->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | 
 |   EGL(eglBindAPI(EGL_OPENGL_ES_API)); | 
 |  | 
 |   // initialize | 
 |   EGL(eglInitialize(engineContext->eglDisplay, 0, 0)); | 
 |  | 
 |   // config | 
 |   EGLConfig eglConfig; | 
 |   EGLint eglConfigAttribList[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, | 
 |                                   EGL_RED_SIZE,     8, | 
 |                                   EGL_GREEN_SIZE,   8, | 
 |                                   EGL_BLUE_SIZE,    8, | 
 |                                   EGL_ALPHA_SIZE,   8, | 
 |                                   EGL_NONE}; | 
 |   int numConfig = 0; | 
 |   EGL(eglChooseConfig(engineContext->eglDisplay, eglConfigAttribList, &eglConfig, 1, &numConfig)); | 
 |  | 
 |   // context | 
 |   EGLint eglContextAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 3, | 
 |                                    isSecure ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, | 
 |                                    isSecure ? EGL_TRUE : EGL_NONE, | 
 |                                    EGL_NONE}; | 
 |   engineContext->eglContext = eglCreateContext(engineContext->eglDisplay, eglConfig, NULL, eglContextAttribList); | 
 |  | 
 |   // surface | 
 |   EGLint eglSurfaceAttribList[] = {EGL_WIDTH, 1, | 
 |                                    EGL_HEIGHT, 1, | 
 |                                    isSecure ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, | 
 |                                    isSecure ? EGL_TRUE : EGL_NONE, | 
 |                                    EGL_NONE}; | 
 |   engineContext->eglSurface = eglCreatePbufferSurface(engineContext->eglDisplay, eglConfig, eglSurfaceAttribList); | 
 |  | 
 |   eglMakeCurrent(engineContext->eglDisplay, engineContext->eglSurface, engineContext->eglSurface, engineContext->eglContext); | 
 |  | 
 |   ALOGI("In %s context = %p", __FUNCTION__, (void *)(engineContext->eglContext)); | 
 |  | 
 |   return (void*)(engineContext); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | // Shutdown. | 
 | void engine_shutdown(void* context) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   EngineContext* engineContext = (EngineContext*)context; | 
 |   EGL(eglMakeCurrent(engineContext->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); | 
 |   EGL(eglDestroySurface(engineContext->eglDisplay, engineContext->eglSurface)); | 
 |   EGL(eglDestroyContext(engineContext->eglDisplay, engineContext->eglContext)); | 
 |   EGL(eglTerminate(engineContext->eglDisplay)); | 
 |   engineContext->eglDisplay = EGL_NO_DISPLAY; | 
 |   engineContext->eglContext = EGL_NO_CONTEXT; | 
 |   engineContext->eglSurface = EGL_NO_SURFACE; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_deleteInputBuffer(unsigned int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   if (id != 0) { | 
 |     GL(glDeleteTextures(1, &id)); | 
 |   } | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_deleteProgram(unsigned int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   if (id != 0) { | 
 |     GL(glDeleteProgram(id)); | 
 |   } | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_setData2f(int location, float* data) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |     GL(glUniform2f(location, data[0], data[1])); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | unsigned int engine_load3DTexture(void *colorMapData, int sz, int format) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GLuint texture = 0; | 
 |   GL(glGenTextures(1, &texture)); | 
 |   GL(glBindTexture(GL_TEXTURE_3D, texture)); | 
 |   GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); | 
 |   GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); | 
 |   GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)); | 
 |   GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); | 
 |   GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); | 
 |  | 
 |   GL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB10_A2, sz, sz, sz, 0, GL_RGBA, | 
 |                   GL_UNSIGNED_INT_2_10_10_10_REV, colorMapData)); | 
 |  | 
 |   return texture; | 
 | } | 
 | //----------------------------------------------------------------------------- | 
 | unsigned int engine_load1DTexture(void *data, int sz, int format) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GLuint texture = 0; | 
 |   if ((data != 0) && (sz != 0)) { | 
 |     GL(glGenTextures(1, &texture)); | 
 |     GL(glBindTexture(GL_TEXTURE_2D, texture)); | 
 |     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); | 
 |     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); | 
 |     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); | 
 |     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); | 
 |  | 
 |     GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, sz, 1, 0, GL_RGBA, | 
 |                     GL_UNSIGNED_INT_2_10_10_10_REV, data)); | 
 |   } | 
 |   return texture; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void dumpShaderLog(int shader) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   int success = 0; | 
 |   GLchar infoLog[512]; | 
 |   GL(glGetShaderiv(shader, GL_COMPILE_STATUS, &success)); | 
 |   if (!success) { | 
 |     glGetShaderInfoLog(shader, 512, NULL, infoLog); | 
 |     ALOGI("Shader Failed to compile: %s\n", infoLog); | 
 |   } | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | GLuint engine_loadProgram(int vertexEntries, const char **vertex, int fragmentEntries, | 
 |                           const char **fragment) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GLuint progId = glCreateProgram(); | 
 |  | 
 |   int vertId = glCreateShader(GL_VERTEX_SHADER); | 
 |   int fragId = glCreateShader(GL_FRAGMENT_SHADER); | 
 |  | 
 |   GL(glShaderSource(vertId, vertexEntries, vertex, 0)); | 
 |   GL(glCompileShader(vertId)); | 
 |   dumpShaderLog(vertId); | 
 |  | 
 |   GL(glShaderSource(fragId, fragmentEntries, fragment, 0)); | 
 |   GL(glCompileShader(fragId)); | 
 |   dumpShaderLog(fragId); | 
 |  | 
 |   GL(glAttachShader(progId, vertId)); | 
 |   GL(glAttachShader(progId, fragId)); | 
 |  | 
 |   GL(glLinkProgram(progId)); | 
 |  | 
 |   GL(glDetachShader(progId, vertId)); | 
 |   GL(glDetachShader(progId, fragId)); | 
 |  | 
 |   GL(glDeleteShader(vertId)); | 
 |   GL(glDeleteShader(fragId)); | 
 |  | 
 |   return progId; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void WaitOnNativeFence(int fd) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   if (fd != -1) { | 
 |     EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd, EGL_NONE}; | 
 |  | 
 |     EGLSyncKHR sync = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); | 
 |  | 
 |     if (sync == EGL_NO_SYNC_KHR) { | 
 |       ALOGE("%s - Failed to Create sync from source fd", __FUNCTION__); | 
 |     } else { | 
 |       // the gpu will wait for this sync - not this cpu thread. | 
 |       EGL(eglWaitSyncKHR(eglGetCurrentDisplay(), sync, 0)); | 
 |       EGL(eglDestroySyncKHR(eglGetCurrentDisplay(), sync)); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | int CreateNativeFence() | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   int fd = -1; | 
 |  | 
 |   EGLSyncKHR sync = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); | 
 |   GL(glFlush()); | 
 |   if (sync == EGL_NO_SYNC_KHR) { | 
 |     ALOGE("%s - Failed to Create Native Fence sync", __FUNCTION__); | 
 |   } else { | 
 |     fd = eglDupNativeFenceFDANDROID(eglGetCurrentDisplay(), sync); | 
 |     if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { | 
 |       ALOGE("%s - Failed to dup sync", __FUNCTION__); | 
 |     } | 
 |     EGL(eglDestroySyncKHR(eglGetCurrentDisplay(), sync)); | 
 |   } | 
 |  | 
 |   return fd; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_setDestination(int id, int x, int y, int w, int h) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GL(glBindFramebuffer(GL_FRAMEBUFFER, id)); | 
 |   GL(glViewport(x, y, w, h)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_setProgram(int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GL(glUseProgram(id)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_set2DInputBuffer(int binding, unsigned int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GL(glActiveTexture(GL_TEXTURE0 + binding)); | 
 |   GL(glBindTexture(GL_TEXTURE_2D, id)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_set3DInputBuffer(int binding, unsigned int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GL(glActiveTexture(GL_TEXTURE0 + binding)); | 
 |   GL(glBindTexture(GL_TEXTURE_3D, id)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void engine_setExternalInputBuffer(int binding, unsigned int id) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   GL(glActiveTexture(GL_TEXTURE0 + binding)); | 
 |   GL(glBindTexture(0x8D65, id)); | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | int engine_blit(int srcFenceFd) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   int fd = -1; | 
 |   WaitOnNativeFence(srcFenceFd); | 
 |   float fullscreen_vertices[]{0.0f, 2.0f, 0.0f, 0.0f, 2.0f, 0.0f}; | 
 |   GL(glEnableVertexAttribArray(0)); | 
 |   GL(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, fullscreen_vertices)); | 
 |   GL(glDrawArrays(GL_TRIANGLES, 0, 3)); | 
 |   fd = CreateNativeFence(); | 
 |   GL(glFlush()); | 
 |   return fd; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void checkGlError(const char *file, int line) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   for (GLint error = glGetError(); error; error = glGetError()) { | 
 |     char *pError; | 
 |     switch (error) { | 
 |       case GL_NO_ERROR: | 
 |         pError = (char *)"GL_NO_ERROR"; | 
 |         break; | 
 |       case GL_INVALID_ENUM: | 
 |         pError = (char *)"GL_INVALID_ENUM"; | 
 |         break; | 
 |       case GL_INVALID_VALUE: | 
 |         pError = (char *)"GL_INVALID_VALUE"; | 
 |         break; | 
 |       case GL_INVALID_OPERATION: | 
 |         pError = (char *)"GL_INVALID_OPERATION"; | 
 |         break; | 
 |       case GL_OUT_OF_MEMORY: | 
 |         pError = (char *)"GL_OUT_OF_MEMORY"; | 
 |         break; | 
 |       case GL_INVALID_FRAMEBUFFER_OPERATION: | 
 |         pError = (char *)"GL_INVALID_FRAMEBUFFER_OPERATION"; | 
 |         break; | 
 |  | 
 |       default: | 
 |         ALOGE("glError (0x%x) %s:%d\n", error, file, line); | 
 |         return; | 
 |     } | 
 |  | 
 |     ALOGE("glError (%s) %s:%d\n", pError, file, line); | 
 |     return; | 
 |   } | 
 |   return; | 
 | } | 
 |  | 
 | //----------------------------------------------------------------------------- | 
 | void checkEglError(const char *file, int line) | 
 | //----------------------------------------------------------------------------- | 
 | { | 
 |   for (int i = 0; i < 5; i++) { | 
 |     const EGLint error = eglGetError(); | 
 |     if (error == EGL_SUCCESS) { | 
 |       break; | 
 |     } | 
 |  | 
 |     char *pError; | 
 |     switch (error) { | 
 |       case EGL_SUCCESS: | 
 |         pError = (char *)"EGL_SUCCESS"; | 
 |         break; | 
 |       case EGL_NOT_INITIALIZED: | 
 |         pError = (char *)"EGL_NOT_INITIALIZED"; | 
 |         break; | 
 |       case EGL_BAD_ACCESS: | 
 |         pError = (char *)"EGL_BAD_ACCESS"; | 
 |         break; | 
 |       case EGL_BAD_ALLOC: | 
 |         pError = (char *)"EGL_BAD_ALLOC"; | 
 |         break; | 
 |       case EGL_BAD_ATTRIBUTE: | 
 |         pError = (char *)"EGL_BAD_ATTRIBUTE"; | 
 |         break; | 
 |       case EGL_BAD_CONTEXT: | 
 |         pError = (char *)"EGL_BAD_CONTEXT"; | 
 |         break; | 
 |       case EGL_BAD_CONFIG: | 
 |         pError = (char *)"EGL_BAD_CONFIG"; | 
 |         break; | 
 |       case EGL_BAD_CURRENT_SURFACE: | 
 |         pError = (char *)"EGL_BAD_CURRENT_SURFACE"; | 
 |         break; | 
 |       case EGL_BAD_DISPLAY: | 
 |         pError = (char *)"EGL_BAD_DISPLAY"; | 
 |         break; | 
 |       case EGL_BAD_SURFACE: | 
 |         pError = (char *)"EGL_BAD_SURFACE"; | 
 |         break; | 
 |       case EGL_BAD_MATCH: | 
 |         pError = (char *)"EGL_BAD_MATCH"; | 
 |         break; | 
 |       case EGL_BAD_PARAMETER: | 
 |         pError = (char *)"EGL_BAD_PARAMETER"; | 
 |         break; | 
 |       case EGL_BAD_NATIVE_PIXMAP: | 
 |         pError = (char *)"EGL_BAD_NATIVE_PIXMAP"; | 
 |         break; | 
 |       case EGL_BAD_NATIVE_WINDOW: | 
 |         pError = (char *)"EGL_BAD_NATIVE_WINDOW"; | 
 |         break; | 
 |       case EGL_CONTEXT_LOST: | 
 |         pError = (char *)"EGL_CONTEXT_LOST"; | 
 |         break; | 
 |       default: | 
 |         ALOGE("eglError (0x%x) %s:%d\n", error, file, line); | 
 |         return; | 
 |     } | 
 |     ALOGE("eglError (%s) %s:%d\n", pError, file, line); | 
 |     return; | 
 |   } | 
 |   return; | 
 | } |