mirror of
https://github.com/libretro/parallel-n64.git
synced 2026-11-07 12:37:50 -10:00
glsm: survive a lost GL context in every GL plugin
A frontend can hand the core a new GL context at any time: RetroArch does so when threaded video is toggled, when the video driver is reinitialised, or when the shared core context changes. The core then gets context_destroy (sometimes) and context_reset. Two things went wrong on the second context_reset: - glsm called GLideN64's retroChangeWindow() from its RESET handler for whichever plugin was active. With Glide64, Rice or glN64 this ran GLideN64 code on state it never set up and crashed. The GLideN64 reinit already happens in context_reset() in libretro.c, so the call in glsm is dropped and glsm now only rebinds its own state for the new context. - GLideN64's teardown deleted its textures, buffers, framebuffers, VAOs, syncs and queries by name in the new context. Those names belonged to the lost context; in the new one they may already name the frontend's own objects. glsm_abandon_objects() makes glsm's delete calls no-ops while the old objects are forgotten, and context_reset() uses it around gliden64DestroyGfxContext(). lrhost_gl gains LRHOST_GL_SWITCH_EVERY (and LRHOST_GL_SWITCH_DESTROY) to replace the context every N frames, and the GL renderer lane runs every plugin through a context switch with and without a destroy. Each must finish all frames; GLideN64 must render the same frames as without the switch. On the old code Glide64, Rice and glN64 segfault.
This commit is contained in:
1 parent
3654ab56e1
commit
e8cbdbbfa6
6 files changed
+102
-10
No files matched your search
+28
-9
@@ -295,8 +295,14 @@ static GLuint frontend_framebuffer;
|
||||
|
||||
static GLenum active_texture;
|
||||
static int window_first = 0;
|
||||
/* the objects named are the lost context's: deleting them would delete
|
||||
* whatever the new context has under those names */
|
||||
static int resetting_context = 0;
|
||||
extern void retroChangeWindow();
|
||||
|
||||
void glsm_abandon_objects(bool abandon)
|
||||
{
|
||||
resetting_context = abandon ? 1 : 0;
|
||||
}
|
||||
|
||||
static void bindFBO(GLenum target)
|
||||
{
|
||||
@@ -1003,6 +1009,8 @@ void rglCompressedTexImage2D(GLenum target, GLint level,
|
||||
void rglDeleteFramebuffers(GLsizei n, const GLuint *_framebuffers)
|
||||
{
|
||||
int i, p;
|
||||
if (resetting_context)
|
||||
return;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (_framebuffers[i] == 0)
|
||||
@@ -1026,6 +1034,8 @@ void rglDeleteTextures(GLsizei n, const GLuint *textures)
|
||||
int i;
|
||||
size_t f;
|
||||
unsigned u;
|
||||
if (resetting_context)
|
||||
return;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (textures[i] == 0)
|
||||
@@ -1077,6 +1087,8 @@ void rglBindRenderbuffer(GLenum target, GLuint renderbuffer)
|
||||
*/
|
||||
void rglDeleteRenderbuffers(GLsizei n, GLuint *renderbuffers)
|
||||
{
|
||||
if (resetting_context)
|
||||
return;
|
||||
glDeleteRenderbuffers(n, renderbuffers);
|
||||
}
|
||||
|
||||
@@ -1214,6 +1226,8 @@ void rglGetQueryObjectuiv( GLuint id,
|
||||
void rglDeleteQueries( GLsizei n,
|
||||
const GLuint * ids)
|
||||
{
|
||||
if (resetting_context)
|
||||
return;
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3)
|
||||
glDeleteQueries(n, ids);
|
||||
#endif
|
||||
@@ -1710,6 +1724,8 @@ void rglDeleteBuffers(GLsizei n, const GLuint *buffers)
|
||||
{
|
||||
int i;
|
||||
unsigned a;
|
||||
if (resetting_context)
|
||||
return;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (buffers[i] == 0)
|
||||
@@ -2280,6 +2296,8 @@ void * rglMapBuffer( GLenum target, GLenum access)
|
||||
*/
|
||||
GLboolean rglUnmapBuffer( GLenum target)
|
||||
{
|
||||
if (resetting_context)
|
||||
return GL_TRUE;
|
||||
#if defined(HAVE_OPENGLES)
|
||||
return glUnmapBufferOES(target);
|
||||
#else
|
||||
@@ -2446,6 +2464,8 @@ void rglDeleteVertexArrays(GLsizei n, const GLuint *arrays)
|
||||
{
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3)
|
||||
int i;
|
||||
if (resetting_context)
|
||||
return;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (arrays[i] != 0 && arrays[i] == gl_state.bindvertex.array)
|
||||
@@ -2487,6 +2507,8 @@ void *rglFenceSync(GLenum condition, GLbitfield flags)
|
||||
*/
|
||||
void rglDeleteSync(GLsync sync)
|
||||
{
|
||||
if (resetting_context)
|
||||
return;
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3)
|
||||
glDeleteSync((GLsync)sync);
|
||||
#endif
|
||||
@@ -3411,17 +3433,14 @@ bool glsm_ctl(enum glsm_state_ctl state, void *data)
|
||||
initGLFunctions();
|
||||
#endif
|
||||
|
||||
if (window_first > 0) {
|
||||
resetting_context = 1;
|
||||
/* a new context: what glsm knew of the old one is dropped */
|
||||
if (window_first > 0)
|
||||
{
|
||||
glsm_state_setup();
|
||||
#ifdef HAVE_GLIDEN64
|
||||
retroChangeWindow();
|
||||
#endif
|
||||
glsm_state_unbind();
|
||||
resetting_context = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
window_first = 1;
|
||||
window_first = 1;
|
||||
break;
|
||||
case GLSM_CTL_STATE_CONTEXT_DESTROY:
|
||||
glsm_state_ctx_destroy(data);
|
||||
|
||||
@@ -141,6 +141,11 @@ void glsm_draw_target(unsigned width, unsigned height);
|
||||
void glsm_draw_target_present(void);
|
||||
bool glsm_draw_target_show(void);
|
||||
|
||||
/* While set, deleting GL objects does nothing: for tearing down a
|
||||
* renderer whose context was lost, after GLSM_CTL_STATE_CONTEXT_RESET,
|
||||
* where the names may be the new context's. */
|
||||
void glsm_abandon_objects(bool abandon);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -1520,7 +1520,10 @@ static void context_reset(void)
|
||||
* before a context exists. */
|
||||
if (gfx_plugin == GFX_GLIDEN64 && !first_context_reset)
|
||||
{
|
||||
/* its objects were the lost context's: forgotten, not deleted */
|
||||
glsm_abandon_objects(true);
|
||||
gliden64DestroyGfxContext();
|
||||
glsm_abandon_objects(false);
|
||||
gliden64ReinitGfxContext();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,15 @@ across drivers, so the frames are not held to stored hashes; the lane holds them
|
||||
GLideN64's under -fcommon, crashed at exit, and Rice crashed in the
|
||||
driver putting a frame the CPU left in RDRAM on screen with no program
|
||||
bound (shade32, tlut16 and zbuf16 draw once, so Rice shows RDRAM after);
|
||||
* every renderer running on when the frontend moves it to a new
|
||||
context every 10 runs and calls context_reset (lrhost_gl's
|
||||
LRHOST_GL_SWITCH_EVERY), with and without context_destroy first, as
|
||||
RetroArch does when its video driver restarts - threaded video
|
||||
toggled moves a hardware core between two contexts - and GLideN64
|
||||
drawing the same frames as without: Glide64, Rice and glN64 crashed
|
||||
in the second context_reset, which drove GLideN64's window change
|
||||
whichever renderer was in use, and GLideN64 deleted the lost
|
||||
context's objects by name in the new one;
|
||||
* GLideN64 drawing the same frames whatever its colour buffer copy to
|
||||
RDRAM is set to (Off, Sync, Async, TripleBuffer): the copy writes RDRAM
|
||||
and must not disturb what is drawn, its pixel buffers or the vertex
|
||||
@@ -94,6 +103,22 @@ def main():
|
||||
if not ok:
|
||||
failures += 1
|
||||
sys.stdout.write(out[-2000:])
|
||||
for plugin in PLUGINS:
|
||||
rc, base, out = run(a.host, a.core, path, plugin, [], tmp)
|
||||
for name, env_extra in (("switch", {"LRHOST_GL_SWITCH_EVERY": "10"}),
|
||||
("destroy+switch", {"LRHOST_GL_SWITCH_EVERY": "10",
|
||||
"LRHOST_GL_SWITCH_DESTROY": "1"})):
|
||||
rc, frames, out = run(a.host, a.core, path, plugin, [], tmp, env_extra)
|
||||
ok = rc == 0 and len(frames) == len(base)
|
||||
if ok and plugin == "gliden64" and frames != base:
|
||||
ok = False
|
||||
print("%-10s %-9s context %-14s %s" % (rom, plugin, name,
|
||||
"ok" if ok else "FAILED (exit %d, %d of %d frames%s)" % (
|
||||
rc, len(frames), len(base),
|
||||
", other frames" if rc == 0 and len(frames) == len(base) else "")))
|
||||
if not ok:
|
||||
failures += 1
|
||||
sys.stdout.write(out[-2000:])
|
||||
reference = None
|
||||
for mode in COPY_MODES:
|
||||
rc, frames, out = run(a.host, a.core, path, "gliden64",
|
||||
|
||||
@@ -523,6 +523,11 @@ int main(int argc, char **argv)
|
||||
for (k = 0; k < len; k++)
|
||||
ram[off + k] = (unsigned char)((k * 7u + (unsigned)i * 13u) ^ (k >> 3));
|
||||
}
|
||||
#ifdef LRHOST_GL
|
||||
if (getenv("LRHOST_GL_SWITCH_EVERY") && i > 0 && gl_egl_ctx != EGL_NO_CONTEXT
|
||||
&& i % atoi(getenv("LRHOST_GL_SWITCH_EVERY")) == 0 && !gl_switch_context())
|
||||
return 1;
|
||||
#endif
|
||||
run_frame = i; t0 = now_ms();
|
||||
if (getenv("LRHOST_RUNAHEAD") && i > 0)
|
||||
{
|
||||
|
||||
+36
-1
@@ -29,6 +29,8 @@ typedef int GLHsizei;
|
||||
static struct retro_hw_render_callback gl_hw;
|
||||
static EGLDisplay gl_dpy = EGL_NO_DISPLAY;
|
||||
static EGLContext gl_egl_ctx = EGL_NO_CONTEXT;
|
||||
static EGLConfig gl_cfg;
|
||||
static EGLint gl_ctx_attr[16];
|
||||
static GLHuint gl_fbo, gl_rb_color, gl_rb_depth;
|
||||
static unsigned gl_fb_w, gl_fb_h;
|
||||
static uint32_t *gl_frame; static size_t gl_frame_cap;
|
||||
@@ -51,12 +53,14 @@ static retro_proc_address_t gl_get_proc_address(const char *sym)
|
||||
return (retro_proc_address_t)eglGetProcAddress(sym);
|
||||
}
|
||||
|
||||
static int gl_make_framebuffer(unsigned max_w, unsigned max_h);
|
||||
|
||||
static int gl_init(unsigned max_w, unsigned max_h)
|
||||
{
|
||||
EGLint major, minor, n;
|
||||
EGLConfig cfg;
|
||||
EGLint *ctx_attr = gl_ctx_attr;
|
||||
const EGLint cfg_attr[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE };
|
||||
EGLint ctx_attr[16];
|
||||
int i = 0;
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display =
|
||||
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
@@ -83,6 +87,7 @@ static int gl_init(unsigned max_w, unsigned max_h)
|
||||
ctx_attr[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK; ctx_attr[i++] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
|
||||
}
|
||||
ctx_attr[i++] = EGL_NONE;
|
||||
gl_cfg = cfg;
|
||||
gl_egl_ctx = eglCreateContext(gl_dpy, cfg, EGL_NO_CONTEXT, ctx_attr);
|
||||
if (gl_egl_ctx == EGL_NO_CONTEXT || !eglMakeCurrent(gl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, gl_egl_ctx))
|
||||
{ fprintf(stderr, "lrhost_gl: cannot make a %s %u.%u context current\n",
|
||||
@@ -96,6 +101,12 @@ static int gl_init(unsigned max_w, unsigned max_h)
|
||||
#undef GLP
|
||||
|
||||
gl_fb_w = max_w; gl_fb_h = max_h;
|
||||
return gl_make_framebuffer(max_w, max_h);
|
||||
}
|
||||
|
||||
/* the framebuffer the core draws into, in the context current */
|
||||
static int gl_make_framebuffer(unsigned max_w, unsigned max_h)
|
||||
{
|
||||
p_glGenFramebuffers(1, &gl_fbo);
|
||||
p_glBindFramebuffer(GLH_FRAMEBUFFER, gl_fbo);
|
||||
p_glGenRenderbuffers(1, &gl_rb_color);
|
||||
@@ -115,6 +126,30 @@ static int gl_init(unsigned max_w, unsigned max_h)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* LRHOST_GL_SWITCH_EVERY=n: every n runs the core is moved to a new
|
||||
* context sharing objects with the old one, with a new framebuffer, as
|
||||
* a frontend restarting its video driver moves it (RetroArch toggling
|
||||
* threaded video moves a hardware core between its own context and a
|
||||
* shared one). The new context is made current and context_reset
|
||||
* called; LRHOST_GL_SWITCH_DESTROY=1 calls context_destroy in the old
|
||||
* one first, as a frontend that tears the core down before does. */
|
||||
static int gl_switch_context(void)
|
||||
{
|
||||
EGLContext old = gl_egl_ctx;
|
||||
if (getenv("LRHOST_GL_SWITCH_DESTROY") && gl_hw.context_destroy)
|
||||
gl_hw.context_destroy();
|
||||
gl_egl_ctx = eglCreateContext(gl_dpy, gl_cfg, old, gl_ctx_attr);
|
||||
if (gl_egl_ctx == EGL_NO_CONTEXT
|
||||
|| !eglMakeCurrent(gl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, gl_egl_ctx))
|
||||
{ fprintf(stderr, "lrhost_gl: no second context\n"); return 0; }
|
||||
eglDestroyContext(gl_dpy, old);
|
||||
if (!gl_make_framebuffer(gl_fb_w, gl_fb_h))
|
||||
return 0;
|
||||
if (gl_hw.context_reset)
|
||||
gl_hw.context_reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the frame the core drew, top-down XRGB8888 */
|
||||
static const uint32_t *gl_read_frame(unsigned w, unsigned h)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user