/*
* 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;
}