diff options
author | Greg Hartman <ghartman@google.com> | 2018-08-17 12:43:34 -0700 |
---|---|---|
committer | Greg Hartman <ghartman@google.com> | 2018-08-23 17:24:52 -0700 |
commit | 8cf8f6b088cc014921e1a9b3d4ebc93c3b5de8b8 (patch) | |
tree | d4968732d8516f2940cfcb340bacdf7693e0ceb3 /distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp | |
parent | 182e4691d1c5e724fc60ed94b6de8a3356018cb6 (diff) | |
download | device_generic_opengl-transport-8cf8f6b088cc014921e1a9b3d4ebc93c3b5de8b8.tar.gz device_generic_opengl-transport-8cf8f6b088cc014921e1a9b3d4ebc93c3b5de8b8.tar.bz2 device_generic_opengl-transport-8cf8f6b088cc014921e1a9b3d4ebc93c3b5de8b8.zip |
distrib/android-emugl: Move Android GPU emulation sources here.
This patch is used to add the unmodified sources of the GPU
emulation libraries that are normally under sdk/emulator/opengl/.
These sources were originally built with the platform build system,
but this is no longer the case. Since they are only built through
the emulator's own build system (i.e. android-rebuild.sh), it's
easier to put them under external/qemu/ to ensure that they are
always in sync with the rest of the emulator.
Note that the encoder sources are still in the platform under
device/generic/goldfish/opengl/.
A future patch will update the emulator build system to pick
the sources from here, and another one will remove the sources
from sdk/ after that.
Note: these are the unmodified sources from the following
commit in https://android.googlesource.com/platform/sdk.git :
182e469 emulator/opengl: Fix Windows cross-build.
Change-Id: If2010577ef4af4c6755ab345f424af0b546f9282
Diffstat (limited to 'distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp')
-rw-r--r-- | distrib/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp | 897 |
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(); +} |