mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-28 05:38:24 +00:00
Merge pull request #654 from libretro/shared-context
libretro GL shared context support
This commit is contained in:
commit
41c3d55f5d
@ -284,6 +284,9 @@ static unsigned swap_interval = 1;
|
||||
// Threaded video. Will possibly increase performance significantly at cost of worse synchronization and latency.
|
||||
static const bool video_threaded = false;
|
||||
|
||||
// Set to true if HW render cores should get their private context.
|
||||
static const bool video_shared_context = false;
|
||||
|
||||
// Smooths picture
|
||||
static const bool video_smooth = true;
|
||||
|
||||
|
@ -183,6 +183,7 @@ struct settings
|
||||
bool gpu_screenshot;
|
||||
|
||||
bool allow_rotate;
|
||||
bool shared_context;
|
||||
} video;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
@ -47,6 +47,8 @@
|
||||
#include <sys/poll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static bool g_use_hw_ctx;
|
||||
static EGLContext g_egl_hw_ctx;
|
||||
static EGLContext g_egl_ctx;
|
||||
static EGLSurface g_egl_surf;
|
||||
static EGLDisplay g_egl_dpy;
|
||||
@ -593,9 +595,19 @@ static bool gfx_ctx_set_video_mode(void *data,
|
||||
goto error;
|
||||
|
||||
g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, (g_api == GFX_CTX_OPENGL_ES_API) ? gles_context_attribs : NULL);
|
||||
if (!g_egl_ctx)
|
||||
if (g_egl_ctx == EGL_NO_CONTEXT)
|
||||
goto error;
|
||||
|
||||
if (g_use_hw_ctx)
|
||||
{
|
||||
g_egl_hw_ctx = eglCreateContext(g_egl_dpy, g_config, g_egl_ctx,
|
||||
(g_api == GFX_CTX_OPENGL_ES_API) ? gles_context_attribs : NULL);
|
||||
RARCH_LOG("[KMS/EGL]: Created shared context: %p.\n", (void*)g_egl_hw_ctx);
|
||||
|
||||
if (g_egl_hw_ctx == EGL_NO_CONTEXT)
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, (EGLNativeWindowType)g_gbm_surface, NULL);
|
||||
if (!g_egl_surf)
|
||||
goto error;
|
||||
@ -636,6 +648,9 @@ void gfx_ctx_destroy(void *data)
|
||||
eglDestroyContext(g_egl_dpy, g_egl_ctx);
|
||||
}
|
||||
|
||||
if (g_egl_hw_ctx)
|
||||
eglDestroyContext(g_egl_dpy, g_egl_hw_ctx);
|
||||
|
||||
if (g_egl_surf)
|
||||
eglDestroySurface(g_egl_dpy, g_egl_surf);
|
||||
eglTerminate(g_egl_dpy);
|
||||
@ -717,6 +732,14 @@ static bool gfx_ctx_bind_api(void *data, enum gfx_ctx_api api, unsigned major, u
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_ctx_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
(void)data;
|
||||
g_use_hw_ctx = enable;
|
||||
if (g_egl_dpy)
|
||||
eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, enable ? g_egl_hw_ctx : g_egl_ctx);
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_drm_egl = {
|
||||
gfx_ctx_init,
|
||||
gfx_ctx_destroy,
|
||||
@ -736,5 +759,6 @@ const gfx_ctx_driver_t gfx_ctx_drm_egl = {
|
||||
NULL,
|
||||
NULL,
|
||||
"kms-egl",
|
||||
gfx_ctx_bind_hw_render,
|
||||
};
|
||||
|
||||
|
@ -40,7 +40,8 @@ static unsigned g_screen;
|
||||
static XIM g_xim;
|
||||
static XIC g_xic;
|
||||
|
||||
static GLXContext g_ctx;
|
||||
static bool g_use_hw_ctx;
|
||||
static GLXContext g_ctx, g_hw_ctx;
|
||||
static GLXFBConfig g_fbc;
|
||||
static unsigned g_major;
|
||||
static unsigned g_minor;
|
||||
@ -413,9 +414,24 @@ static bool gfx_ctx_set_video_mode(void *data,
|
||||
|
||||
*aptr = None;
|
||||
g_ctx = glx_create_context_attribs(g_dpy, g_fbc, NULL, True, attribs);
|
||||
if (g_use_hw_ctx)
|
||||
{
|
||||
RARCH_LOG("[GLX]: Creating shared HW context.\n");
|
||||
g_hw_ctx = glx_create_context_attribs(g_dpy, g_fbc, g_ctx, True, attribs);
|
||||
if (!g_hw_ctx)
|
||||
RARCH_ERR("[GLX]: Failed to create new shared context.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, 0, True);
|
||||
if (g_use_hw_ctx)
|
||||
{
|
||||
g_hw_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, g_ctx, True);
|
||||
if (!g_hw_ctx)
|
||||
RARCH_ERR("[GLX]: Failed to create new shared context.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_ctx)
|
||||
{
|
||||
@ -500,8 +516,11 @@ static void gfx_ctx_destroy(void *data)
|
||||
glXMakeContextCurrent(g_dpy, None, None, NULL);
|
||||
if (!driver.video_cache_context)
|
||||
{
|
||||
if (g_hw_ctx)
|
||||
glXDestroyContext(g_dpy, g_hw_ctx);
|
||||
glXDestroyContext(g_dpy, g_ctx);
|
||||
g_ctx = NULL;
|
||||
g_hw_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,6 +615,18 @@ static void gfx_ctx_show_mouse(void *data, bool state)
|
||||
x11_show_mouse(g_dpy, g_win, state);
|
||||
}
|
||||
|
||||
static void gfx_ctx_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
(void)data;
|
||||
g_use_hw_ctx = enable;
|
||||
|
||||
if (g_dpy)
|
||||
{
|
||||
//RARCH_LOG("[GLX]: Binding context (%s): %p\n", enable ? "RetroArch" : "HW render", enable ? (void*)g_hw_ctx : (void*)g_ctx);
|
||||
glXMakeContextCurrent(g_dpy, g_glx_win, g_glx_win, enable ? g_hw_ctx : g_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_glx = {
|
||||
gfx_ctx_init,
|
||||
gfx_ctx_destroy,
|
||||
@ -617,5 +648,7 @@ const gfx_ctx_driver_t gfx_ctx_glx = {
|
||||
#endif
|
||||
gfx_ctx_show_mouse,
|
||||
"glx",
|
||||
|
||||
gfx_ctx_bind_hw_render,
|
||||
};
|
||||
|
||||
|
@ -33,8 +33,10 @@
|
||||
#define IDI_ICON 1
|
||||
#define MAX_MONITORS 9
|
||||
|
||||
static bool g_use_hw_ctx;
|
||||
static HWND g_hwnd;
|
||||
static HGLRC g_hrc;
|
||||
static HGLRC g_hw_hrc;
|
||||
static HDC g_hdc;
|
||||
static HMONITOR g_last_hm;
|
||||
static HMONITOR g_all_hms[MAX_MONITORS];
|
||||
@ -82,8 +84,31 @@ static void create_gl_context(HWND hwnd)
|
||||
g_hdc = GetDC(hwnd);
|
||||
setup_pixel_format(g_hdc);
|
||||
|
||||
#ifdef GL_DEBUG
|
||||
bool debug = true;
|
||||
#else
|
||||
bool debug = g_extern.system.hw_render_callback.debug_context;
|
||||
#endif
|
||||
bool core_context = (g_major * 1000 + g_minor) >= 3001;
|
||||
|
||||
if (!g_hrc)
|
||||
{
|
||||
g_hrc = wglCreateContext(g_hdc);
|
||||
if (g_hrc && !core_context && !debug) // We'll create shared context later if not.
|
||||
{
|
||||
g_hw_hrc = wglCreateContext(g_hdc);
|
||||
if (g_hw_hrc)
|
||||
{
|
||||
if (!wglShareLists(g_hrc, g_hw_hrc))
|
||||
{
|
||||
RARCH_LOG("[WGL]: Failed to share contexts.\n");
|
||||
g_quit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
g_quit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("[WGL]: Using cached GL context.\n");
|
||||
@ -103,14 +128,6 @@ static void create_gl_context(HWND hwnd)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef GL_DEBUG
|
||||
bool debug = true;
|
||||
#else
|
||||
bool debug = g_extern.system.hw_render_callback.debug_context;
|
||||
#endif
|
||||
|
||||
bool core_context = (g_major * 1000 + g_minor) >= 3001;
|
||||
|
||||
if (core_context || debug)
|
||||
{
|
||||
#ifndef WGL_CONTEXT_MAJOR_VERSION_ARB
|
||||
@ -169,6 +186,16 @@ static void create_gl_context(HWND hwnd)
|
||||
}
|
||||
else
|
||||
RARCH_ERR("[WGL]: Failed to create core context. Falling back to legacy context.\n");
|
||||
|
||||
if (g_use_hw_ctx)
|
||||
{
|
||||
g_hw_hrc = pcreate_context(g_hdc, context, attribs);
|
||||
if (!g_hw_hrc)
|
||||
{
|
||||
RARCH_ERR("[WGL]: Failed to create shared context.\n");
|
||||
g_quit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
RARCH_ERR("[WGL]: wglCreateContextAttribsARB not supported.\n");
|
||||
@ -489,8 +516,11 @@ static void gfx_ctx_destroy(void *data)
|
||||
|
||||
if (!driver.video_cache_context)
|
||||
{
|
||||
if (g_hw_hrc)
|
||||
wglDeleteContext(g_hw_hrc);
|
||||
wglDeleteContext(g_hrc);
|
||||
g_hrc = NULL;
|
||||
g_hw_hrc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,6 +589,13 @@ static void gfx_ctx_show_mouse(void *data, bool state)
|
||||
show_cursor(state);
|
||||
}
|
||||
|
||||
static void gfx_ctx_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
g_use_hw_ctx = enable;
|
||||
if (g_hdc)
|
||||
wglMakeCurrent(g_hdc, enable ? g_hw_hrc : g_hrc);
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_wgl = {
|
||||
gfx_ctx_init,
|
||||
gfx_ctx_destroy,
|
||||
@ -576,5 +613,6 @@ const gfx_ctx_driver_t gfx_ctx_wgl = {
|
||||
gfx_ctx_get_proc_address,
|
||||
gfx_ctx_show_mouse,
|
||||
"wgl",
|
||||
gfx_ctx_bind_hw_render,
|
||||
};
|
||||
|
||||
|
@ -41,6 +41,8 @@ static unsigned g_screen;
|
||||
static XIM g_xim;
|
||||
static XIC g_xic;
|
||||
|
||||
static bool g_use_hw_ctx;
|
||||
static EGLContext g_egl_hw_ctx;
|
||||
static EGLContext g_egl_ctx;
|
||||
static EGLSurface g_egl_surf;
|
||||
static EGLDisplay g_egl_dpy;
|
||||
@ -422,10 +424,19 @@ static bool gfx_ctx_set_video_mode(void *data,
|
||||
(g_api == GFX_CTX_OPENGL_ES_API) ? egl_ctx_gles_attribs : NULL);
|
||||
|
||||
RARCH_LOG("[X/EGL]: Created context: %p.\n", (void*)g_egl_ctx);
|
||||
|
||||
if (!g_egl_ctx)
|
||||
if (g_egl_ctx == EGL_NO_CONTEXT)
|
||||
goto error;
|
||||
|
||||
if (g_use_hw_ctx)
|
||||
{
|
||||
g_egl_hw_ctx = eglCreateContext(g_egl_dpy, g_config, g_egl_ctx,
|
||||
(g_api == GFX_CTX_OPENGL_ES_API) ? egl_ctx_gles_attribs : NULL);
|
||||
RARCH_LOG("[X/EGL]: Created shared context: %p.\n", (void*)g_egl_hw_ctx);
|
||||
|
||||
if (g_egl_hw_ctx == EGL_NO_CONTEXT)
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, (EGLNativeWindowType)g_win, NULL);
|
||||
if (!g_egl_surf)
|
||||
goto error;
|
||||
@ -513,6 +524,9 @@ static void gfx_ctx_destroy(void *data)
|
||||
eglDestroyContext(g_egl_dpy, g_egl_ctx);
|
||||
}
|
||||
|
||||
if (g_egl_hw_ctx)
|
||||
eglDestroyContext(g_egl_dpy, g_egl_hw_ctx);
|
||||
|
||||
if (g_egl_surf)
|
||||
eglDestroySurface(g_egl_dpy, g_egl_surf);
|
||||
eglTerminate(g_egl_dpy);
|
||||
@ -622,6 +636,14 @@ static void gfx_ctx_show_mouse(void *data, bool state)
|
||||
x11_show_mouse(g_dpy, g_win, state);
|
||||
}
|
||||
|
||||
static void gfx_ctx_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
(void)data;
|
||||
g_use_hw_ctx = enable;
|
||||
if (g_egl_dpy)
|
||||
eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, enable ? g_egl_hw_ctx : g_egl_ctx);
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_x_egl = {
|
||||
gfx_ctx_init,
|
||||
gfx_ctx_destroy,
|
||||
@ -641,5 +663,6 @@ const gfx_ctx_driver_t gfx_ctx_x_egl = {
|
||||
NULL,
|
||||
gfx_ctx_show_mouse,
|
||||
"x-egl",
|
||||
gfx_ctx_bind_hw_render,
|
||||
};
|
||||
|
||||
|
@ -1405,7 +1405,7 @@ static const gfx_ctx_driver_t *d3d_get_context(void)
|
||||
major = 9;
|
||||
#endif
|
||||
minor = 0;
|
||||
return gfx_ctx_init_first(driver.video_data, api, major, minor);
|
||||
return gfx_ctx_init_first(driver.video_data, api, major, minor, false);
|
||||
}
|
||||
|
||||
static void *d3d_init(const video_info_t *info, const input_driver_t **input,
|
||||
|
@ -71,13 +71,19 @@ const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t *gfx_ctx_init_first(void *data, enum gfx_ctx_api api, unsigned major, unsigned minor)
|
||||
const gfx_ctx_driver_t *gfx_ctx_init_first(void *data, enum gfx_ctx_api api, unsigned major, unsigned minor, bool hw_render_ctx)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; gfx_ctx_drivers[i]; i++)
|
||||
{
|
||||
if (gfx_ctx_drivers[i]->bind_api(data, api, major, minor))
|
||||
{
|
||||
if (gfx_ctx_drivers[i]->bind_hw_render)
|
||||
{
|
||||
gfx_ctx_drivers[i]->bind_hw_render(data,
|
||||
g_settings.video.shared_context && hw_render_ctx);
|
||||
}
|
||||
|
||||
if (gfx_ctx_drivers[i]->init(data))
|
||||
return gfx_ctx_drivers[i];
|
||||
}
|
||||
|
@ -103,6 +103,9 @@ typedef struct gfx_ctx_driver
|
||||
|
||||
// Human readable string.
|
||||
const char *ident;
|
||||
|
||||
// Optional. Binds HW-render offscreen context.
|
||||
void (*bind_hw_render)(void *data, bool enable);
|
||||
} gfx_ctx_driver_t;
|
||||
|
||||
extern const gfx_ctx_driver_t gfx_ctx_sdl_gl;
|
||||
@ -120,7 +123,7 @@ extern const gfx_ctx_driver_t gfx_ctx_emscripten;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_null;
|
||||
|
||||
const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize.
|
||||
const gfx_ctx_driver_t *gfx_ctx_init_first(void *data, enum gfx_ctx_api api, unsigned major, unsigned minor); // Finds first suitable driver and initializes.
|
||||
const gfx_ctx_driver_t *gfx_ctx_init_first(void *data, enum gfx_ctx_api api, unsigned major, unsigned minor, bool hw_render_ctx); // Finds first suitable driver and initializes.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
107
gfx/gl.c
107
gfx/gl.c
@ -631,15 +631,23 @@ void gl_init_fbo(void *data, unsigned width, unsigned height)
|
||||
#ifndef HAVE_GCMGL
|
||||
static void gl_deinit_hw_render(gl_t *gl)
|
||||
{
|
||||
context_bind_hw_render(gl, true);
|
||||
|
||||
if (gl->hw_render_fbo_init)
|
||||
glDeleteFramebuffers(gl->textures, gl->hw_render_fbo);
|
||||
if (gl->hw_render_depth_init)
|
||||
glDeleteRenderbuffers(gl->textures, gl->hw_render_depth);
|
||||
gl->hw_render_fbo_init = false;
|
||||
|
||||
context_bind_hw_render(gl, false);
|
||||
}
|
||||
|
||||
static bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
|
||||
{
|
||||
// We can only share texture objects through contexts.
|
||||
// FBOs are "abstract" objects and are not shared.
|
||||
context_bind_hw_render(gl, true);
|
||||
|
||||
unsigned i;
|
||||
RARCH_LOG("[GL]: Initializing HW render (%u x %u).\n", width, height);
|
||||
GLint max_fbo_size = 0;
|
||||
@ -720,6 +728,8 @@ static bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
|
||||
|
||||
gl_bind_backbuffer();
|
||||
gl->hw_render_fbo_init = true;
|
||||
|
||||
context_bind_hw_render(gl, false);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@ -1310,7 +1320,7 @@ static inline void gl_set_shader_viewport(void *data, unsigned shader)
|
||||
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
static void gl_pbo_async_readback(void *data)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
@ -1327,9 +1337,15 @@ static void gl_pbo_async_readback(void *data)
|
||||
RARCH_PERFORMANCE_INIT(async_readback);
|
||||
RARCH_PERFORMANCE_START(async_readback);
|
||||
glReadBuffer(GL_BACK);
|
||||
#ifdef HAVE_OPENGLES3
|
||||
glReadPixels(gl->vp.x, gl->vp.y,
|
||||
gl->vp.width, gl->vp.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
#else
|
||||
glReadPixels(gl->vp.x, gl->vp.y,
|
||||
gl->vp.width, gl->vp.height,
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
||||
#endif
|
||||
RARCH_PERFORMANCE_STOP(async_readback);
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
@ -1385,6 +1401,7 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
RARCH_PERFORMANCE_START(frame_run);
|
||||
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
#ifndef HAVE_OPENGLES
|
||||
if (gl->core_context)
|
||||
@ -1539,7 +1556,7 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
gl->vp.width, gl->vp.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, gl->readback_buffer_screenshot);
|
||||
}
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
else if (gl->pbo_readback_enable)
|
||||
gl_pbo_async_readback(gl);
|
||||
#endif
|
||||
@ -1574,6 +1591,8 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
|
||||
context_bind_hw_render(gl, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1599,6 +1618,7 @@ static void gl_free(void *data)
|
||||
#endif
|
||||
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
#ifdef HAVE_GL_SYNC
|
||||
if (gl->have_sync)
|
||||
@ -1639,7 +1659,7 @@ static void gl_free(void *data)
|
||||
|
||||
scaler_ctx_gen_reset(&gl->scaler);
|
||||
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
if (gl->pbo_readback_enable)
|
||||
{
|
||||
glDeleteBuffers(4, gl->pbo_readback);
|
||||
@ -1674,7 +1694,9 @@ static void gl_set_nonblock_state(void *data, bool state)
|
||||
RARCH_LOG("GL VSync => %s\n", state ? "off" : "on");
|
||||
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
context_swap_interval_func(gl, state ? 0 : g_settings.video.swap_interval);
|
||||
context_bind_hw_render(gl, true);
|
||||
}
|
||||
|
||||
static bool resolve_extensions(gl_t *gl)
|
||||
@ -1838,7 +1860,7 @@ static inline void gl_reinit_textures(void *data, const video_info_t *video)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
static void gl_init_pbo_readback(void *data)
|
||||
{
|
||||
unsigned i;
|
||||
@ -1848,17 +1870,18 @@ static void gl_init_pbo_readback(void *data)
|
||||
if (!gl->pbo_readback_enable)
|
||||
return;
|
||||
|
||||
RARCH_LOG("Async PBO readback enabled.\n");
|
||||
RARCH_LOG("[GL]: Async PBO readback enabled.\n");
|
||||
|
||||
glGenBuffers(4, gl->pbo_readback);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, gl->pbo_readback[i]);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, gl->vp.width * gl->vp.height * sizeof(uint32_t),
|
||||
NULL, GL_STREAM_COPY);
|
||||
NULL, GL_STREAM_READ);
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
#ifndef HAVE_OPENGLES3
|
||||
struct scaler_ctx *scaler = &gl->pbo_readback_scaler;
|
||||
scaler->in_width = gl->vp.width;
|
||||
scaler->in_height = gl->vp.height;
|
||||
@ -1876,6 +1899,7 @@ static void gl_init_pbo_readback(void *data)
|
||||
RARCH_ERR("Failed to init pixel conversion for PBO.\n");
|
||||
glDeleteBuffers(4, gl->pbo_readback);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1900,6 +1924,8 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl)
|
||||
const char *api_name = "OpenGL";
|
||||
#endif
|
||||
|
||||
gl->shared_context_use = g_settings.video.shared_context && cb->context_type != RETRO_HW_CONTEXT_NONE;
|
||||
|
||||
if (*g_settings.video.gl_context)
|
||||
{
|
||||
const gfx_ctx_driver_t *ctx = gfx_ctx_find_driver(g_settings.video.gl_context);
|
||||
@ -1911,6 +1937,10 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Enables or disables offscreen HW context.
|
||||
if (ctx->bind_hw_render)
|
||||
ctx->bind_hw_render(gl, gl->shared_context_use);
|
||||
|
||||
if (!ctx->init(gl))
|
||||
{
|
||||
RARCH_ERR("Failed to init GL context: %s.\n", ctx->ident);
|
||||
@ -1926,7 +1956,7 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl)
|
||||
return ctx;
|
||||
}
|
||||
else
|
||||
return gfx_ctx_init_first(gl, api, major, minor);
|
||||
return gfx_ctx_init_first(gl, api, major, minor, gl->shared_context_use);
|
||||
}
|
||||
|
||||
#ifdef GL_DEBUG
|
||||
@ -2113,7 +2143,14 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL_CORE;
|
||||
#endif
|
||||
if (gl->hw_render_use)
|
||||
{
|
||||
gl->textures = 1; // All on GPU, no need to excessively create textures.
|
||||
#ifdef GL_DEBUG
|
||||
context_set_hw_render(true);
|
||||
gl_begin_debug(gl);
|
||||
context_set_hw_render(false);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
gl->white_color_ptr = white_color;
|
||||
|
||||
@ -2213,7 +2250,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
gl->win_width, gl->win_height);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
gl_init_pbo_readback(gl);
|
||||
#endif
|
||||
|
||||
@ -2224,6 +2261,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context_bind_hw_render(gl, true);
|
||||
return gl;
|
||||
}
|
||||
|
||||
@ -2254,6 +2292,7 @@ static void gl_update_tex_filter_frame(gl_t *gl)
|
||||
{
|
||||
unsigned i;
|
||||
bool smooth = false;
|
||||
context_bind_hw_render(gl, false);
|
||||
if (!gl_shader_filter_type(gl, 1, &smooth))
|
||||
smooth = g_settings.video.smooth;
|
||||
GLenum wrap_mode = gl_wrap_type_to_enum(gl_shader_wrap_type(gl, 1));
|
||||
@ -2278,12 +2317,14 @@ static void gl_update_tex_filter_frame(gl_t *gl)
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
context_bind_hw_render(gl, true);
|
||||
}
|
||||
|
||||
#if defined(HAVE_GLSL) || defined(HAVE_CG)
|
||||
static bool gl_set_shader(void *data, enum rarch_shader_type type, const char *path)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
if (type == RARCH_SHADER_NONE)
|
||||
return false;
|
||||
@ -2312,6 +2353,7 @@ static bool gl_set_shader(void *data, enum rarch_shader_type type, const char *p
|
||||
if (!gl->shader)
|
||||
{
|
||||
RARCH_ERR("[GL]: Cannot find shader core for path: %s.\n", path);
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2326,6 +2368,7 @@ static bool gl_set_shader(void *data, enum rarch_shader_type type, const char *p
|
||||
bool ret = gl->shader->init(gl, NULL);
|
||||
if (!ret)
|
||||
gl->shader = NULL;
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2366,6 +2409,7 @@ static bool gl_set_shader(void *data, enum rarch_shader_type type, const char *p
|
||||
// Apparently need to set viewport for passes when we aren't using FBOs.
|
||||
gl_set_shader_viewport(gl, 0);
|
||||
gl_set_shader_viewport(gl, 1);
|
||||
context_bind_hw_render(gl, true);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@ -2387,25 +2431,56 @@ static void gl_viewport_info(void *data, struct rarch_viewport *vp)
|
||||
static bool gl_read_viewport(void *data, uint8_t *buffer)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
RARCH_PERFORMANCE_INIT(read_viewport);
|
||||
RARCH_PERFORMANCE_START(read_viewport);
|
||||
|
||||
#if defined(HAVE_FFMPEG) && !defined(HAVE_OPENGLES)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
if (gl->pbo_readback_enable)
|
||||
{
|
||||
if (!gl->pbo_readback_valid) // We haven't buffered up enough frames yet, come back later.
|
||||
{
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, gl->pbo_readback[gl->pbo_readback_index]);
|
||||
#ifdef HAVE_OPENGLES3
|
||||
// Slower path, but should work on all implementations at least.
|
||||
unsigned num_pixels = gl->vp.width * gl->vp.height;
|
||||
const uint8_t *ptr = (const uint8_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER,
|
||||
0, num_pixels * sizeof(uint32_t), GL_MAP_READ_BIT);
|
||||
if (ptr)
|
||||
{
|
||||
unsigned x, y;
|
||||
for (y = 0; y < gl->vp.height; y++)
|
||||
{
|
||||
for (x = 0; x < gl->vp.width; x++, buffer += 3, ptr += 4)
|
||||
{
|
||||
buffer[0] = ptr[2]; // RGBA -> BGR.
|
||||
buffer[1] = ptr[1];
|
||||
buffer[2] = ptr[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_ERR("[GL]: Failed to map pixel unpack buffer.\n");
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
const void *ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
if (!ptr)
|
||||
{
|
||||
RARCH_ERR("Failed to map pixel unpack buffer.\n");
|
||||
RARCH_ERR("[GL]: Failed to map pixel unpack buffer.\n");
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
scaler_ctx_scale(&gl->pbo_readback_scaler, buffer, ptr);
|
||||
#endif
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
@ -2423,6 +2498,7 @@ static bool gl_read_viewport(void *data, uint8_t *buffer)
|
||||
if (!gl->readback_buffer_screenshot)
|
||||
{
|
||||
RARCH_PERFORMANCE_STOP(read_viewport);
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2443,6 +2519,7 @@ static bool gl_read_viewport(void *data, uint8_t *buffer)
|
||||
}
|
||||
|
||||
RARCH_PERFORMANCE_STOP(read_viewport);
|
||||
context_bind_hw_render(gl, true);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@ -2471,11 +2548,15 @@ static bool gl_overlay_load(void *data, const struct texture_image *images, unsi
|
||||
{
|
||||
unsigned i;
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
gl_free_overlay(gl);
|
||||
gl->overlay = (struct gl_overlay_data*)calloc(num_images, sizeof(*gl->overlay));
|
||||
if (!gl->overlay)
|
||||
{
|
||||
context_bind_hw_render(gl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
gl->overlays = num_images;
|
||||
|
||||
@ -2499,6 +2580,7 @@ static bool gl_overlay_load(void *data, const struct texture_image *images, unsi
|
||||
gl->overlay[i].alpha_mod = 1.0f;
|
||||
}
|
||||
|
||||
context_bind_hw_render(gl, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2658,6 +2740,7 @@ static void gl_set_texture_frame(void *data,
|
||||
float alpha)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
|
||||
if (!gl->rgui_texture)
|
||||
{
|
||||
@ -2692,6 +2775,7 @@ static void gl_set_texture_frame(void *data,
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
context_bind_hw_render(gl, true);
|
||||
}
|
||||
|
||||
static void gl_set_texture_enable(void *data, bool state, bool full_screen)
|
||||
@ -2711,10 +2795,12 @@ static void gl_apply_state_changes(void *data)
|
||||
static void gl_set_osd_msg(void *data, const char *msg, void *userdata)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
context_bind_hw_render(gl, false);
|
||||
font_params_t *params = (font_params_t*)userdata;
|
||||
|
||||
if (gl->font_ctx)
|
||||
gl->font_ctx->render_msg(gl, msg, params);
|
||||
context_bind_hw_render(gl, true);
|
||||
}
|
||||
|
||||
static void gl_show_mouse(void *data, bool state)
|
||||
@ -2782,4 +2868,3 @@ const video_driver_t video_gl = {
|
||||
gl_get_poke_interface,
|
||||
};
|
||||
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
#define context_swap_buffers_func(gl) gl->ctx_driver->swap_buffers(gl)
|
||||
#define context_swap_interval_func(gl, var) gl->ctx_driver->swap_interval(gl, var)
|
||||
#define context_has_focus_func(gl) gl->ctx_driver->has_focus(gl)
|
||||
#define context_bind_hw_render(gl, enable) if (gl->shared_context_use && gl->ctx_driver->bind_hw_render) gl->ctx_driver->bind_hw_render(gl, enable)
|
||||
#define context_check_window_func(gl, quit, resize, width, height, frame_count) \
|
||||
gl->ctx_driver->check_window(gl, quit, resize, width, height, frame_count)
|
||||
|
||||
@ -57,6 +58,10 @@
|
||||
gl->ctx_driver->write_egl_image(gl, frame, width, height, pitch, base_size, tex_index,img)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FFMPEG) && (!defined(HAVE_OPENGLES) || defined(HAVE_OPENGLES3))
|
||||
#define HAVE_GL_ASYNC_READBACK
|
||||
#endif
|
||||
|
||||
static inline bool gl_check_error(void)
|
||||
{
|
||||
int error = glGetError();
|
||||
@ -178,6 +183,7 @@ typedef struct gl
|
||||
bool has_fp_fbo;
|
||||
#endif
|
||||
bool hw_render_use;
|
||||
bool shared_context_use;
|
||||
|
||||
bool should_resize;
|
||||
bool quitting;
|
||||
@ -238,7 +244,7 @@ typedef struct gl
|
||||
bool overlay_full_screen;
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)
|
||||
#ifdef HAVE_GL_ASYNC_READBACK
|
||||
// PBOs used for asynchronous viewport readbacks.
|
||||
GLuint pbo_readback[4];
|
||||
bool pbo_readback_enable;
|
||||
|
2
gfx/vg.c
2
gfx/vg.c
@ -87,7 +87,7 @@ static void *vg_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
if (!vg)
|
||||
return NULL;
|
||||
|
||||
vg->driver = gfx_ctx_init_first(vg, GFX_CTX_OPENVG_API, 0, 0);
|
||||
vg->driver = gfx_ctx_init_first(vg, GFX_CTX_OPENVG_API, 0, 0, false);
|
||||
|
||||
if (!vg->driver)
|
||||
{
|
||||
|
@ -122,6 +122,10 @@
|
||||
# Use threaded video driver. Using this might improve performance at possible cost of latency and more video stuttering.
|
||||
# video_threaded = false
|
||||
|
||||
# Use a shared context for HW rendered libretro cores.
|
||||
# Avoids having to assume GL state changes inbetween frames.
|
||||
# video_shared_context = false
|
||||
|
||||
# Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders.
|
||||
# video_smooth = true
|
||||
|
||||
|
@ -252,6 +252,7 @@ void config_set_defaults(void)
|
||||
g_settings.video.black_frame_insertion = black_frame_insertion;
|
||||
g_settings.video.swap_interval = swap_interval;
|
||||
g_settings.video.threaded = video_threaded;
|
||||
g_settings.video.shared_context = video_shared_context;
|
||||
g_settings.video.smooth = video_smooth;
|
||||
g_settings.video.force_aspect = force_aspect;
|
||||
g_settings.video.scale_integer = scale_integer;
|
||||
@ -775,6 +776,7 @@ bool config_load_file(const char *path, bool set_defaults)
|
||||
g_settings.video.swap_interval = max(g_settings.video.swap_interval, 1);
|
||||
g_settings.video.swap_interval = min(g_settings.video.swap_interval, 4);
|
||||
CONFIG_GET_BOOL(video.threaded, "video_threaded");
|
||||
CONFIG_GET_BOOL(video.shared_context, "video_shared_context");
|
||||
CONFIG_GET_BOOL(video.smooth, "video_smooth");
|
||||
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
|
||||
CONFIG_GET_BOOL(video.scale_integer, "video_scale_integer");
|
||||
@ -1280,6 +1282,7 @@ bool config_save_file(const char *path)
|
||||
config_set_bool(conf, "video_scale_integer", g_settings.video.scale_integer);
|
||||
config_set_bool(conf, "video_smooth", g_settings.video.smooth);
|
||||
config_set_bool(conf, "video_threaded", g_settings.video.threaded);
|
||||
config_set_bool(conf, "video_shared_context", g_settings.video.shared_context);
|
||||
config_set_bool(conf, "video_fullscreen", g_settings.video.fullscreen);
|
||||
config_set_float(conf, "video_refresh_rate", g_settings.video.refresh_rate);
|
||||
config_set_int(conf, "video_monitor_index", g_settings.video.monitor_index);
|
||||
|
@ -299,7 +299,7 @@ static const gfx_ctx_driver_t *d3d_get_context(void *data)
|
||||
major = 9;
|
||||
#endif
|
||||
minor = 0;
|
||||
return gfx_ctx_init_first(d3d, api, major, minor);
|
||||
return gfx_ctx_init_first(d3d, api, major, minor, false);
|
||||
}
|
||||
|
||||
static bool d3d_init_base(void *data, const video_info_t *info)
|
||||
|
Loading…
Reference in New Issue
Block a user