SDL: Clean up renderers at shutdown

From ab3c8552c2733e03c47253c5840bed7a27716244 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 23 Jun 2024 00:41:19 -0700
Subject: [PATCH] Clean up renderers at shutdown

Fixes https://github.com/libsdl-org/SDL/issues/10082
---
 src/SDL.c                  |  2 ++
 src/render/SDL_render.c    | 27 +++++++++++++++++++++++++++
 src/render/SDL_sysrender.h |  5 +++++
 3 files changed, 34 insertions(+)

diff --git a/src/SDL.c b/src/SDL.c
index d1bf69ff1d85b..7814ad2062467 100644
--- a/src/SDL.c
+++ b/src/SDL.c
@@ -47,6 +47,7 @@
 #include "haptic/SDL_haptic_c.h"
 #include "joystick/SDL_gamepad_c.h"
 #include "joystick/SDL_joystick_c.h"
+#include "render/SDL_sysrender.h"
 #include "sensor/SDL_sensor_c.h"
 #include "stdlib/SDL_getenv_c.h"
 #include "video/SDL_video_c.h"
@@ -474,6 +475,7 @@ void SDL_QuitSubSystem(Uint32 flags)
 #ifndef SDL_VIDEO_DISABLED
     if (flags & SDL_INIT_VIDEO) {
         if (SDL_ShouldQuitSubsystem(SDL_INIT_VIDEO)) {
+            SDL_QuitRender();
             SDL_VideoQuit();
             /* video implies events */
             SDL_QuitSubSystem(SDL_INIT_EVENTS);
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index c5e126d6c1e6b..83f63028995cd 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -133,6 +133,14 @@ static const SDL_RenderDriver *render_drivers[] = {
 };
 #endif /* !SDL_RENDER_DISABLED */
 
+static SDL_Renderer *SDL_renderers;
+
+void SDL_QuitRender(void)
+{
+    while (SDL_renderers) {
+        SDL_DestroyRenderer(SDL_renderers);
+    }
+}
 
 int SDL_AddSupportedTextureFormat(SDL_Renderer *renderer, SDL_PixelFormatEnum format)
 {
@@ -1105,6 +1113,9 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
                 "Created renderer: %s", renderer->name);
 
+    renderer->next = SDL_renderers;
+    SDL_renderers = renderer;
+
 #ifdef SDL_PLATFORM_ANDROID
     Android_ActivityMutex_Unlock();
 #endif
@@ -4619,6 +4630,22 @@ void SDL_DestroyRenderer(SDL_Renderer *renderer)
     if (!renderer->destroyed) {
         SDL_DestroyRendererWithoutFreeing(renderer);
     }
+
+    SDL_Renderer *curr = SDL_renderers;
+    SDL_Renderer *prev = NULL;
+    while (curr) {
+        if (curr == renderer) {
+            if (prev) {
+                prev->next = renderer->next;
+            } else {
+                SDL_renderers = renderer->next;
+            }
+            break;
+        }
+        prev = curr;
+        curr = curr->next;
+    }
+
     SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, SDL_FALSE);  // It's no longer magical...
 
     SDL_free(renderer->texture_formats);
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 22dd0e714022d..8d7cb758e3674 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -291,6 +291,8 @@ struct SDL_Renderer
     SDL_bool destroyed;   // already destroyed by SDL_DestroyWindow; just free this struct in SDL_DestroyRenderer.
 
     void *driverdata;
+
+    SDL_Renderer *next;
 };
 
 /* Define the SDL render driver structure */
@@ -314,6 +316,9 @@ extern SDL_RenderDriver PSP_RenderDriver;
 extern SDL_RenderDriver SW_RenderDriver;
 extern SDL_RenderDriver VITA_GXM_RenderDriver;
 
+/* Clean up any renderers at shutdown */
+extern void SDL_QuitRender(void);
+
 /* Add a supported texture format to a renderer */
 extern int SDL_AddSupportedTextureFormat(SDL_Renderer *renderer, SDL_PixelFormatEnum format);