Add EGLImage support to GL driver.

On videocore, uploading image textures using OpenVG instead of OpenGL ES saves us about 4.5 milliseconds per frame.
This commit is contained in:
Toad King 2012-10-19 23:08:53 -04:00
parent faf1b47d61
commit c7dad59402
13 changed files with 129 additions and 72 deletions

View File

@ -312,7 +312,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -630,7 +630,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -467,7 +467,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -383,7 +383,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -321,7 +321,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -50,10 +50,12 @@ static enum gfx_ctx_api g_api;
static unsigned g_fb_width; static unsigned g_fb_width;
static unsigned g_fb_height; static unsigned g_fb_height;
static EGLImageKHR eglBuffer; static EGLImageKHR eglBuffer[MAX_EGLIMAGE_TEXTURES];
static EGLContext g_eglimage_ctx; static EGLContext g_eglimage_ctx;
static EGLSurface g_pbuff_surf; static EGLSurface g_pbuff_surf;
static VGImage g_egl_vgimage; static VGImage g_egl_vgimage[MAX_EGLIMAGE_TEXTURES];
static bool g_smooth;
static unsigned g_egl_res;
PFNEGLCREATEIMAGEKHRPROC peglCreateImageKHR; PFNEGLCREATEIMAGEKHRPROC peglCreateImageKHR;
PFNEGLDESTROYIMAGEKHRPROC peglDestroyImageKHR; PFNEGLDESTROYIMAGEKHRPROC peglDestroyImageKHR;
@ -235,23 +237,42 @@ static bool gfx_ctx_set_video_mode(
return true; return true;
} }
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
{
g_api = api;
switch (api)
{
case GFX_CTX_OPENGL_API:
return eglBindAPI(EGL_OPENGL_API);
case GFX_CTX_OPENGL_ES_API:
return eglBindAPI(EGL_OPENGL_ES_API);
case GFX_CTX_OPENVG_API:
return eglBindAPI(EGL_OPENVG_API);
default:
return false;
}
}
static void gfx_ctx_destroy(void) static void gfx_ctx_destroy(void)
{ {
if (g_egl_dpy) if (g_egl_dpy)
{ {
if (eglBuffer && peglDestroyImageKHR) for (unsigned i = 0; i < MAX_EGLIMAGE_TEXTURES; i++)
{ {
eglBindAPI(EGL_OPENGL_VG_API); if (eglBuffer[i] && peglDestroyImageKHR)
eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx); {
peglDestroyImageKHR(e_egl_dpy, eglBuffer); eglBindAPI(EGL_OPENVG_API);
} eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx);
peglDestroyImageKHR(g_egl_dpy, eglBuffer[i]);
}
if (g_egl_vgimage) if (g_egl_vgimage[i])
{ {
eglBindAPI(EGL_OPENGL_VG_API); eglBindAPI(EGL_OPENVG_API);
eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx); eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx);
vgDestroyImage(g_egl_vgimage); vgDestroyImage(g_egl_vgimage[i]);
}
} }
if (g_egl_ctx) if (g_egl_ctx)
@ -263,7 +284,7 @@ static void gfx_ctx_destroy(void)
if (g_eglimage_ctx) if (g_eglimage_ctx)
{ {
eglBindAPI(EGL_OPENGL_VG_API); eglBindAPI(EGL_OPENVG_API);
eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(g_egl_dpy, g_eglimage_ctx); eglDestroyContext(g_egl_dpy, g_eglimage_ctx);
} }
@ -276,11 +297,11 @@ static void gfx_ctx_destroy(void)
if (g_pbuff_surf) if (g_pbuff_surf)
{ {
eglBindAPI(EGL_OPENGL_VG_API); eglBindAPI(EGL_OPENVG_API);
eglDestroySurface(g_egl_dpy, g_pbuff_surf); eglDestroySurface(g_egl_dpy, g_pbuff_surf);
} }
eglBindAPI(EGL_OPENGL_VG_API); eglBindAPI(EGL_OPENVG_API);
eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
gfx_ctx_bind_api(g_api); gfx_ctx_bind_api(g_api);
eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@ -288,14 +309,18 @@ static void gfx_ctx_destroy(void)
} }
g_egl_ctx = NULL; g_egl_ctx = NULL;
g_eglimage_ctx = NULL g_eglimage_ctx = NULL;
g_egl_surf = NULL; g_egl_surf = NULL;
g_pbuff_surf = NULL; g_pbuff_surf = NULL;
g_egl_dpy = NULL; g_egl_dpy = NULL;
eglBuffer = NULL;
g_egl_vgimage = NULL;
g_config = 0; g_config = 0;
g_inited = false; g_inited = false;
for (unsigned i = 0; i < MAX_EGLIMAGE_TEXTURES; i++)
{
eglBuffer[i] = NULL;
g_egl_vgimage[i] = 0;
}
} }
static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data)
@ -315,22 +340,6 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return eglGetProcAddress(symbol); return eglGetProcAddress(symbol);
} }
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
{
g_api = api;
switch (api)
{
case GFX_CTX_OPENGL_API:
return eglBindAPI(EGL_OPENGL_API);
case GFX_CTX_OPENGL_ES_API:
return eglBindAPI(EGL_OPENGL_ES_API);
case GFX_CTX_OPENVG_API:
return eglBindAPI(EGL_OPENVG_API);
default:
return false;
}
}
static float gfx_ctx_translate_aspect(unsigned width, unsigned height) static float gfx_ctx_translate_aspect(unsigned width, unsigned height)
{ {
// check for SD televisions: they should always be 4:3. // check for SD televisions: they should always be 4:3.
@ -355,12 +364,12 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
unsigned res = video->input_scale * RARCH_SCALE_BASE; g_egl_res = video->input_scale * RARCH_SCALE_BASE;
EGLint pbufsurface_list[] = EGLint pbufsurface_list[] =
{ {
EGL_WIDTH, res, EGL_WIDTH, g_egl_res,
EGL_HEIGHT, res, EGL_HEIGHT, g_egl_res,
EGL_NONE EGL_NONE
}; };
@ -381,6 +390,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
goto fail; goto fail;
} }
// test to make sure we can switch context
result = eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx); result = eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx);
if (result == EGL_FALSE) if (result == EGL_FALSE)
{ {
@ -388,14 +398,12 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
goto fail; goto fail;
} }
g_egl_vgimage = vgCreateImage(VG_sXRGB_8888, res, res, video->smooth ? VG_IMAGE_QUALITY_BETTER : VG_IMAGE_QUALITY_NONANTIALIASED);
eglBuffer = peglCreateImageKHR(g_egl_dpy, g_eglimage_ctx, EGL_VG_PARENT_IMAGE_KHR, (EGLClientBuffer)g_egl_vgimage, NULL);
gfx_ctx_bind_api(g_api); gfx_ctx_bind_api(g_api);
eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx); eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx);
g_smooth = video->smooth;
return true; return true;
fail: fail:
if (g_pbuff_surf != EGL_NO_SURFACE) if (g_pbuff_surf != EGL_NO_SURFACE)
{ {
@ -415,27 +423,33 @@ fail:
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
static bool first = true; bool ret = false;
if (index >= MAX_EGLIMAGE_TEXTURES)
{
*image_handle = NULL;
return false;
}
eglBindAPI(EGL_OPENVG_API); eglBindAPI(EGL_OPENVG_API);
eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx); eglMakeCurrent(g_egl_dpy, g_pbuff_surf, g_pbuff_surf, g_eglimage_ctx);
vgImageSubData(g_egl_vgimage, frame, pitch, (rgb32 ? VG_sXRGB_8888 : VG_sARGB_1555), 0, 0, width, height); if (!eglBuffer[index] || !g_egl_vgimage[index])
*image_handle = eglBuffer; {
g_egl_vgimage[index] = vgCreateImage(VG_sXRGB_8888, g_egl_res, g_egl_res, g_smooth ? VG_IMAGE_QUALITY_BETTER : VG_IMAGE_QUALITY_NONANTIALIASED);
eglBuffer[index] = peglCreateImageKHR(g_egl_dpy, g_eglimage_ctx, EGL_VG_PARENT_IMAGE_KHR, (EGLClientBuffer)g_egl_vgimage[index], NULL);
ret = true;
}
vgImageSubData(g_egl_vgimage[index], frame, pitch, (rgb32 ? VG_sXRGB_8888 : VG_sARGB_1555), 0, 0, width, height);
*image_handle = eglBuffer[index];
gfx_ctx_bind_api(g_api); gfx_ctx_bind_api(g_api);
eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx); eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx);
if (first) return ret;
{
first = false;
return true;
}
else
{
return false;
}
} }
const gfx_ctx_driver_t gfx_ctx_videocore = { const gfx_ctx_driver_t gfx_ctx_videocore = {

View File

@ -409,7 +409,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -530,7 +530,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -508,7 +508,7 @@ static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video)
return false; return false;
} }
static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle) static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle)
{ {
return false; return false;
} }

View File

@ -23,6 +23,8 @@
#include "../config.h" #include "../config.h"
#endif #endif
#define MAX_EGLIMAGE_TEXTURES 32
enum gfx_ctx_api enum gfx_ctx_api
{ {
GFX_CTX_OPENGL_API, GFX_CTX_OPENGL_API,
@ -80,12 +82,12 @@ typedef struct gfx_ctx_driver
// Wraps whatever gl_proc_address() there is. // Wraps whatever gl_proc_address() there is.
gfx_ctx_proc_t (*get_proc_address)(const char*); gfx_ctx_proc_t (*get_proc_address)(const char*);
// Returns true if this context supports EGL Image buffers for screen drawing and was initalized correctly. // Returns true if this context supports EGLImage buffers for screen drawing and was initalized correctly.
bool (*init_egl_image_buffer)(const video_info_t*); bool (*init_egl_image_buffer)(const video_info_t*);
// Writes the frame to the EGL Image and sets image_handle to it. Returns true if a new image handle is created. // Writes the frame to the EGLImage and sets image_handle to it. Returns true if a new image handle is created.
// Always returns true the first time it's called. The graphics core must handle a change in the handle correctly. // Always returns true the first time it's called for a new index. The graphics core must handle a change in the handle correctly.
bool (*write_egl_image)(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, void **image_handle); bool (*write_egl_image)(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle);
// Human readable string. // Human readable string.
const char *ident; const char *ident;

View File

@ -94,6 +94,17 @@ const GLfloat *default_vertex_ptr = vertexes_flipped;
memcpy(&(pgl##SYM), &sym, sizeof(sym)); \ memcpy(&(pgl##SYM), &sym, sizeof(sym)); \
} }
#ifdef HAVE_EGL
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC pglEGLImageTargetTexture2DOES = NULL;
static bool load_eglimage_proc(gl_t *gl)
{
LOAD_GL_SYM(EGLImageTargetTexture2DOES);
return pglEGLImageTargetTexture2DOES;
}
#endif
#ifdef HAVE_FBO #ifdef HAVE_FBO
#if defined(_WIN32) && !defined(RARCH_CONSOLE) #if defined(_WIN32) && !defined(RARCH_CONSOLE)
static PFNGLGENFRAMEBUFFERSPROC pglGenFramebuffers = NULL; static PFNGLGENFRAMEBUFFERSPROC pglGenFramebuffers = NULL;
@ -925,7 +936,21 @@ static void gl_init_textures(gl_t *gl)
#else #else
static inline void gl_copy_frame(gl_t *gl, const void *frame, unsigned width, unsigned height, unsigned pitch) static inline void gl_copy_frame(gl_t *gl, const void *frame, unsigned width, unsigned height, unsigned pitch)
{ {
if (gl->base_size == 2) // ARGB1555 => ARGB8888, SIMD-style :D #ifdef HAVE_EGL
if (gl->egl_images)
{
EGLImageKHR img = 0;
bool new_egl = gl->ctx_driver->write_egl_image(frame, width, height, pitch, (gl->base_size == 4), gl->tex_index, &img);
rarch_assert(img != EGL_NO_IMAGE_KHR);
if (new_egl)
{
pglEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)img);
}
}
else
#endif
if (gl->base_size == 2) // ARGB1555 => ARGB8888, SIMD-style :D
{ {
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t))); // Always use 32-bit textures. glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t))); // Always use 32-bit textures.
gl_convert_frame_rgb15_32(gl, gl->conv_buffer, frame, width, height, pitch); gl_convert_frame_rgb15_32(gl, gl->conv_buffer, frame, width, height, pitch);
@ -1024,6 +1049,9 @@ static void gl_render_menu(gl_t *gl)
static bool gl_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg) static bool gl_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{ {
RARCH_PERFORMANCE_INIT(frame_run);
RARCH_PERFORMANCE_START(frame_run);
gl_t *gl = (gl_t*)data; gl_t *gl = (gl_t*)data;
gl_shader_use(1); gl_shader_use(1);
@ -1095,6 +1123,9 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
gl->ctx_driver->update_window_title(false); gl->ctx_driver->update_window_title(false);
#endif #endif
RARCH_PERFORMANCE_STOP(frame_run);
RARCH_PERFORMANCE_LOG("gl_frame", frame_run);
#ifdef RARCH_CONSOLE #ifdef RARCH_CONSOLE
if (!gl->block_swap) if (!gl->block_swap)
#endif #endif
@ -1374,6 +1405,10 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
return NULL; return NULL;
} }
#ifdef HAVE_EGL
gl->egl_images = load_eglimage_proc(gl) && gl->ctx_driver->init_egl_image_buffer(video);
#endif
return gl; return gl;
} }

View File

@ -28,6 +28,11 @@
#include <string.h> #include <string.h>
#ifdef HAVE_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#endif
#if defined(__APPLE__) #if defined(__APPLE__)
#include <OpenGL/gl.h> #include <OpenGL/gl.h>
#include <OpenGL/glext.h> #include <OpenGL/glext.h>
@ -36,8 +41,6 @@
#include <PSGL/psglu.h> #include <PSGL/psglu.h>
#include <GLES/glext.h> #include <GLES/glext.h>
#elif defined(HAVE_OPENGL_MODERN) #elif defined(HAVE_OPENGL_MODERN)
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GL3/gl3.h> #include <GL3/gl3.h>
#include <GL3/gl3ext.h> #include <GL3/gl3ext.h>
#elif defined(HAVE_OPENGLES2) #elif defined(HAVE_OPENGLES2)
@ -233,6 +236,10 @@ typedef struct gl
bool menu_render; bool menu_render;
GLuint menu_texture_id; GLuint menu_texture_id;
#endif #endif
#ifdef HAVE_EGL
bool egl_images;
#endif
} gl_t; } gl_t;
// Windows ... <_< // Windows ... <_<

View File

@ -350,17 +350,16 @@ static void vg_copy_frame(void *data, const void *frame, unsigned width, unsigne
if (vg->mEglImageBuf) if (vg->mEglImageBuf)
{ {
EGLImageKHR img = 0; EGLImageKHR img = 0;
bool new_egl = vg->driver->write_egl_image(frame, width, height, pitch, (vg->mTexType == VG_sXRGB_8888), &img); bool new_egl = vg->driver->write_egl_image(frame, width, height, pitch, (vg->mTexType == VG_sXRGB_8888), 0, &img);
rarch_assert(img != EGL_NO_IMAGE_KHR); rarch_assert(img != EGL_NO_IMAGE_KHR);
if (new_egl) if (new_egl)
{ {
vgDestroyImage(vg->mImage); vgDestroyImage(vg->mImage);
RARCH_LOG("[VG] %08x\n", (unsigned) img);
vg->mImage = pvgCreateEGLImageTargetKHR((VGeglImageKHR) img); vg->mImage = pvgCreateEGLImageTargetKHR((VGeglImageKHR) img);
if (!vg->mImage) if (!vg->mImage)
{ {
RARCH_ERR("[VG] Error creating image: %08x\n", vgGetError()); RARCH_ERR("[VG:EGLImage] Error creating image: %08x\n", vgGetError());
exit(2); exit(2);
} }
vg->last_egl_image = img; vg->last_egl_image = img;