Merge "EmuGL: Deliver every frame to a callback"
This commit is contained in:
@@ -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 -
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user