sdl12-compat: video: Don't tear everything down in SDL_SetVideoMode software surface changes.

From 0ea5b381a3b95308852bd70e5c6a418381035bce Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 8 Jan 2026 12:38:09 -0500
Subject: [PATCH] video: Don't tear everything down in SDL_SetVideoMode
 software surface changes.

This is noticable in, for example, Loki's port of Sid Meier's Alpha Centauri,
where it wants to show static Loki/Firaxis logos on an 8-bit screen surface,
then set the video mode to 16-bit for intro videos through SMPEG, then back to
an 8-bit surface for the main menu and gameplay. Before this, windows would
be destroyed and immediately pop back up: an unnecessary visual glitch, but
also it caused rendering to fail on at least one system.

Since software surfaces already go through a conversion to whatever format the
SDL2 renderer's texture wants, it was better to avoid all the damage by
letting the conversion surface absorb the format change.

Fixes #389.
---
 src/SDL12_compat.c | 40 ++++++++++++++++++++++++----------------
 src/SDL20_syms.h   |  1 +
 2 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 44bd0f2ba..db9f4092e 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -6515,8 +6515,8 @@ SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12)
 
     if (VideoSurface12->surface20 && ((VideoSurface12->flags & SDL12_OPENGL) != (flags12 & SDL12_OPENGL))) {
         EndVidModeCreate();  /* rebuild the window if moving to/from a GL context */
-    } else if (VideoSurface12->surface20 && (VideoSurface12->surface20->format->format != appfmt)) {
-        EndVidModeCreate();  /* rebuild the window if changing pixel format */
+    } else if ((flags12 & SDL12_OPENGL) && VideoSurface12->surface20 && (VideoSurface12->surface20->format->format != appfmt)) {
+        EndVidModeCreate();  /* rebuild the window if changing pixel format on an OpenGL surface */
     } else if (DesiredRefreshRate != CurrentRefreshRate) {
         EndVidModeCreate();  /* rebuild the window if changing refresh rate */
     } else {
@@ -6525,7 +6525,7 @@ SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12)
             /* The windx5 driver _always_ destroyed the window, unconditionally, but the default (windib) did not, so match windib.
              *  windib: keep if:
              *   - window already exists
-             *   - BitsPerPixel hasn't changed
+             *   - BitsPerPixel hasn't changed (in OpenGL mode; for software we don't care about the format change).
              *   - none of the window flags (except SDL_ANYFORMAT) have changed
              *   - window is already SDL_OPENGL.
              *   - window is not a fullscreen window.
@@ -6533,7 +6533,7 @@ SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12)
             const Uint32 important_flags = ~(SDL12_PREALLOC | SDL12_ANYFORMAT);
             const SDL_bool recreate_window = (
                 ((VideoSurface12->flags & important_flags) != (flags12 & important_flags)) ||
-                (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp)) ||
+                ((flags12 & SDL12_OPENGL) && (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp))) ||
                 ((flags12 & SDL12_FULLSCREEN) == SDL12_FULLSCREEN)
             ) ? SDL_TRUE : SDL_FALSE;
         #elif defined(__APPLE__)
@@ -6547,12 +6547,12 @@ SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12)
              *   - window already exists
              *   - window is already SDL_OPENGL.
              *   - new flags also want SDL_OPENGL.
-             *   - BitsPerPixel hasn't changed
+             *   - BitsPerPixel hasn't changed (in OpenGL mode; for software we don't care about the format change).
              *   - SDL_NOFRAME hasn't changed
              */
             const SDL_bool recreate_window = (
                 ((VideoSurface12->flags & SDL12_OPENGL) != (flags12 & SDL12_OPENGL)) ||
-                (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp)) ||
+                ((flags12 & SDL12_OPENGL) && (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp))) ||
                 ((VideoSurface12->flags & SDL12_NOFRAME) != (flags12 & SDL12_NOFRAME))
             ) ? SDL_TRUE : SDL_FALSE;
         #else
@@ -6804,20 +6804,28 @@ SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12)
             return EndVidModeCreate();
         }
 
-        if (VideoTexture20) {
-            SDL20_DestroyTexture(VideoTexture20);
+        if (!VideoTexture20) {
+            SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, WantScaleMethodNearest ? "0" : "1");
+            VideoTexture20 = SDL20_CreateTexture(VideoRenderer20, rinfo.texture_formats[0], SDL_TEXTUREACCESS_STREAMING, width, height);
+            SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_scale_quality);
+            if (!VideoTexture20) {
+                return EndVidModeCreate();
+            }
         }
 
-        if (VideoConvertSurface20) {
-            SDL20_FreeSurface(VideoConvertSurface20);
-            VideoConvertSurface20 = NULL;
+        /* clear the texture for (re)use */
+        {
+            SDL_Surface *surface = NULL;
+            if (SDL20_LockTextureToSurface(VideoTexture20, NULL, &surface) == 0) {
+                SDL20_FillRect(surface, NULL, SDL20_MapRGB(surface->format, 0, 0, 0));
+                SDL20_UnlockTexture(VideoTexture20);
+            }
         }
 
-        SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, WantScaleMethodNearest ? "0" : "1");
-        VideoTexture20 = SDL20_CreateTexture(VideoRenderer20, rinfo.texture_formats[0], SDL_TEXTUREACCESS_STREAMING, width, height);
-        SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_scale_quality);
-        if (!VideoTexture20) {
-            return EndVidModeCreate();
+        /* don't need conversion, or need to change the conversion surface's format? Nuke the existing surface (and maybe rebuild it later). */
+        if (VideoConvertSurface20 && ((rinfo.texture_formats[0] == appfmt) || (rinfo.texture_formats[0] != VideoConvertSurface20->format->format))) {
+            SDL20_FreeSurface(VideoConvertSurface20);
+            VideoConvertSurface20 = NULL;
         }
 
         if (rinfo.texture_formats[0] != appfmt) {
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index 23b8bcc52..f0ac1e91d 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -325,6 +325,7 @@ SDL20_SYM(void,RenderGetScale,(SDL_Renderer *a, float *b, float *c),(a,b,c),retu
 SDL20_SYM(void,RenderGetViewport,(SDL_Renderer *a, SDL_Rect *b),(a,b),return)
 SDL20_SYM(SDL_Texture *,CreateTexture,(SDL_Renderer *a, Uint32 b, int c, int d, int e),(a,b,c,d,e),return)
 SDL20_SYM(int,LockTexture,(SDL_Texture *a, const SDL_Rect *b, void **c, int *d),(a,b,c,d),return)
+SDL20_SYM(int,LockTextureToSurface,(SDL_Texture *a, const SDL_Rect *b, SDL_Surface **c),(a,b,c),return)
 SDL20_SYM(void,UnlockTexture,(SDL_Texture *a),(a),)
 SDL20_SYM(int,UpdateTexture,(SDL_Texture *a, const SDL_Rect *b, const void *c, int d),(a,b,c,d),return)
 SDL20_SYM(int,UpdateYUVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f, const Uint8 *g, int h),(a,b,c,d,e,f,g,h),return)