Merge "EmuGL: Deliver every frame to a callback"

This commit is contained in:
Jesse Hall
2012-03-26 15:42:20 -07:00
committed by android code review
5 changed files with 84 additions and 11 deletions

View File

@@ -22,6 +22,38 @@ extern "C" {
#include "render_api_platform_types.h" #include "render_api_platform_types.h"
/* If a function with this signature is passed to initOpenGLRenderer(),
* it will be called by the renderer just before each new frame is displayed,
* providing a copy of the framebuffer contents.
*
* The callback will be called from one of the renderer's threads, so will
* probably need synchronization on any data structures it modifies. The
* pixels buffer may be overwritten as soon as the callback returns; if it needs
* the pixels afterwards it must copy them.
*
* The pixels buffer is intentionally not const: the callback may modify the
* data without copying to another buffer if it wants, e.g. in-place RGBA to RGB
* conversion, or in-place y-inversion.
*
* Parameters are:
* context The pointer optionally provided when the callback was
* registered. The client can use this to pass whatever
* information it wants to the callback.
* width, height Dimensions of the image, in pixels. Rows are tightly packed;
* there is no inter-row padding.
* ydir Indicates row order: 1 means top-to-bottom order, -1 means
* bottom-to-top order.
* format, type Format and type GL enums, as used in glTexImage2D() or
* glReadPixels(), describing the pixel format.
* pixels The framebuffer image.
*
* In the first implementation, ydir is always -1 (bottom to top), format and
* type are always GL_RGBA and GL_UNSIGNED_BYTE, and the width and height will
* always be the same as the ones passed to initOpenGLRenderer().
*/
typedef void (*OnPostFn)(void* context, int width, int height, int ydir,
int format, int type, unsigned char* pixels);
// initLibrary - initialize the library and tries to load the corresponding // initLibrary - initialize the library and tries to load the corresponding
// GLES translator libraries. This function must be called before anything // GLES translator libraries. This function must be called before anything
// else to ensure that everything works. If it returns an error, then // else to ensure that everything works. If it returns an error, then
@@ -53,8 +85,8 @@ int setStreamMode(int mode);
// This function is *NOT* thread safe and should be called first // This function is *NOT* thread safe and should be called first
// to initialize the renderer after initLibrary(). // to initialize the renderer after initLibrary().
// //
bool initOpenGLRenderer(int width, int height, int portNum); bool initOpenGLRenderer(int width, int height, int portNum,
OnPostFn onPost, void* onPostContext);
// //
// createOpenGLSubwindow - // createOpenGLSubwindow -

View File

@@ -101,7 +101,7 @@ void FrameBuffer::finalize(){
} }
} }
bool FrameBuffer::initialize(int width, int height) bool FrameBuffer::initialize(int width, int height, OnPostFn onPost, void* onPostContext)
{ {
if (s_theFrameBuffer != NULL) { if (s_theFrameBuffer != NULL) {
return true; return true;
@@ -110,7 +110,7 @@ bool FrameBuffer::initialize(int width, int height)
// //
// allocate space for the FrameBuffer object // allocate space for the FrameBuffer object
// //
FrameBuffer *fb = new FrameBuffer(width, height); FrameBuffer *fb = new FrameBuffer(width, height, onPost, onPostContext);
if (!fb) { if (!fb) {
ERR("Failed to create fb\n"); ERR("Failed to create fb\n");
return false; return false;
@@ -334,6 +334,17 @@ bool FrameBuffer::initialize(int width, int height)
// //
fb->initGLState(); fb->initGLState();
//
// Allocate space for the onPost framebuffer image
//
if (onPost) {
fb->m_fbImage = (unsigned char*)malloc(4 * width * height);
if (!fb->m_fbImage) {
delete fb;
return false;
}
}
// release the FB context // release the FB context
fb->unbind_locked(); fb->unbind_locked();
@@ -344,7 +355,8 @@ bool FrameBuffer::initialize(int width, int height)
return true; return true;
} }
FrameBuffer::FrameBuffer(int p_width, int p_height) : FrameBuffer::FrameBuffer(int p_width, int p_height,
OnPostFn onPost, void* onPostContext) :
m_width(p_width), m_width(p_width),
m_height(p_height), m_height(p_height),
m_eglDisplay(EGL_NO_DISPLAY), m_eglDisplay(EGL_NO_DISPLAY),
@@ -360,13 +372,17 @@ FrameBuffer::FrameBuffer(int p_width, int p_height) :
m_zRot(0.0f), m_zRot(0.0f),
m_eglContextInitialized(false), m_eglContextInitialized(false),
m_statsNumFrames(0), m_statsNumFrames(0),
m_statsStartTime(0LL) m_statsStartTime(0LL),
m_onPost(onPost),
m_onPostContext(onPostContext),
m_fbImage(NULL)
{ {
m_fpsStats = getenv("SHOW_FPS_STATS") != NULL; m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
} }
FrameBuffer::~FrameBuffer() FrameBuffer::~FrameBuffer()
{ {
free(m_fbImage);
} }
bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window, bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window,
@@ -780,6 +796,15 @@ bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock)
s_gl.glPopMatrix(); s_gl.glPopMatrix();
if (ret) { if (ret) {
//
// Send framebuffer (without FPS overlay) to callback
//
if (m_onPost) {
s_gl.glReadPixels(0, 0, m_width, m_height,
GL_RGBA, GL_UNSIGNED_BYTE, m_fbImage);
m_onPost(m_onPostContext, m_width, m_height, -1,
GL_RGBA, GL_UNSIGNED_BYTE, m_fbImage);
}
// //
// output FPS statistics // output FPS statistics

View File

@@ -42,7 +42,7 @@ struct FrameBufferCaps
class FrameBuffer class FrameBuffer
{ {
public: public:
static bool initialize(int width, int height); static bool initialize(int width, int height, OnPostFn onPost, void* onPostContext);
static bool setupSubWindow(FBNativeWindowType p_window, static bool setupSubWindow(FBNativeWindowType p_window,
int x, int y, int x, int y,
int width, int height, float zRot); int width, int height, float zRot);
@@ -85,7 +85,7 @@ public:
} }
private: private:
FrameBuffer(int p_width, int p_height); FrameBuffer(int p_width, int p_height, OnPostFn onPost, void* onPostContext);
~FrameBuffer(); ~FrameBuffer();
HandleType genHandle(); HandleType genHandle();
bool bindSubwin_locked(); bool bindSubwin_locked();
@@ -124,5 +124,9 @@ private:
int m_statsNumFrames; int m_statsNumFrames;
long long m_statsStartTime; long long m_statsStartTime;
bool m_fpsStats; bool m_fpsStats;
OnPostFn m_onPost;
void* m_onPostContext;
unsigned char* m_fbImage;
}; };
#endif #endif

View File

@@ -76,7 +76,8 @@ bool initLibrary(void)
return true; return true;
} }
bool initOpenGLRenderer(int width, int height, int portNum) bool initOpenGLRenderer(int width, int height, int portNum,
OnPostFn onPost, void* onPostContext)
{ {
// //
@@ -93,7 +94,7 @@ bool initOpenGLRenderer(int width, int height, int portNum)
// initialize the renderer and listen to connections // initialize the renderer and listen to connections
// on a thread in the current process. // on a thread in the current process.
// //
bool inited = FrameBuffer::initialize(width, height); bool inited = FrameBuffer::initialize(width, height, onPost, onPostContext);
if (!inited) { if (!inited) {
return false; return false;
} }
@@ -106,6 +107,17 @@ bool initOpenGLRenderer(int width, int height, int portNum)
s_renderThread->start(); s_renderThread->start();
#else #else
if (onPost) {
// onPost callback not supported with separate renderer process.
//
// If we ever revive separate process support, we could make the choice
// between thread and process at runtime instead of compile time, and
// choose the thread path if an onPost callback is requested. Or, the
// callback could be supported with a separate process using shmem or
// other IPC mechanism.
return false;
}
// //
// Launch emulator_renderer // Launch emulator_renderer
// //

View File

@@ -120,7 +120,7 @@ int main(int argc, char *argv[])
// //
// initialize Framebuffer // initialize Framebuffer
// //
bool inited = FrameBuffer::initialize(winWidth, winHeight); bool inited = FrameBuffer::initialize(winWidth, winHeight, NULL, NULL);
if (!inited) { if (!inited) {
fprintf(stderr,"Failed to initialize Framebuffer\n"); fprintf(stderr,"Failed to initialize Framebuffer\n");
return -1; return -1;