blob: 1bb82f88ba4879eb8856658e1586690a55c93acd [file] [log] [blame]
/*
* Copyright (C) 2011 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.
*/
// #define LOG_NDEBUG 0
#include "base/logging.h"
#include "base/utilities.h"
#include "core/gl_env.h"
#include "core/shader_program.h"
#include "core/vertex_frame.h"
#include "system/window.h"
#include <map>
#include <string>
#include <EGL/eglext.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <gui/GLConsumer.h>
#include <gui/IGraphicBufferProducer.h>
namespace android {
namespace filterfw {
GLEnv::GLEnv()
: display_(EGL_NO_DISPLAY),
context_id_(0),
surface_id_(0),
max_surface_id_(0),
created_context_(false),
created_surface_(false),
initialized_(false) {
}
GLEnv::~GLEnv() {
// Destroy surfaces
for (std::map<int, SurfaceWindowPair>::iterator it = surfaces_.begin();
it != surfaces_.end();
++it) {
if (it->first != 0 || created_surface_) {
eglDestroySurface(display(), it->second.first);
if (it->second.second) {
it->second.second->Destroy();
delete it->second.second;
}
}
}
// Destroy contexts
for (std::map<int, EGLContext>::iterator it = contexts_.begin();
it != contexts_.end();
++it) {
if (it->first != 0 || created_context_)
eglDestroyContext(display(), it->second);
}
// Destroy attached shaders and frames
STLDeleteValues(&attached_shaders_);
STLDeleteValues(&attached_vframes_);
// Destroy display
if (initialized_)
eglTerminate(display());
// Log error if this did not work
if (CheckEGLError("TearDown!"))
ALOGE("GLEnv: Error tearing down GL Environment!");
}
bool GLEnv::IsInitialized() const {
return (contexts_.size() > 0 &&
surfaces_.size() > 0 &&
display_ != EGL_NO_DISPLAY);
}
bool GLEnv::Deactivate() {
eglMakeCurrent(display(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
return !CheckEGLError("eglMakeCurrent");
}
bool GLEnv::Activate() {
ALOGV("Activate()");
if (display() != eglGetCurrentDisplay() ||
context() != eglGetCurrentContext() ||
surface() != eglGetCurrentSurface(EGL_DRAW)) {
// Make sure we are initialized
if (context() == EGL_NO_CONTEXT || surface() == EGL_NO_SURFACE)
return false;
// Make our context current
ALOGV("eglMakeCurrent");
eglMakeCurrent(display(), surface(), surface(), context());
return !CheckEGLMakeCurrentError();
}
return true;
}
bool GLEnv::SwapBuffers() {
const bool result = eglSwapBuffers(display(), surface()) == EGL_TRUE;
return !CheckEGLError("eglSwapBuffers") && result;
}
bool GLEnv::InitWithCurrentContext() {
if (IsInitialized())
return true;
display_ = eglGetCurrentDisplay();
contexts_[0] = eglGetCurrentContext();
surfaces_[0] = SurfaceWindowPair(eglGetCurrentSurface(EGL_DRAW), NULL);
return (context() != EGL_NO_CONTEXT) &&
(display() != EGL_NO_DISPLAY) &&
(surface() != EGL_NO_SURFACE);
}
bool GLEnv::InitWithNewContext() {
if (IsInitialized()) {
ALOGE("GLEnv: Attempting to reinitialize environment!");
return false;
}
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (CheckEGLError("eglGetDisplay")) return false;
EGLint majorVersion;
EGLint minorVersion;
eglInitialize(display(), &majorVersion, &minorVersion);
if (CheckEGLError("eglInitialize")) return false;
initialized_ = true;
// Configure context/surface
EGLConfig config;
EGLint numConfigs = -1;
// TODO(renn): Do we need the window bit here?
// TODO: Currently choosing the config that includes all
// This is not needed if the encoding is not being used
EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_RECORDABLE_ANDROID, EGL_TRUE,
EGL_NONE
};
eglChooseConfig(display(), configAttribs, &config, 1, &numConfigs);
if (numConfigs < 1) {
ALOGE("GLEnv::Init: No suitable EGL configuration found!");
return false;
}
// Create dummy surface using a GLConsumer
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
surfaceTexture_ = new GLConsumer(consumer, 0, GLConsumer::TEXTURE_EXTERNAL,
true, false);
window_ = new Surface(producer);
surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
if (CheckEGLError("eglCreateWindowSurface")) return false;
// Create context
EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
contexts_[0] = eglCreateContext(display(),
config,
EGL_NO_CONTEXT,
context_attribs);
if (CheckEGLError("eglCreateContext")) return false;
created_context_ = created_surface_ = true;
return true;
}
bool GLEnv::IsActive() const {
ALOGV("IsActive()");
return context() == eglGetCurrentContext()
&& display() == eglGetCurrentDisplay()
&& surface() == eglGetCurrentSurface(EGL_DRAW);
}
bool GLEnv::IsContextActive() const {
return context() == eglGetCurrentContext();
}
bool GLEnv::IsAnyContextActive() {
return eglGetCurrentContext() != EGL_NO_CONTEXT;
}
int GLEnv::AddWindowSurface(const EGLSurface& surface, WindowHandle* window_handle) {
const int id = ++max_surface_id_;
surfaces_[id] = SurfaceWindowPair(surface, window_handle);
return id;
}
int GLEnv::AddSurface(const EGLSurface& surface) {
return AddWindowSurface(surface, NULL);
}
bool GLEnv::SwitchToSurfaceId(int surface_id) {
ALOGV("SwitchToSurfaceId");
if (surface_id_ != surface_id) {
const SurfaceWindowPair* surface = FindOrNull(surfaces_, surface_id);
if (surface) {
bool wasActive = IsActive();
surface_id_ = surface_id;
return wasActive ? Activate() : true;
}
return false;
}
return true;
}
bool GLEnv::ReleaseSurfaceId(int surface_id) {
if (surface_id > 0) {
const SurfaceWindowPair* surface_window_pair = FindOrNull(surfaces_, surface_id);
if (surface_window_pair) {
if (surface_id_ == surface_id)
SwitchToSurfaceId(0);
eglDestroySurface(display(), surface_window_pair->first);
if (surface_window_pair->second) {
surface_window_pair->second->Destroy();
delete surface_window_pair->second;
}
surfaces_.erase(surface_id);
return true;
}
}
return false;
}
bool GLEnv::SetSurfaceTimestamp(int64_t timestamp) {
if (surface_id_ > 0) {
const SurfaceWindowPair* surface_window_pair = FindOrNull(surfaces_,
surface_id_);
if (surface_window_pair) {
ANativeWindow *window = static_cast<ANativeWindow*>(
surface_window_pair->second->InternalHandle());
native_window_set_buffers_timestamp(window, timestamp);
return true;
}
}
return false;
}
int GLEnv::FindSurfaceIdForWindow(const WindowHandle* window_handle) {
for (std::map<int, SurfaceWindowPair>::iterator it = surfaces_.begin();
it != surfaces_.end();
++it) {
const WindowHandle* my_handle = it->second.second;
if (my_handle && my_handle->Equals(window_handle)) {
return it->first;
}
}
return -1;
}
int GLEnv::AddContext(const EGLContext& context) {
const int id = contexts_.size();
contexts_[id] = context;
return id;
}
bool GLEnv::SwitchToContextId(int context_id) {
const EGLContext* context = FindOrNull(contexts_, context_id);
if (context) {
if (context_id_ != context_id) {
context_id_ = context_id;
return Activate();
}
return true;
}
return false;
}
void GLEnv::ReleaseContextId(int context_id) {
if (context_id > 0) {
const EGLContext* context = FindOrNull(contexts_, context_id);
if (context) {
contexts_.erase(context_id);
if (context_id_ == context_id && IsActive())
SwitchToContextId(0);
eglDestroyContext(display(), *context);
}
}
}
bool GLEnv::CheckGLError(const std::string& op) {
bool err = false;
for (GLint error = glGetError(); error; error = glGetError()) {
ALOGE("GL Error: Operation '%s' caused GL error (0x%x)\n",
op.c_str(),
error);
err = true;
}
return err;
}
bool GLEnv::CheckEGLError(const std::string& op) {
bool err = false;
for (EGLint error = eglGetError();
error != EGL_SUCCESS;
error = eglGetError()) {
ALOGE("EGL Error: Operation '%s' caused EGL error (0x%x)\n",
op.c_str(),
error);
err = true;
}
return err;
}
bool GLEnv::CheckEGLMakeCurrentError() {
bool err = false;
for (EGLint error = eglGetError();
error != EGL_SUCCESS;
error = eglGetError()) {
switch (error) {
case EGL_BAD_DISPLAY:
ALOGE("EGL Error: Attempting to activate context with bad display!");
break;
case EGL_BAD_SURFACE:
ALOGE("EGL Error: Attempting to activate context with bad surface!");
break;
case EGL_BAD_ACCESS:
ALOGE("EGL Error: Attempting to activate context, which is "
"already active in another thread!");
break;
default:
ALOGE("EGL Error: Making EGL rendering context current caused "
"error: 0x%x\n", error);
}
err = true;
}
return err;
}
GLuint GLEnv::GetCurrentProgram() {
GLint result;
glGetIntegerv(GL_CURRENT_PROGRAM, &result);
ALOG_ASSERT(result >= 0);
return static_cast<GLuint>(result);
}
EGLDisplay GLEnv::GetCurrentDisplay() {
return eglGetCurrentDisplay();
}
int GLEnv::NumberOfComponents(GLenum type) {
switch (type) {
case GL_BOOL:
case GL_FLOAT:
case GL_INT:
return 1;
case GL_BOOL_VEC2:
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
return 2;
case GL_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_BOOL_VEC3:
return 3;
case GL_BOOL_VEC4:
case GL_FLOAT_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_MAT2:
return 4;
case GL_FLOAT_MAT3:
return 9;
case GL_FLOAT_MAT4:
return 16;
default:
return 0;
}
}
void GLEnv::AttachShader(int key, ShaderProgram* shader) {
ShaderProgram* existingShader = ShaderWithKey(key);
if (existingShader)
delete existingShader;
attached_shaders_[key] = shader;
}
void GLEnv::AttachVertexFrame(int key, VertexFrame* frame) {
VertexFrame* existingFrame = VertexFrameWithKey(key);
if (existingFrame)
delete existingFrame;
attached_vframes_[key] = frame;
}
ShaderProgram* GLEnv::ShaderWithKey(int key) {
return FindPtrOrNull(attached_shaders_, key);
}
VertexFrame* GLEnv::VertexFrameWithKey(int key) {
return FindPtrOrNull(attached_vframes_, key);
}
} // namespace filterfw
} // namespace android