From f6581127561eb513870d7ca07a11d8ca5b7ca241 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 15:37:00 +0200 Subject: [PATCH 1/8] Add experimental support for separate HW contexts. --- gfx/context/glx_ctx.c | 19 +++++++++++++++++-- gfx/gfx_context.h | 3 +++ gfx/gl.c | 43 ++++++++++++++++++++++++++++++++++++++++++- gfx/gl_common.h | 1 + 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/gfx/context/glx_ctx.c b/gfx/context/glx_ctx.c index 143631e1c2..cd2885d750 100644 --- a/gfx/context/glx_ctx.c +++ b/gfx/context/glx_ctx.c @@ -40,7 +40,7 @@ static unsigned g_screen; static XIM g_xim; static XIC g_xic; -static GLXContext g_ctx; +static GLXContext g_ctx, g_hw_ctx; static GLXFBConfig g_fbc; static unsigned g_major; static unsigned g_minor; @@ -413,11 +413,15 @@ static bool gfx_ctx_set_video_mode(void *data, *aptr = None; g_ctx = glx_create_context_attribs(g_dpy, g_fbc, NULL, True, attribs); + g_hw_ctx = glx_create_context_attribs(g_dpy, g_fbc, g_ctx, True, attribs); } else + { g_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, 0, True); + g_hw_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, g_ctx, True); + } - if (!g_ctx) + if (!g_ctx || !g_hw_ctx) { RARCH_ERR("[GLX]: Failed to create new context.\n"); goto error; @@ -500,8 +504,10 @@ static void gfx_ctx_destroy(void *data) glXMakeContextCurrent(g_dpy, None, None, NULL); if (!driver.video_cache_context) { + glXDestroyContext(g_dpy, g_hw_ctx); glXDestroyContext(g_dpy, g_ctx); g_ctx = NULL; + g_hw_ctx = NULL; } } @@ -596,6 +602,13 @@ 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; + 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 +630,7 @@ const gfx_ctx_driver_t gfx_ctx_glx = { #endif gfx_ctx_show_mouse, "glx", + + gfx_ctx_bind_hw_render, }; diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index 6f7e2b8026..90f173776c 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -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; diff --git a/gfx/gl.c b/gfx/gl.c index 910ef7a01d..6172155e7c 100644 --- a/gfx/gl.c +++ b/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 @@ -1385,6 +1395,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) @@ -1574,6 +1585,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 +1612,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) @@ -1674,7 +1688,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) @@ -2113,7 +2129,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; @@ -2224,6 +2247,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 +2278,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 +2303,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 +2339,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 +2354,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 +2395,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,6 +2417,7 @@ 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); @@ -2423,6 +2454,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 +2475,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 +2504,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 +2536,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 +2696,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 +2731,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 +2751,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 +2824,3 @@ const video_driver_t video_gl = { gl_get_poke_interface, }; - diff --git a/gfx/gl_common.h b/gfx/gl_common.h index d8e62f557d..fbd43788e0 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -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->hw_render_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) From 105872a42c30059500afd0f6d282d7d059fac76b Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 16:06:12 +0200 Subject: [PATCH 2/8] Add HW context support to X/EGL and KMS/EGL. --- gfx/context/drm_egl_ctx.c | 26 +++++++++++++++++++++++++- gfx/context/glx_ctx.c | 30 ++++++++++++++++++++++++------ gfx/context/xegl_ctx.c | 27 +++++++++++++++++++++++++-- gfx/d3d9/d3d.cpp | 2 +- gfx/gfx_context.c | 4 +++- gfx/gfx_context.h | 2 +- gfx/gl.c | 6 +++++- gfx/vg.c | 2 +- xdk/xdk_d3d.cpp | 2 +- 9 files changed, 86 insertions(+), 15 deletions(-) diff --git a/gfx/context/drm_egl_ctx.c b/gfx/context/drm_egl_ctx.c index e8b92099c8..18c9103157 100644 --- a/gfx/context/drm_egl_ctx.c +++ b/gfx/context/drm_egl_ctx.c @@ -47,6 +47,8 @@ #include #include +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, }; diff --git a/gfx/context/glx_ctx.c b/gfx/context/glx_ctx.c index cd2885d750..c52298173a 100644 --- a/gfx/context/glx_ctx.c +++ b/gfx/context/glx_ctx.c @@ -40,6 +40,7 @@ static unsigned g_screen; static XIM g_xim; static XIC g_xic; +static bool g_use_hw_ctx; static GLXContext g_ctx, g_hw_ctx; static GLXFBConfig g_fbc; static unsigned g_major; @@ -413,15 +414,26 @@ static bool gfx_ctx_set_video_mode(void *data, *aptr = None; g_ctx = glx_create_context_attribs(g_dpy, g_fbc, NULL, True, attribs); - g_hw_ctx = glx_create_context_attribs(g_dpy, g_fbc, g_ctx, 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); - g_hw_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, g_ctx, 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 || !g_hw_ctx) + if (!g_ctx) { RARCH_ERR("[GLX]: Failed to create new context.\n"); goto error; @@ -504,7 +516,8 @@ static void gfx_ctx_destroy(void *data) glXMakeContextCurrent(g_dpy, None, None, NULL); if (!driver.video_cache_context) { - glXDestroyContext(g_dpy, g_hw_ctx); + if (g_hw_ctx) + glXDestroyContext(g_dpy, g_hw_ctx); glXDestroyContext(g_dpy, g_ctx); g_ctx = NULL; g_hw_ctx = NULL; @@ -605,8 +618,13 @@ static void gfx_ctx_show_mouse(void *data, bool state) static void gfx_ctx_bind_hw_render(void *data, bool enable) { (void)data; - 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); + 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 = { diff --git a/gfx/context/xegl_ctx.c b/gfx/context/xegl_ctx.c index 1877754d53..ae1628f76d 100644 --- a/gfx/context/xegl_ctx.c +++ b/gfx/context/xegl_ctx.c @@ -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, }; diff --git a/gfx/d3d9/d3d.cpp b/gfx/d3d9/d3d.cpp index 405ac249cf..9e0c7afbab 100644 --- a/gfx/d3d9/d3d.cpp +++ b/gfx/d3d9/d3d.cpp @@ -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, diff --git a/gfx/gfx_context.c b/gfx/gfx_context.c index 065914a911..04576bf578 100644 --- a/gfx/gfx_context.c +++ b/gfx/gfx_context.c @@ -71,13 +71,15 @@ 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, hw_render_ctx); if (gfx_ctx_drivers[i]->init(data)) return gfx_ctx_drivers[i]; } diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index 90f173776c..80cc01d94f 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -123,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 } diff --git a/gfx/gl.c b/gfx/gl.c index 6172155e7c..df3ced4538 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1927,6 +1927,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, cb->context_type != RETRO_HW_CONTEXT_NONE); + if (!ctx->init(gl)) { RARCH_ERR("Failed to init GL context: %s.\n", ctx->ident); @@ -1942,7 +1946,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, cb->context_type != RETRO_HW_CONTEXT_NONE); } #ifdef GL_DEBUG diff --git a/gfx/vg.c b/gfx/vg.c index 706a0000e2..0ee6018d98 100644 --- a/gfx/vg.c +++ b/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) { diff --git a/xdk/xdk_d3d.cpp b/xdk/xdk_d3d.cpp index 6f0d24bbc7..f35ee7eb17 100644 --- a/xdk/xdk_d3d.cpp +++ b/xdk/xdk_d3d.cpp @@ -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) From 5bdd368192e7005a13e04c19127e12eb82f0f5de Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 16:21:37 +0200 Subject: [PATCH 3/8] Add Windows shared context. --- gfx/context/wgl_ctx.c | 54 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/gfx/context/wgl_ctx.c b/gfx/context/wgl_ctx.c index 51452d5257..6420b6972d 100644 --- a/gfx/context/wgl_ctx.c +++ b/gfx/context/wgl_ctx.c @@ -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_render = 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, }; From 5614a77ceca5c6a2719c878ba8c12af9ba6c6160 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 16:25:27 +0200 Subject: [PATCH 4/8] Build fix. --- gfx/context/wgl_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/context/wgl_ctx.c b/gfx/context/wgl_ctx.c index 6420b6972d..90e87b7f63 100644 --- a/gfx/context/wgl_ctx.c +++ b/gfx/context/wgl_ctx.c @@ -591,7 +591,7 @@ static void gfx_ctx_show_mouse(void *data, bool state) static void gfx_ctx_bind_hw_render(void *data, bool enable) { - g_use_hw_render = enable; + g_use_hw_ctx = enable; if (g_hdc) wglMakeCurrent(g_hdc, enable ? g_hw_hrc : g_hrc); } From 1cbb47229a8687a6c167f437abf51f0a51fe5e51 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 16:37:39 +0200 Subject: [PATCH 5/8] Make video_shared_context an option. Might be part of libretro API later if necessary. Must be discussed with other frontend developers first. --- config.def.h | 3 +++ general.h | 1 + gfx/gfx_context.c | 6 +++++- gfx/gl.c | 2 +- retroarch.cfg | 4 ++++ settings.c | 3 +++ 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/config.def.h b/config.def.h index 232b745997..aae6d51880 100644 --- a/config.def.h +++ b/config.def.h @@ -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; diff --git a/general.h b/general.h index 466a1a2282..ea28fa0f10 100644 --- a/general.h +++ b/general.h @@ -183,6 +183,7 @@ struct settings bool gpu_screenshot; bool allow_rotate; + bool shared_context; } video; #ifdef HAVE_MENU diff --git a/gfx/gfx_context.c b/gfx/gfx_context.c index 04576bf578..eea953a0bc 100644 --- a/gfx/gfx_context.c +++ b/gfx/gfx_context.c @@ -79,7 +79,11 @@ const gfx_ctx_driver_t *gfx_ctx_init_first(void *data, enum gfx_ctx_api api, uns 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, hw_render_ctx); + { + 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]; } diff --git a/gfx/gl.c b/gfx/gl.c index df3ced4538..77cb55af7a 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1929,7 +1929,7 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl) // Enables or disables offscreen HW context. if (ctx->bind_hw_render) - ctx->bind_hw_render(gl, cb->context_type != RETRO_HW_CONTEXT_NONE); + ctx->bind_hw_render(gl, g_settings.video.shared_context && cb->context_type != RETRO_HW_CONTEXT_NONE); if (!ctx->init(gl)) { diff --git a/retroarch.cfg b/retroarch.cfg index 99ee5738ec..dafdfc6292 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -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 diff --git a/settings.c b/settings.c index 8bd41900fe..0db5d507d6 100644 --- a/settings.c +++ b/settings.c @@ -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); From 095718c8559d8ffe7a1d966f3cf1d4d576e688d1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 16:59:26 +0200 Subject: [PATCH 6/8] Only bind shared context if necessary. --- gfx/gl.c | 6 ++++-- gfx/gl_common.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 77cb55af7a..2b52a6196a 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1916,6 +1916,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); @@ -1929,7 +1931,7 @@ static const gfx_ctx_driver_t *gl_get_context(gl_t *gl) // Enables or disables offscreen HW context. if (ctx->bind_hw_render) - ctx->bind_hw_render(gl, g_settings.video.shared_context && cb->context_type != RETRO_HW_CONTEXT_NONE); + ctx->bind_hw_render(gl, gl->shared_context_use); if (!ctx->init(gl)) { @@ -1946,7 +1948,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, cb->context_type != RETRO_HW_CONTEXT_NONE); + return gfx_ctx_init_first(gl, api, major, minor, gl->shared_context_use); } #ifdef GL_DEBUG diff --git a/gfx/gl_common.h b/gfx/gl_common.h index fbd43788e0..734d38e47b 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -45,7 +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->hw_render_use && gl->ctx_driver->bind_hw_render) gl->ctx_driver->bind_hw_render(gl, enable) +#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) @@ -179,6 +179,7 @@ typedef struct gl bool has_fp_fbo; #endif bool hw_render_use; + bool shared_context_use; bool should_resize; bool quitting; From c39d27fd8db041c21b6ae7cab54dab56b24aa9f8 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 17:22:25 +0200 Subject: [PATCH 7/8] Add async readback to GLES3. Have support for PBOs and glMapBufferRange. --- gfx/gl.c | 52 +++++++++++++++++++++++++++++++++++++++++-------- gfx/gl_common.h | 6 +++++- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 2b52a6196a..c036b49ef4 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1320,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; @@ -1337,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); @@ -1550,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 @@ -1653,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); @@ -1854,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; @@ -1864,7 +1870,7 @@ 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++) @@ -2242,7 +2248,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 @@ -2428,21 +2434,51 @@ static bool gl_read_viewport(void *data, uint8_t *buffer) 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); } diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 734d38e47b..0d1ac66b99 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -58,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(); @@ -240,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; From 80a12ae1d60384f93d0efcebd899282e8a293e89 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 19 Apr 2014 17:27:49 +0200 Subject: [PATCH 8/8] Don't create PBO scaler if not needed. --- gfx/gl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gfx/gl.c b/gfx/gl.c index c036b49ef4..be7fb18a8f 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1877,10 +1877,11 @@ static void gl_init_pbo_readback(void *data) { 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; @@ -1898,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