sdl12-compat: video: Try to reset current GL context after touching the renderer.

From aa87f014c1240cc6f518f8b072f84a0f8e06e95c Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 1 Sep 2022 14:47:25 -0400
Subject: [PATCH] video: Try to reset current GL context after touching the
 renderer.

This way if an app tries to render on a background thread, SDL2's GL
renderers will have a chance of working, since they'll be able to set
the context current for that thread.

This does not fix tucnak, for reasons that aren't clear, so that'll
continue to force the software renderer through the quirks mechanism, but
it definitely seems to fix linapple and probably other things, too.

Fixes #104.
Fixes #110.
Fixes #181.
Fixes #155.

(I think.)
---
 src/SDL12_compat.c | 31 +++++++++++++++++++++++++++++++
 src/SDL20_syms.h   |  1 +
 2 files changed, 32 insertions(+)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index bb015e5a..d89a5b12 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -5578,6 +5578,26 @@ InitializeOpenGLScaling(const int w, const int h)
 }
 
 
+/* The idea here is that SDL's OpenGL-based renderers always notice if
+   they aren't using the correct context and attempt to set the correct
+   context before doing any work, but (at least for X11, and probably
+   other platforms), the current context is thread-local, and _it's an
+   error to set a context current if it's already current on another thread_.
+   So we try to catch every place we call into the renderer and end that
+   work with a call to this function, which will reset the GL context to NULL,
+   so if an app tries to render from a background thread, the GL renderer
+   will be able to set the context, do it's work, and then we reset it right
+   after. Without this, we either need all apps to render exclusively on
+   the main thread or fail to draw at all.
+
+   This feels risky, but it's better than the alternative! */
+static void ResetVideoRendererForThreading(void)
+{
+    if ((VideoRenderer20 != NULL) && (SDL20_GL_GetCurrentContext() != NULL)) {
+        SDL20_GL_MakeCurrent(NULL, NULL);
+    }
+}
+
 static void HandleInputGrab(SDL12_GrabMode mode);
 
 DECLSPEC SDL12_Surface * SDLCALL
@@ -6008,6 +6028,10 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12)
     VideoSurfacePresentTicks = 0;
     VideoSurfaceLastPresentTicks = 0;
 
+    if ((flags12 & SDL12_OPENGL) == 0) {
+        ResetVideoRendererForThreading();
+    }
+
     return VideoSurface12;
 }
 
@@ -6322,6 +6346,8 @@ PresentScreen(void)
     SDL20_RenderPresent(VideoRenderer20);
     VideoSurfaceLastPresentTicks = SDL20_GetTicks();
     VideoSurfacePresentTicks = 0;
+
+    ResetVideoRendererForThreading();
 }
 
 static void
@@ -6752,6 +6778,7 @@ SDL_WM_ToggleFullScreen(SDL12_Surface *surface)
         }
         if (retval && VideoRenderer20) {
             SDL20_RenderSetLogicalSize(VideoRenderer20, VideoSurface12->w, VideoSurface12->h);
+            ResetVideoRendererForThreading();
         }
     }
     return retval;
@@ -7203,6 +7230,8 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12)
     retval->pixels = hwdata->pixels;
     hwdata->dirty = SDL_TRUE;
 
+    ResetVideoRendererForThreading();
+
     return retval;
 }
 
@@ -7298,6 +7327,8 @@ SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12)
         VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame();  /* flip it later. */
     }
 
+    ResetVideoRendererForThreading();
+
     return 0;
 }
 
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index b4b7d04f..8c4bf0af 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -136,6 +136,7 @@ SDL20_SYM(int,GL_GetAttribute,(SDL_GLattr a, int *b),(a,b),return)
 SDL20_SYM(int,GL_SetSwapInterval,(int a),(a),return)
 SDL20_SYM(int,GL_GetSwapInterval,(void),(),return)
 SDL20_SYM(SDL_GLContext,GL_CreateContext,(SDL_Window *a),(a),return)
+SDL20_SYM(SDL_GLContext,GL_GetCurrentContext,(void),(),return)
 SDL20_SYM(int,GL_MakeCurrent,(SDL_Window *a, SDL_GLContext b),(a,b),return)
 SDL20_SYM(void,GL_SwapWindow,(SDL_Window *a),(a),)
 SDL20_SYM(void,GL_DeleteContext,(SDL_GLContext a),(a),)