Files
android_development/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp
Jesse Hall ac018fe3f6 EmuGL: refcount ColorBuffers on the host
The gralloc API assumes system-wide reference counting of gralloc
buffers. The host-GL accelerated gralloc maps buffers to host-side
ColorBuffer objects, but was destroying them unconditionally in
gralloc_free(), ignoring any additional references from
gralloc_register_buffer().

This affected the SurfaceTexture gralloc buffers used by the
Browser/WebView. For some reason these buffers are actually allocated
by SurfaceFlinger and passed back to the WebView through Binder. But
since SurfaceFlinger doesn't actually need the buffer for anything,
sometime after the WebView has called gralloc_register_buffer()
SurfaceFlinger calls gralloc_free() on it. This caused the host
ColorBuffer to be destroyed long before the WebView is done using it.

Change-Id: I33dbee887a48a6907041cf19e9f38a1f6c983eff
2012-02-21 11:13:20 -08:00

844 lines
24 KiB
C++

/*
* 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 <stdio.h>
#include "TimeUtils.h"
FrameBuffer *FrameBuffer::s_theFrameBuffer = NULL;
HandleType FrameBuffer::s_nextHandle = 0;
#ifdef WITH_GLES2
static const 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;
}
const char *extString = (const char *)s_gl2.glGetString(GL_EXTENSIONS);
if (!extString) {
extString = "";
}
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
//
const 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");
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());
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());
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());
delete fb;
return false;
}
// Make the context current
if (!fb->bind_locked()) {
ERR("Failed to make current\n");
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);
}
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();
// 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(NULL),
m_subWinDisplay(NULL),
m_lastPostedColorBuffer(0),
m_zRot(0.0f),
m_eglContextInitialized(false),
m_statsNumFrames(0),
m_statsStartTime(0LL)
{
m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
}
FrameBuffer::~FrameBuffer()
{
}
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 = NULL;
}
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 = NULL;
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)
{
android::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)
{
android::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)
{
android::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)
{
android::Mutex::Autolock mutex(m_lock);
m_contexts.erase(p_context);
}
void FrameBuffer::DestroyWindowSurface(HandleType p_surface)
{
android::Mutex::Autolock mutex(m_lock);
m_windows.erase(p_surface);
}
void FrameBuffer::openColorBuffer(HandleType p_colorbuffer)
{
android::Mutex::Autolock mutex(m_lock);
ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
if (c == m_colorbuffers.end()) {
// bad colorbuffer handle
return;
}
(*c).second.refcount++;
}
void FrameBuffer::closeColorBuffer(HandleType p_colorbuffer)
{
android::Mutex::Autolock mutex(m_lock);
ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
if (c == m_colorbuffers.end()) {
// bad colorbuffer handle
return;
}
if (--(*c).second.refcount == 0) {
m_colorbuffers.erase(c);
}
}
bool FrameBuffer::flushWindowSurfaceColorBuffer(HandleType p_surface)
{
android::Mutex::Autolock mutex(m_lock);
WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
if (w == m_windows.end()) {
// bad surface handle
return false;
}
(*w).second->flushColorBuffer();
return true;
}
bool FrameBuffer::setWindowSurfaceColorBuffer(HandleType p_surface,
HandleType p_colorbuffer)
{
android::Mutex::Autolock mutex(m_lock);
WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
if (w == m_windows.end()) {
// bad surface handle
return false;
}
ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
if (c == m_colorbuffers.end()) {
// 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)
{
android::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)
{
android::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)
{
android::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)
{
android::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)) {
// MakeCurrent failed
return false;
}
//
// Bind the surface(s) to the context
//
RenderThreadInfo *tinfo = getRenderThreadInfo();
if (draw.Ptr() == NULL && read.Ptr() == NULL) {
// if this is an unbind operation - make sure the current bound
// surfaces get unbound from the context.
draw = tinfo->currDrawSurf;
read = tinfo->currReadSurf;
}
if (draw.Ptr() != NULL && read.Ptr() != NULL) {
if (p_readSurface != p_drawSurface) {
draw->bind( ctx, SURFACE_BIND_DRAW );
read->bind( ctx, SURFACE_BIND_READ );
}
else {
draw->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();
}
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();
}