diff --git a/tools/emulator/opengl/host/include/libOpenglRender/render_api.h b/tools/emulator/opengl/host/include/libOpenglRender/render_api.h index 14ebc682c..9c76b3ecf 100644 --- a/tools/emulator/opengl/host/include/libOpenglRender/render_api.h +++ b/tools/emulator/opengl/host/include/libOpenglRender/render_api.h @@ -22,6 +22,38 @@ extern "C" { #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 // GLES translator libraries. This function must be called before anything // 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 // 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 - diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp index ae3a0fd40..b7599cec8 100644 --- a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp +++ b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.cpp @@ -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) { return true; @@ -110,7 +110,7 @@ bool FrameBuffer::initialize(int width, int height) // // allocate space for the FrameBuffer object // - FrameBuffer *fb = new FrameBuffer(width, height); + FrameBuffer *fb = new FrameBuffer(width, height, onPost, onPostContext); if (!fb) { ERR("Failed to create fb\n"); return false; @@ -334,6 +334,17 @@ bool FrameBuffer::initialize(int width, int height) // 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 fb->unbind_locked(); @@ -344,7 +355,8 @@ bool FrameBuffer::initialize(int width, int height) 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_height(p_height), m_eglDisplay(EGL_NO_DISPLAY), @@ -360,13 +372,17 @@ FrameBuffer::FrameBuffer(int p_width, int p_height) : m_zRot(0.0f), m_eglContextInitialized(false), 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; } FrameBuffer::~FrameBuffer() { + free(m_fbImage); } bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window, @@ -799,6 +815,15 @@ bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock) s_gl.glPopMatrix(); 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 diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h index b5293ffe3..343f384e3 100644 --- a/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h +++ b/tools/emulator/opengl/host/libs/libOpenglRender/FrameBuffer.h @@ -46,7 +46,7 @@ struct FrameBufferCaps class FrameBuffer { 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, int x, int y, int width, int height, float zRot); @@ -90,7 +90,7 @@ public: } private: - FrameBuffer(int p_width, int p_height); + FrameBuffer(int p_width, int p_height, OnPostFn onPost, void* onPostContext); ~FrameBuffer(); HandleType genHandle(); bool bindSubwin_locked(); @@ -129,5 +129,9 @@ private: int m_statsNumFrames; long long m_statsStartTime; bool m_fpsStats; + + OnPostFn m_onPost; + void* m_onPostContext; + unsigned char* m_fbImage; }; #endif diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp index 8a5f46422..c8d3e06ce 100644 --- a/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp +++ b/tools/emulator/opengl/host/libs/libOpenglRender/render_api.cpp @@ -76,7 +76,8 @@ bool initLibrary(void) 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 // on a thread in the current process. // - bool inited = FrameBuffer::initialize(width, height); + bool inited = FrameBuffer::initialize(width, height, onPost, onPostContext); if (!inited) { return false; } @@ -106,6 +107,17 @@ bool initOpenGLRenderer(int width, int height, int portNum) s_renderThread->start(); #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 // diff --git a/tools/emulator/opengl/host/renderer/main.cpp b/tools/emulator/opengl/host/renderer/main.cpp index 4549c5636..ae7ace3dd 100644 --- a/tools/emulator/opengl/host/renderer/main.cpp +++ b/tools/emulator/opengl/host/renderer/main.cpp @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) // // initialize Framebuffer // - bool inited = FrameBuffer::initialize(winWidth, winHeight); + bool inited = FrameBuffer::initialize(winWidth, winHeight, NULL, NULL); if (!inited) { fprintf(stderr,"Failed to initialize Framebuffer\n"); return -1;