You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
15 KiB
472 lines
15 KiB
4 months ago
|
/*
|
||
|
* Copyright (C) 2017 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 "GlWrapper.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#include <utility>
|
||
|
|
||
|
#include <ui/DisplayMode.h>
|
||
|
#include <ui/DisplayState.h>
|
||
|
#include <ui/GraphicBuffer.h>
|
||
|
|
||
|
|
||
|
using namespace android;
|
||
|
|
||
|
|
||
|
using android::GraphicBuffer;
|
||
|
using android::sp;
|
||
|
|
||
|
|
||
|
const char vertexShaderSource[] = ""
|
||
|
"#version 300 es \n"
|
||
|
"layout(location = 0) in vec4 pos; \n"
|
||
|
"layout(location = 1) in vec2 tex; \n"
|
||
|
"out vec2 uv; \n"
|
||
|
"void main() \n"
|
||
|
"{ \n"
|
||
|
" gl_Position = pos; \n"
|
||
|
" uv = tex; \n"
|
||
|
"} \n";
|
||
|
|
||
|
const char pixelShaderSource[] =
|
||
|
"#version 300 es \n"
|
||
|
"precision mediump float; \n"
|
||
|
"uniform sampler2D tex; \n"
|
||
|
"in vec2 uv; \n"
|
||
|
"out vec4 color; \n"
|
||
|
"void main() \n"
|
||
|
"{ \n"
|
||
|
" vec4 texel = texture(tex, uv); \n"
|
||
|
" color = texel; \n"
|
||
|
"} \n";
|
||
|
|
||
|
|
||
|
static const char *getEGLError(void) {
|
||
|
switch (eglGetError()) {
|
||
|
case EGL_SUCCESS:
|
||
|
return "EGL_SUCCESS";
|
||
|
case EGL_NOT_INITIALIZED:
|
||
|
return "EGL_NOT_INITIALIZED";
|
||
|
case EGL_BAD_ACCESS:
|
||
|
return "EGL_BAD_ACCESS";
|
||
|
case EGL_BAD_ALLOC:
|
||
|
return "EGL_BAD_ALLOC";
|
||
|
case EGL_BAD_ATTRIBUTE:
|
||
|
return "EGL_BAD_ATTRIBUTE";
|
||
|
case EGL_BAD_CONTEXT:
|
||
|
return "EGL_BAD_CONTEXT";
|
||
|
case EGL_BAD_CONFIG:
|
||
|
return "EGL_BAD_CONFIG";
|
||
|
case EGL_BAD_CURRENT_SURFACE:
|
||
|
return "EGL_BAD_CURRENT_SURFACE";
|
||
|
case EGL_BAD_DISPLAY:
|
||
|
return "EGL_BAD_DISPLAY";
|
||
|
case EGL_BAD_SURFACE:
|
||
|
return "EGL_BAD_SURFACE";
|
||
|
case EGL_BAD_MATCH:
|
||
|
return "EGL_BAD_MATCH";
|
||
|
case EGL_BAD_PARAMETER:
|
||
|
return "EGL_BAD_PARAMETER";
|
||
|
case EGL_BAD_NATIVE_PIXMAP:
|
||
|
return "EGL_BAD_NATIVE_PIXMAP";
|
||
|
case EGL_BAD_NATIVE_WINDOW:
|
||
|
return "EGL_BAD_NATIVE_WINDOW";
|
||
|
case EGL_CONTEXT_LOST:
|
||
|
return "EGL_CONTEXT_LOST";
|
||
|
default:
|
||
|
return "Unknown error";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Given shader source, load and compile it
|
||
|
static GLuint loadShader(GLenum type, const char *shaderSrc) {
|
||
|
// Create the shader object
|
||
|
GLuint shader = glCreateShader (type);
|
||
|
if (shader == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Load and compile the shader
|
||
|
glShaderSource(shader, 1, &shaderSrc, nullptr);
|
||
|
glCompileShader(shader);
|
||
|
|
||
|
// Verify the compilation worked as expected
|
||
|
GLint compiled = 0;
|
||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||
|
if (!compiled) {
|
||
|
LOG(ERROR) << "Error compiling shader";
|
||
|
|
||
|
GLint size = 0;
|
||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
|
||
|
if (size > 0)
|
||
|
{
|
||
|
// Get and report the error message
|
||
|
char *infoLog = (char*)malloc(size);
|
||
|
glGetShaderInfoLog(shader, size, nullptr, infoLog);
|
||
|
LOG(ERROR) << " msg:" << std::endl << infoLog;
|
||
|
free(infoLog);
|
||
|
}
|
||
|
|
||
|
glDeleteShader(shader);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return shader;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Create a program object given vertex and pixels shader source
|
||
|
static GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
|
||
|
GLuint program = glCreateProgram();
|
||
|
if (program == 0) {
|
||
|
LOG(ERROR) << "Failed to allocate program object";
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Compile the shaders and bind them to this program
|
||
|
GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
|
||
|
if (vertexShader == 0) {
|
||
|
LOG(ERROR) << "Failed to load vertex shader";
|
||
|
glDeleteProgram(program);
|
||
|
return 0;
|
||
|
}
|
||
|
GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
|
||
|
if (pixelShader == 0) {
|
||
|
LOG(ERROR) << "Failed to load pixel shader";
|
||
|
glDeleteProgram(program);
|
||
|
glDeleteShader(vertexShader);
|
||
|
return 0;
|
||
|
}
|
||
|
glAttachShader(program, vertexShader);
|
||
|
glAttachShader(program, pixelShader);
|
||
|
|
||
|
// Link the program
|
||
|
glLinkProgram(program);
|
||
|
GLint linked = 0;
|
||
|
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||
|
if (!linked)
|
||
|
{
|
||
|
LOG(ERROR) << "Error linking program";
|
||
|
GLint size = 0;
|
||
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
|
||
|
if (size > 0)
|
||
|
{
|
||
|
// Get and report the error message
|
||
|
char *infoLog = (char*)malloc(size);
|
||
|
glGetProgramInfoLog(program, size, nullptr, infoLog);
|
||
|
LOG(ERROR) << " msg: " << infoLog;
|
||
|
free(infoLog);
|
||
|
}
|
||
|
|
||
|
glDeleteProgram(program);
|
||
|
glDeleteShader(vertexShader);
|
||
|
glDeleteShader(pixelShader);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return program;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Main entry point
|
||
|
bool GlWrapper::initialize(sp<IAutomotiveDisplayProxyService> pWindowProxy,
|
||
|
uint64_t displayId) {
|
||
|
LOG(DEBUG) << __FUNCTION__;
|
||
|
|
||
|
if (pWindowProxy == nullptr) {
|
||
|
LOG(ERROR) << "Could not get IAutomotiveDisplayProxyService.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// We will use the first display in the list as the primary.
|
||
|
pWindowProxy->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
|
||
|
ui::DisplayMode *pConfig = (ui::DisplayMode*)dpyConfig.data();
|
||
|
mWidth = pConfig->resolution.getWidth();
|
||
|
mHeight = pConfig->resolution.getHeight();
|
||
|
|
||
|
ui::DisplayState* pState = (ui::DisplayState*)dpyState.data();
|
||
|
if (pState->orientation != ui::ROTATION_0 &&
|
||
|
pState->orientation != ui::ROTATION_180) {
|
||
|
// rotate
|
||
|
std::swap(mWidth, mHeight);
|
||
|
}
|
||
|
|
||
|
LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
|
||
|
});
|
||
|
|
||
|
mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
|
||
|
if (mGfxBufferProducer == nullptr) {
|
||
|
LOG(ERROR) << "Failed to get IGraphicBufferProducer from IAutomotiveDisplayProxyService.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
|
||
|
if (mSurfaceHolder == nullptr) {
|
||
|
LOG(ERROR) << "Failed to get a Surface from HGBP.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mWindow = getNativeWindow(mSurfaceHolder.get());
|
||
|
if (mWindow == nullptr) {
|
||
|
LOG(ERROR) << "Failed to get a native window from Surface.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set up our OpenGL ES context associated with the default display
|
||
|
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||
|
if (mDisplay == EGL_NO_DISPLAY) {
|
||
|
LOG(ERROR) << "Failed to get egl display";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
EGLint major = 3;
|
||
|
EGLint minor = 0;
|
||
|
if (!eglInitialize(mDisplay, &major, &minor)) {
|
||
|
LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
const EGLint config_attribs[] = {
|
||
|
// Tag Value
|
||
|
EGL_RED_SIZE, 8,
|
||
|
EGL_GREEN_SIZE, 8,
|
||
|
EGL_BLUE_SIZE, 8,
|
||
|
EGL_DEPTH_SIZE, 0,
|
||
|
EGL_NONE
|
||
|
};
|
||
|
|
||
|
// Pick the default configuration without constraints (is this good enough?)
|
||
|
EGLConfig egl_config = {0};
|
||
|
EGLint numConfigs = -1;
|
||
|
eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
|
||
|
if (numConfigs != 1) {
|
||
|
LOG(ERROR) << "Didn't find a suitable format for our display window";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Create the EGL render target surface
|
||
|
mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
|
||
|
if (mSurface == EGL_NO_SURFACE) {
|
||
|
LOG(ERROR) << "eglCreateWindowSurface failed.";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Create the EGL context
|
||
|
// NOTE: Our shader is (currently at least) written to require version 3, so this
|
||
|
// is required.
|
||
|
const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||
|
mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
|
||
|
if (mContext == EGL_NO_CONTEXT) {
|
||
|
LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Activate our render target for drawing
|
||
|
if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
|
||
|
LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Create the shader program for our simple pipeline
|
||
|
mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
|
||
|
if (!mShaderProgram) {
|
||
|
LOG(ERROR) << "Failed to build shader program: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Create a GL texture that will eventually wrap our externally created texture surface(s)
|
||
|
glGenTextures(1, &mTextureMap);
|
||
|
if (mTextureMap <= 0) {
|
||
|
LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Turn off mip-mapping for the created texture surface
|
||
|
// (the inbound camera imagery doesn't have MIPs)
|
||
|
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GlWrapper::shutdown() {
|
||
|
|
||
|
// Drop our device textures
|
||
|
if (mKHRimage != EGL_NO_IMAGE_KHR) {
|
||
|
eglDestroyImageKHR(mDisplay, mKHRimage);
|
||
|
mKHRimage = EGL_NO_IMAGE_KHR;
|
||
|
}
|
||
|
|
||
|
// Release all GL resources
|
||
|
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||
|
eglDestroySurface(mDisplay, mSurface);
|
||
|
eglDestroyContext(mDisplay, mContext);
|
||
|
eglTerminate(mDisplay);
|
||
|
mSurface = EGL_NO_SURFACE;
|
||
|
mContext = EGL_NO_CONTEXT;
|
||
|
mDisplay = EGL_NO_DISPLAY;
|
||
|
|
||
|
// Release the window
|
||
|
mSurfaceHolder = nullptr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GlWrapper::showWindow(sp<IAutomotiveDisplayProxyService>& pWindowProxy, uint64_t id) {
|
||
|
if (pWindowProxy != nullptr) {
|
||
|
pWindowProxy->showWindow(id);
|
||
|
} else {
|
||
|
LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void GlWrapper::hideWindow(sp<IAutomotiveDisplayProxyService>& pWindowProxy, uint64_t id) {
|
||
|
if (pWindowProxy != nullptr) {
|
||
|
pWindowProxy->hideWindow(id);
|
||
|
} else {
|
||
|
LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool GlWrapper::updateImageTexture(const BufferDesc_1_0& buffer) {
|
||
|
BufferDesc_1_1 newBuffer = {};
|
||
|
AHardwareBuffer_Desc* pDesc =
|
||
|
reinterpret_cast<AHardwareBuffer_Desc *>(&newBuffer.buffer.description);
|
||
|
pDesc->width = buffer.width;
|
||
|
pDesc->height = buffer.height;
|
||
|
pDesc->layers = 1;
|
||
|
pDesc->format = buffer.format;
|
||
|
pDesc->usage = buffer.usage;
|
||
|
pDesc->stride = buffer.stride;
|
||
|
newBuffer.buffer.nativeHandle = buffer.memHandle;
|
||
|
newBuffer.pixelSize = buffer.pixelSize;
|
||
|
newBuffer.bufferId = buffer.bufferId;
|
||
|
|
||
|
return updateImageTexture(newBuffer);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool GlWrapper::updateImageTexture(const BufferDesc_1_1& aFrame) {
|
||
|
|
||
|
// If we haven't done it yet, create an "image" object to wrap the gralloc buffer
|
||
|
if (mKHRimage == EGL_NO_IMAGE_KHR) {
|
||
|
// create a temporary GraphicBuffer to wrap the provided handle
|
||
|
const AHardwareBuffer_Desc* pDesc =
|
||
|
reinterpret_cast<const AHardwareBuffer_Desc *>(&aFrame.buffer.description);
|
||
|
sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
|
||
|
pDesc->width,
|
||
|
pDesc->height,
|
||
|
pDesc->format,
|
||
|
pDesc->layers,
|
||
|
pDesc->usage,
|
||
|
pDesc->stride,
|
||
|
const_cast<native_handle_t*>(aFrame.buffer.nativeHandle.getNativeHandle()),
|
||
|
false /* keep ownership */
|
||
|
);
|
||
|
if (pGfxBuffer.get() == nullptr) {
|
||
|
LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Get a GL compatible reference to the graphics buffer we've been given
|
||
|
EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
|
||
|
EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
|
||
|
mKHRimage = eglCreateImageKHR(mDisplay,
|
||
|
EGL_NO_CONTEXT,
|
||
|
EGL_NATIVE_BUFFER_ANDROID,
|
||
|
cbuf,
|
||
|
eglImageAttributes);
|
||
|
if (mKHRimage == EGL_NO_IMAGE_KHR) {
|
||
|
LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Update the texture handle we already created to refer to this gralloc buffer
|
||
|
glActiveTexture(GL_TEXTURE0);
|
||
|
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
|
||
|
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GlWrapper::renderImageToScreen() {
|
||
|
// Set the viewport
|
||
|
glViewport(0, 0, mWidth, mHeight);
|
||
|
|
||
|
// Clear the color buffer
|
||
|
glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
|
||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||
|
|
||
|
// Select our screen space simple texture shader
|
||
|
glUseProgram(mShaderProgram);
|
||
|
|
||
|
// Bind the texture and assign it to the shader's sampler
|
||
|
glActiveTexture(GL_TEXTURE0);
|
||
|
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||
|
GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
|
||
|
glUniform1i(sampler, 0);
|
||
|
|
||
|
// We want our image to show up opaque regardless of alpha values
|
||
|
glDisable(GL_BLEND);
|
||
|
|
||
|
|
||
|
// Draw a rectangle on the screen
|
||
|
GLfloat vertsCarPos[] = { -0.8, 0.8, 0.0f, // left top in window space
|
||
|
0.8, 0.8, 0.0f, // right top
|
||
|
-0.8, -0.8, 0.0f, // left bottom
|
||
|
0.8, -0.8, 0.0f // right bottom
|
||
|
};
|
||
|
|
||
|
// NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
|
||
|
GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top
|
||
|
1.0f, 0.0f, // right top
|
||
|
0.0f, 1.0f, // left bottom
|
||
|
1.0f, 1.0f // right bottom
|
||
|
};
|
||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
|
||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
|
||
|
glEnableVertexAttribArray(0);
|
||
|
glEnableVertexAttribArray(1);
|
||
|
|
||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||
|
|
||
|
|
||
|
// Clean up and flip the rendered result to the front so it is visible
|
||
|
glDisableVertexAttribArray(0);
|
||
|
glDisableVertexAttribArray(1);
|
||
|
|
||
|
glFinish();
|
||
|
|
||
|
eglSwapBuffers(mDisplay, mSurface);
|
||
|
}
|
||
|
|