summaryrefslogtreecommitdiffstats
path: root/distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp')
-rw-r--r--distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp897
1 files changed, 897 insertions, 0 deletions
diff --git a/distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp b/distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp
new file mode 100644
index 000000000..e7a79602c
--- /dev/null
+++ b/distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp
@@ -0,0 +1,897 @@
+/*
+* 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.
+*/
+
+#include "FrameBuffer.h"
+#include "NativeSubWindow.h"
+#include "FBConfig.h"
+#include "EGLDispatch.h"
+#include "GLDispatch.h"
+#include "GL2Dispatch.h"
+#include "ThreadInfo.h"
+#include "TimeUtils.h"
+#include <stdio.h>
+
+FrameBuffer *FrameBuffer::s_theFrameBuffer = NULL;
+HandleType FrameBuffer::s_nextHandle = 0;
+
+#ifdef WITH_GLES2
+static char* getGLES2ExtensionString(EGLDisplay p_dpy)
+{
+ EGLConfig config;
+ EGLSurface surface;
+
+ GLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ int n;
+ if (!s_egl.eglChooseConfig(p_dpy, configAttribs,
+ &config, 1, &n)) {
+ return NULL;
+ }
+
+ EGLint pbufAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ surface = s_egl.eglCreatePbufferSurface(p_dpy, config, pbufAttribs);
+ if (surface == EGL_NO_SURFACE) {
+ return NULL;
+ }
+
+ GLint gl2ContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ EGLContext ctx = s_egl.eglCreateContext(p_dpy, config,
+ EGL_NO_CONTEXT,
+ gl2ContextAttribs);
+ if (ctx == EGL_NO_CONTEXT) {
+ s_egl.eglDestroySurface(p_dpy, surface);
+ return NULL;
+ }
+
+ if (!s_egl.eglMakeCurrent(p_dpy, surface, surface, ctx)) {
+ s_egl.eglDestroySurface(p_dpy, surface);
+ s_egl.eglDestroyContext(p_dpy, ctx);
+ return NULL;
+ }
+
+ // the string pointer may become invalid when the context is destroyed
+ const char* s = (const char*)s_gl2.glGetString(GL_EXTENSIONS);
+ char* extString = strdup(s ? s : "");
+
+ s_egl.eglMakeCurrent(p_dpy, NULL, NULL, NULL);
+ s_egl.eglDestroyContext(p_dpy, ctx);
+ s_egl.eglDestroySurface(p_dpy, surface);
+
+ return extString;
+}
+#endif
+
+void FrameBuffer::finalize(){
+ if(s_theFrameBuffer){
+ s_theFrameBuffer->removeSubWindow();
+ s_theFrameBuffer->m_colorbuffers.clear();
+ s_theFrameBuffer->m_windows.clear();
+ s_theFrameBuffer->m_contexts.clear();
+ s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
+ s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_eglContext);
+ s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufContext);
+ s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufSurface);
+ s_theFrameBuffer = NULL;
+ }
+}
+
+bool FrameBuffer::initialize(int width, int height)
+{
+ if (s_theFrameBuffer != NULL) {
+ return true;
+ }
+
+ //
+ // allocate space for the FrameBuffer object
+ //
+ FrameBuffer *fb = new FrameBuffer(width, height);
+ if (!fb) {
+ ERR("Failed to create fb\n");
+ return false;
+ }
+
+#ifdef WITH_GLES2
+ //
+ // Try to load GLES2 Plugin, not mandatory
+ //
+ if (getenv("ANDROID_NO_GLES2")) {
+ fb->m_caps.hasGL2 = false;
+ }
+ else {
+ fb->m_caps.hasGL2 = s_gl2_enabled;
+ }
+#else
+ fb->m_caps.hasGL2 = false;
+#endif
+
+ //
+ // Initialize backend EGL display
+ //
+ fb->m_eglDisplay = s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (fb->m_eglDisplay == EGL_NO_DISPLAY) {
+ ERR("Failed to Initialize backend EGL display\n");
+ delete fb;
+ return false;
+ }
+
+ if (!s_egl.eglInitialize(fb->m_eglDisplay, &fb->m_caps.eglMajor, &fb->m_caps.eglMinor)) {
+ ERR("Failed to eglInitialize\n");
+ delete fb;
+ return false;
+ }
+
+ DBG("egl: %d %d\n", fb->m_caps.eglMajor, fb->m_caps.eglMinor);
+ s_egl.eglBindAPI(EGL_OPENGL_ES_API);
+
+ //
+ // if GLES2 plugin has loaded - try to make GLES2 context and
+ // get GLES2 extension string
+ //
+ char* gl2Extensions = NULL;
+#ifdef WITH_GLES2
+ if (fb->m_caps.hasGL2) {
+ gl2Extensions = getGLES2ExtensionString(fb->m_eglDisplay);
+ if (!gl2Extensions) {
+ // Could not create GLES2 context - drop GL2 capability
+ fb->m_caps.hasGL2 = false;
+ }
+ }
+#endif
+
+ //
+ // Create EGL context for framebuffer post rendering.
+ //
+#if 0
+ GLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
+ EGL_NONE
+ };
+#else
+ GLint configAttribs[] = {
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_NONE
+ };
+#endif
+
+ int n;
+ if (!s_egl.eglChooseConfig(fb->m_eglDisplay, configAttribs,
+ &fb->m_eglConfig, 1, &n)) {
+ ERR("Failed on eglChooseConfig\n");
+ free(gl2Extensions);
+ delete fb;
+ return false;
+ }
+
+ GLint glContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 1,
+ EGL_NONE
+ };
+
+ fb->m_eglContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
+ EGL_NO_CONTEXT,
+ glContextAttribs);
+ if (fb->m_eglContext == EGL_NO_CONTEXT) {
+ printf("Failed to create Context 0x%x\n", s_egl.eglGetError());
+ free(gl2Extensions);
+ delete fb;
+ return false;
+ }
+
+ //
+ // Create another context which shares with the eglContext to be used
+ // when we bind the pbuffer. That prevent switching drawable binding
+ // back and forth on framebuffer context.
+ // The main purpose of it is to solve a "blanking" behaviour we see on
+ // on Mac platform when switching binded drawable for a context however
+ // it is more efficient on other platforms as well.
+ //
+ fb->m_pbufContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
+ fb->m_eglContext,
+ glContextAttribs);
+ if (fb->m_pbufContext == EGL_NO_CONTEXT) {
+ printf("Failed to create Pbuffer Context 0x%x\n", s_egl.eglGetError());
+ free(gl2Extensions);
+ delete fb;
+ return false;
+ }
+
+ //
+ // create a 1x1 pbuffer surface which will be used for binding
+ // the FB context.
+ // The FB output will go to a subwindow, if one exist.
+ //
+ EGLint pbufAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ fb->m_pbufSurface = s_egl.eglCreatePbufferSurface(fb->m_eglDisplay,
+ fb->m_eglConfig,
+ pbufAttribs);
+ if (fb->m_pbufSurface == EGL_NO_SURFACE) {
+ printf("Failed to create pbuf surface for FB 0x%x\n", s_egl.eglGetError());
+ free(gl2Extensions);
+ delete fb;
+ return false;
+ }
+
+ // Make the context current
+ if (!fb->bind_locked()) {
+ ERR("Failed to make current\n");
+ free(gl2Extensions);
+ delete fb;
+ return false;
+ }
+
+ //
+ // Initilize framebuffer capabilities
+ //
+ const char *glExtensions = (const char *)s_gl.glGetString(GL_EXTENSIONS);
+ bool has_gl_oes_image = false;
+ if (glExtensions) {
+ has_gl_oes_image = strstr(glExtensions, "GL_OES_EGL_image") != NULL;
+ }
+
+ if (fb->m_caps.hasGL2 && has_gl_oes_image) {
+ has_gl_oes_image &= strstr(gl2Extensions, "GL_OES_EGL_image") != NULL;
+ }
+ free(gl2Extensions);
+ gl2Extensions = NULL;
+
+ const char *eglExtensions = s_egl.eglQueryString(fb->m_eglDisplay,
+ EGL_EXTENSIONS);
+
+ if (eglExtensions && has_gl_oes_image) {
+ fb->m_caps.has_eglimage_texture_2d =
+ strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image") != NULL;
+ fb->m_caps.has_eglimage_renderbuffer =
+ strstr(eglExtensions, "EGL_KHR_gl_renderbuffer_image") != NULL;
+ }
+ else {
+ fb->m_caps.has_eglimage_texture_2d = false;
+ fb->m_caps.has_eglimage_renderbuffer = false;
+ }
+
+ //
+ // Fail initialization if not all of the following extensions
+ // exist:
+ // EGL_KHR_gl_texture_2d_image
+ // GL_OES_EGL_IMAGE (by both GLES implementations [1 and 2])
+ //
+ if (!fb->m_caps.has_eglimage_texture_2d) {
+ ERR("Failed: Missing egl_image related extension(s)\n");
+ delete fb;
+ return false;
+ }
+
+ //
+ // Initialize set of configs
+ //
+ InitConfigStatus configStatus = FBConfig::initConfigList(fb);
+ if (configStatus == INIT_CONFIG_FAILED) {
+ ERR("Failed: Initialize set of configs\n");
+ delete fb;
+ return false;
+ }
+
+ //
+ // Check that we have config for each GLES and GLES2
+ //
+ int nConfigs = FBConfig::getNumConfigs();
+ int nGLConfigs = 0;
+ int nGL2Configs = 0;
+ for (int i=0; i<nConfigs; i++) {
+ GLint rtype = FBConfig::get(i)->getRenderableType();
+ if (0 != (rtype & EGL_OPENGL_ES_BIT)) {
+ nGLConfigs++;
+ }
+ if (0 != (rtype & EGL_OPENGL_ES2_BIT)) {
+ nGL2Configs++;
+ }
+ }
+
+ //
+ // Fail initialization if no GLES configs exist
+ //
+ if (nGLConfigs == 0) {
+ delete fb;
+ return false;
+ }
+
+ //
+ // If no GLES2 configs exist - not GLES2 capability
+ //
+ if (nGL2Configs == 0) {
+ fb->m_caps.hasGL2 = false;
+ }
+
+ //
+ // Initialize some GL state in the pbuffer context
+ //
+ fb->initGLState();
+
+ //
+ // Cache the GL strings so we don't have to think about threading or
+ // current-context when asked for them.
+ //
+ fb->m_glVendor = (const char*)s_gl.glGetString(GL_VENDOR);
+ fb->m_glRenderer = (const char*)s_gl.glGetString(GL_RENDERER);
+ fb->m_glVersion = (const char*)s_gl.glGetString(GL_VERSION);
+
+ // release the FB context
+ fb->unbind_locked();
+
+ //
+ // Keep the singleton framebuffer pointer
+ //
+ s_theFrameBuffer = fb;
+ return true;
+}
+
+FrameBuffer::FrameBuffer(int p_width, int p_height) :
+ m_width(p_width),
+ m_height(p_height),
+ m_eglDisplay(EGL_NO_DISPLAY),
+ m_eglSurface(EGL_NO_SURFACE),
+ m_eglContext(EGL_NO_CONTEXT),
+ m_pbufContext(EGL_NO_CONTEXT),
+ m_prevContext(EGL_NO_CONTEXT),
+ m_prevReadSurf(EGL_NO_SURFACE),
+ m_prevDrawSurf(EGL_NO_SURFACE),
+ m_subWin((EGLNativeWindowType)0),
+ m_subWinDisplay(NULL),
+ m_lastPostedColorBuffer(0),
+ m_zRot(0.0f),
+ m_eglContextInitialized(false),
+ m_statsNumFrames(0),
+ m_statsStartTime(0LL),
+ m_onPost(NULL),
+ m_onPostContext(NULL),
+ m_fbImage(NULL),
+ m_glVendor(NULL),
+ m_glRenderer(NULL),
+ m_glVersion(NULL)
+{
+ m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
+}
+
+FrameBuffer::~FrameBuffer()
+{
+ free(m_fbImage);
+}
+
+void FrameBuffer::setPostCallback(OnPostFn onPost, void* onPostContext)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ m_onPost = onPost;
+ m_onPostContext = onPostContext;
+ if (m_onPost && !m_fbImage) {
+ m_fbImage = (unsigned char*)malloc(4 * m_width * m_height);
+ if (!m_fbImage) {
+ ERR("out of memory, cancelling OnPost callback");
+ m_onPost = NULL;
+ m_onPostContext = NULL;
+ return;
+ }
+ }
+}
+
+bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window,
+ int p_x, int p_y,
+ int p_width, int p_height, float zRot)
+{
+ bool success = false;
+
+ if (s_theFrameBuffer) {
+ s_theFrameBuffer->m_lock.lock();
+ FrameBuffer *fb = s_theFrameBuffer;
+ if (!fb->m_subWin) {
+
+ // create native subwindow for FB display output
+ fb->m_subWin = createSubWindow(p_window,
+ &fb->m_subWinDisplay,
+ p_x,p_y,p_width,p_height);
+ if (fb->m_subWin) {
+ fb->m_nativeWindow = p_window;
+
+ // create EGLSurface from the generated subwindow
+ fb->m_eglSurface = s_egl.eglCreateWindowSurface(fb->m_eglDisplay,
+ fb->m_eglConfig,
+ fb->m_subWin,
+ NULL);
+
+ if (fb->m_eglSurface == EGL_NO_SURFACE) {
+ ERR("Failed to create surface\n");
+ destroySubWindow(fb->m_subWinDisplay, fb->m_subWin);
+ fb->m_subWin = (EGLNativeWindowType)0;
+ }
+ else if (fb->bindSubwin_locked()) {
+ // Subwin creation was successfull,
+ // update viewport and z rotation and draw
+ // the last posted color buffer.
+ s_gl.glViewport(0, 0, p_width, p_height);
+ fb->m_zRot = zRot;
+ fb->post( fb->m_lastPostedColorBuffer, false );
+ fb->unbind_locked();
+ success = true;
+ }
+ }
+ }
+ s_theFrameBuffer->m_lock.unlock();
+ }
+
+ return success;
+}
+
+bool FrameBuffer::removeSubWindow()
+{
+ bool removed = false;
+ if (s_theFrameBuffer) {
+ s_theFrameBuffer->m_lock.lock();
+ if (s_theFrameBuffer->m_subWin) {
+ s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
+ s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,
+ s_theFrameBuffer->m_eglSurface);
+ destroySubWindow(s_theFrameBuffer->m_subWinDisplay,
+ s_theFrameBuffer->m_subWin);
+
+ s_theFrameBuffer->m_eglSurface = EGL_NO_SURFACE;
+ s_theFrameBuffer->m_subWin = (EGLNativeWindowType)0;
+ removed = true;
+ }
+ s_theFrameBuffer->m_lock.unlock();
+ }
+ return removed;
+}
+
+HandleType FrameBuffer::genHandle()
+{
+ HandleType id;
+ do {
+ id = ++s_nextHandle;
+ } while( id == 0 ||
+ m_contexts.find(id) != m_contexts.end() ||
+ m_windows.find(id) != m_windows.end() );
+
+ return id;
+}
+
+HandleType FrameBuffer::createColorBuffer(int p_width, int p_height,
+ GLenum p_internalFormat)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ HandleType ret = 0;
+
+ ColorBufferPtr cb( ColorBuffer::create(p_width, p_height, p_internalFormat) );
+ if (cb.Ptr() != NULL) {
+ ret = genHandle();
+ m_colorbuffers[ret].cb = cb;
+ m_colorbuffers[ret].refcount = 1;
+ }
+ return ret;
+}
+
+HandleType FrameBuffer::createRenderContext(int p_config, HandleType p_share,
+ bool p_isGL2)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ HandleType ret = 0;
+
+ RenderContextPtr share(NULL);
+ if (p_share != 0) {
+ RenderContextMap::iterator s( m_contexts.find(p_share) );
+ if (s == m_contexts.end()) {
+ return 0;
+ }
+ share = (*s).second;
+ }
+
+ RenderContextPtr rctx( RenderContext::create(p_config, share, p_isGL2) );
+ if (rctx.Ptr() != NULL) {
+ ret = genHandle();
+ m_contexts[ret] = rctx;
+ }
+ return ret;
+}
+
+HandleType FrameBuffer::createWindowSurface(int p_config, int p_width, int p_height)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ HandleType ret = 0;
+ WindowSurfacePtr win( WindowSurface::create(p_config, p_width, p_height) );
+ if (win.Ptr() != NULL) {
+ ret = genHandle();
+ m_windows[ret] = win;
+ }
+
+ return ret;
+}
+
+void FrameBuffer::DestroyRenderContext(HandleType p_context)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ m_contexts.erase(p_context);
+}
+
+void FrameBuffer::DestroyWindowSurface(HandleType p_surface)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ m_windows.erase(p_surface);
+}
+
+int FrameBuffer::openColorBuffer(HandleType p_colorbuffer)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
+ if (c == m_colorbuffers.end()) {
+ // bad colorbuffer handle
+ ERR("FB: openColorBuffer cb handle %#x not found\n", p_colorbuffer);
+ return -1;
+ }
+ (*c).second.refcount++;
+ return 0;
+}
+
+void FrameBuffer::closeColorBuffer(HandleType p_colorbuffer)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+ ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
+ if (c == m_colorbuffers.end()) {
+ ERR("FB: closeColorBuffer cb handle %#x not found\n", p_colorbuffer);
+ // bad colorbuffer handle
+ return;
+ }
+ if (--(*c).second.refcount == 0) {
+ m_colorbuffers.erase(c);
+ }
+}
+
+bool FrameBuffer::flushWindowSurfaceColorBuffer(HandleType p_surface)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
+ if (w == m_windows.end()) {
+ ERR("FB::flushWindowSurfaceColorBuffer: window handle %#x not found\n", p_surface);
+ // bad surface handle
+ return false;
+ }
+
+ return (*w).second->flushColorBuffer();
+}
+
+bool FrameBuffer::setWindowSurfaceColorBuffer(HandleType p_surface,
+ HandleType p_colorbuffer)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
+ if (w == m_windows.end()) {
+ // bad surface handle
+ ERR("%s: bad window surface handle %#x\n", __FUNCTION__, p_surface);
+ return false;
+ }
+
+ ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
+ if (c == m_colorbuffers.end()) {
+ ERR("%s: bad color buffer handle %#x\n", __FUNCTION__, p_colorbuffer);
+ // bad colorbuffer handle
+ return false;
+ }
+
+ (*w).second->setColorBuffer( (*c).second.cb );
+
+ return true;
+}
+
+bool FrameBuffer::updateColorBuffer(HandleType p_colorbuffer,
+ int x, int y, int width, int height,
+ GLenum format, GLenum type, void *pixels)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
+ if (c == m_colorbuffers.end()) {
+ // bad colorbuffer handle
+ return false;
+ }
+
+ (*c).second.cb->subUpdate(x, y, width, height, format, type, pixels);
+
+ return true;
+}
+
+bool FrameBuffer::bindColorBufferToTexture(HandleType p_colorbuffer)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
+ if (c == m_colorbuffers.end()) {
+ // bad colorbuffer handle
+ return false;
+ }
+
+ return (*c).second.cb->bindToTexture();
+}
+
+bool FrameBuffer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
+ if (c == m_colorbuffers.end()) {
+ // bad colorbuffer handle
+ return false;
+ }
+
+ return (*c).second.cb->bindToRenderbuffer();
+}
+
+bool FrameBuffer::bindContext(HandleType p_context,
+ HandleType p_drawSurface,
+ HandleType p_readSurface)
+{
+ emugl::Mutex::AutoLock mutex(m_lock);
+
+ WindowSurfacePtr draw(NULL), read(NULL);
+ RenderContextPtr ctx(NULL);
+
+ //
+ // if this is not an unbind operation - make sure all handles are good
+ //
+ if (p_context || p_drawSurface || p_readSurface) {
+ RenderContextMap::iterator r( m_contexts.find(p_context) );
+ if (r == m_contexts.end()) {
+ // bad context handle
+ return false;
+ }
+
+ ctx = (*r).second;
+ WindowSurfaceMap::iterator w( m_windows.find(p_drawSurface) );
+ if (w == m_windows.end()) {
+ // bad surface handle
+ return false;
+ }
+ draw = (*w).second;
+
+ if (p_readSurface != p_drawSurface) {
+ WindowSurfaceMap::iterator w( m_windows.find(p_readSurface) );
+ if (w == m_windows.end()) {
+ // bad surface handle
+ return false;
+ }
+ read = (*w).second;
+ }
+ else {
+ read = draw;
+ }
+ }
+
+ if (!s_egl.eglMakeCurrent(m_eglDisplay,
+ draw ? draw->getEGLSurface() : EGL_NO_SURFACE,
+ read ? read->getEGLSurface() : EGL_NO_SURFACE,
+ ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT)) {
+ ERR("eglMakeCurrent failed\n");
+ return false;
+ }
+
+ //
+ // Bind the surface(s) to the context
+ //
+ RenderThreadInfo *tinfo = RenderThreadInfo::get();
+ WindowSurfacePtr bindDraw, bindRead;
+ if (draw.Ptr() == NULL && read.Ptr() == NULL) {
+ // Unbind the current read and draw surfaces from the context
+ bindDraw = tinfo->currDrawSurf;
+ bindRead = tinfo->currReadSurf;
+ } else {
+ bindDraw = draw;
+ bindRead = read;
+ }
+
+ if (bindDraw.Ptr() != NULL && bindRead.Ptr() != NULL) {
+ if (bindDraw.Ptr() != bindRead.Ptr()) {
+ bindDraw->bind(ctx, SURFACE_BIND_DRAW);
+ bindRead->bind(ctx, SURFACE_BIND_READ);
+ }
+ else {
+ bindDraw->bind(ctx, SURFACE_BIND_READDRAW);
+ }
+ }
+
+ //
+ // update thread info with current bound context
+ //
+ tinfo->currContext = ctx;
+ tinfo->currDrawSurf = draw;
+ tinfo->currReadSurf = read;
+ if (ctx) {
+ if (ctx->isGL2()) tinfo->m_gl2Dec.setContextData(&ctx->decoderContextData());
+ else tinfo->m_glDec.setContextData(&ctx->decoderContextData());
+ }
+ else {
+ tinfo->m_glDec.setContextData(NULL);
+ tinfo->m_gl2Dec.setContextData(NULL);
+ }
+ return true;
+}
+
+//
+// The framebuffer lock should be held when calling this function !
+//
+bool FrameBuffer::bind_locked()
+{
+ EGLContext prevContext = s_egl.eglGetCurrentContext();
+ EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
+ EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+
+ if (!s_egl.eglMakeCurrent(m_eglDisplay, m_pbufSurface,
+ m_pbufSurface, m_pbufContext)) {
+ ERR("eglMakeCurrent failed\n");
+ return false;
+ }
+
+ m_prevContext = prevContext;
+ m_prevReadSurf = prevReadSurf;
+ m_prevDrawSurf = prevDrawSurf;
+ return true;
+}
+
+bool FrameBuffer::bindSubwin_locked()
+{
+ EGLContext prevContext = s_egl.eglGetCurrentContext();
+ EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
+ EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+
+ if (!s_egl.eglMakeCurrent(m_eglDisplay, m_eglSurface,
+ m_eglSurface, m_eglContext)) {
+ ERR("eglMakeCurrent failed\n");
+ return false;
+ }
+
+ //
+ // initialize GL state in eglContext if not yet initilaized
+ //
+ if (!m_eglContextInitialized) {
+ initGLState();
+ m_eglContextInitialized = true;
+ }
+
+ m_prevContext = prevContext;
+ m_prevReadSurf = prevReadSurf;
+ m_prevDrawSurf = prevDrawSurf;
+ return true;
+}
+
+bool FrameBuffer::unbind_locked()
+{
+ if (!s_egl.eglMakeCurrent(m_eglDisplay, m_prevDrawSurf,
+ m_prevReadSurf, m_prevContext)) {
+ return false;
+ }
+
+ m_prevContext = EGL_NO_CONTEXT;
+ m_prevReadSurf = EGL_NO_SURFACE;
+ m_prevDrawSurf = EGL_NO_SURFACE;
+ return true;
+}
+
+bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock)
+{
+ if (needLock) m_lock.lock();
+ bool ret = false;
+
+ ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
+ if (c != m_colorbuffers.end()) {
+
+ m_lastPostedColorBuffer = p_colorbuffer;
+ if (!m_subWin) {
+ // no subwindow created for the FB output
+ // cannot post the colorbuffer
+ if (needLock) m_lock.unlock();
+ return ret;
+ }
+
+
+ // bind the subwindow eglSurface
+ if (!bindSubwin_locked()) {
+ ERR("FrameBuffer::post eglMakeCurrent failed\n");
+ if (needLock) m_lock.unlock();
+ return false;
+ }
+
+ //
+ // render the color buffer to the window
+ //
+ s_gl.glPushMatrix();
+ s_gl.glRotatef(m_zRot, 0.0f, 0.0f, 1.0f);
+ if (m_zRot != 0.0f) {
+ s_gl.glClear(GL_COLOR_BUFFER_BIT);
+ }
+ ret = (*c).second.cb->post();
+ s_gl.glPopMatrix();
+
+ if (ret) {
+ //
+ // output FPS statistics
+ //
+ if (m_fpsStats) {
+ long long currTime = GetCurrentTimeMS();
+ m_statsNumFrames++;
+ if (currTime - m_statsStartTime >= 1000) {
+ float dt = (float)(currTime - m_statsStartTime) / 1000.0f;
+ printf("FPS: %5.3f\n", (float)m_statsNumFrames / dt);
+ m_statsStartTime = currTime;
+ m_statsNumFrames = 0;
+ }
+ }
+
+ s_egl.eglSwapBuffers(m_eglDisplay, m_eglSurface);
+ }
+
+ // restore previous binding
+ unbind_locked();
+
+ //
+ // Send framebuffer (without FPS overlay) to callback
+ //
+ if (m_onPost) {
+ (*c).second.cb->readback(m_fbImage);
+ m_onPost(m_onPostContext, m_width, m_height, -1,
+ GL_RGBA, GL_UNSIGNED_BYTE, m_fbImage);
+ }
+
+ }
+
+ if (needLock) m_lock.unlock();
+ return ret;
+}
+
+bool FrameBuffer::repost()
+{
+ if (m_lastPostedColorBuffer) {
+ return post( m_lastPostedColorBuffer );
+ }
+ return false;
+}
+
+void FrameBuffer::initGLState()
+{
+ s_gl.glMatrixMode(GL_PROJECTION);
+ s_gl.glLoadIdentity();
+ s_gl.glOrthof(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
+ s_gl.glMatrixMode(GL_MODELVIEW);
+ s_gl.glLoadIdentity();
+}