SDL: render: Batching is always enabled now!

From 6ba90f77752ab0350f28fb228c5dde550244904e Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 20 Nov 2023 20:26:12 -0500
Subject: [PATCH] render: Batching is always enabled now!

Make sure your app uses SDL_RenderFlush() before it talks to D3D/OpenGL/etc!

Fixes #8584.
---
 docs/README-migration.md            |  8 +++++
 include/SDL3/SDL_hints.h            | 24 ---------------
 include/SDL3/SDL_render.h           | 20 ++++--------
 src/render/SDL_render.c             | 47 +++++++----------------------
 src/render/SDL_sysrender.h          |  2 --
 src/render/metal/SDL_render_metal.m |  2 --
 src/test/SDL_test_common.c          |  1 -
 7 files changed, 25 insertions(+), 79 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index ff1e8f9763ae..92351965f6ab 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -559,6 +559,7 @@ The following hints have been removed:
 * SDL_HINT_IME_SUPPORT_EXTENDED_TEXT - the normal text editing event has extended text
 * SDL_HINT_MOUSE_RELATIVE_SCALING - mouse coordinates are no longer automatically scaled by the SDL renderer
 * SDL_HINT_RENDER_LOGICAL_SIZE_MODE - the logical size mode is explicitly set with SDL_SetRenderLogicalPresentation()
+* SDL_HINT_RENDER_BATCHING - Render batching is always enabled, apps should call SDL_FlushRenderer() before calling into a lower-level graphics API.
 * SDL_HINT_VIDEO_FOREIGN_WINDOW_OPENGL - replaced with the "opengl" property in SDL_CreateWindowWithProperties()
 * SDL_HINT_VIDEO_FOREIGN_WINDOW_VULKAN - replaced with the "vulkan" property in SDL_CreateWindowWithProperties()
 * SDL_HINT_VIDEO_HIGHDPI_DISABLED - high DPI support is always enabled
@@ -811,6 +812,13 @@ The following functions have been renamed:
 
 ## SDL_render.h
 
+The 2D renderer API always uses batching in SDL3. There is no magic to turn
+it on and off; it doesn't matter if you select a specific renderer or try to
+use any hint. This means that all apps that use SDL3's 2D renderer and also
+want to call directly into the platform's lower-layer graphics API _must_ call
+SDL_RenderFlush() before doing so. This will make sure any pending rendering
+work from SDL is done before the app starts directly drawing.
+
 SDL_GetRenderDriverInfo() has been removed, since most of the information it reported were
 estimates and could not be accurate before creating a renderer. Often times this function
 was used to figure out the index of a driver, so one would call it in a for-loop, looking
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index d2c2b0d8a73a..184dac3fb1e7 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1387,30 +1387,6 @@ extern "C" {
  */
 #define SDL_HINT_QTWAYLAND_WINDOW_FLAGS "SDL_QTWAYLAND_WINDOW_FLAGS"
 
-/**
- *  A variable controlling whether the 2D render API is compatible or efficient.
- *
- *  This variable can be set to the following values:
- *
- *    "0"     - Don't use batching to make rendering more efficient.
- *    "1"     - Use batching, but might cause problems if app makes its own direct OpenGL calls.
- *
- *  Up to SDL 2.0.9, the render API would draw immediately when requested. Now
- *  it batches up draw requests and sends them all to the GPU only when forced
- *  to (during SDL_RenderPresent, when changing render targets, by updating a
- *  texture that the batch needs, etc). This is significantly more efficient,
- *  but it can cause problems for apps that expect to render on top of the
- *  render API's output. As such, SDL will disable batching if a specific
- *  render backend is requested (since this might indicate that the app is
- *  planning to use the underlying graphics API directly). This hint can
- *  be used to explicitly request batching in this instance. It is a contract
- *  that you will either never use the underlying graphics API directly, or
- *  if you do, you will call SDL_RenderFlush() before you do so any current
- *  batch goes to the GPU before your work begins. Not following this contract
- *  will result in undefined behavior.
- */
-#define SDL_HINT_RENDER_BATCHING  "SDL_RENDER_BATCHING"
-
 /**
  *  A variable controlling how the 2D render API renders lines
  *
diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index b7313d9d09d2..caff6f87a672 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -1559,20 +1559,12 @@ extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer *renderer);
  * are planning to call into OpenGL/Direct3D/Metal/whatever directly in
  * addition to using an SDL_Renderer.
  *
- * This is for a very-specific case: if you are using SDL's render API, you
- * asked for a specific renderer backend (OpenGL, Direct3D, etc), you set
- * SDL_HINT_RENDER_BATCHING to "1", and you plan to make OpenGL/D3D/whatever
- * calls in addition to SDL render API calls. If all of this applies, you
- * should call SDL_RenderFlush() between calls to SDL's render API and the
- * low-level API you're using in cooperation.
- *
- * In all other cases, you can ignore this function. This is only here to get
- * maximum performance out of a specific situation. In all other cases, SDL
- * will do the right thing, perhaps at a performance loss.
- *
- * This function is first available in SDL 2.0.10, and is not needed in 2.0.9
- * and earlier, as earlier versions did not queue rendering commands at all,
- * instead flushing them to the OS immediately.
+ * This is for a very-specific case: if you are using SDL's render API, and
+ * you plan to make OpenGL/D3D/whatever calls in addition to SDL render API
+ * calls. If this applies, you should call SDL_RenderFlush() between calls to
+ * SDL's render API and the low-level API you're using in cooperation.
+ *
+ * In all other cases, you can ignore this function.
  *
  * \param renderer the rendering context
  * \returns 0 on success or a negative error code on failure; call
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index aedde6af332c..533a2fe524c5 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -265,11 +265,6 @@ static int FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
     return 0;
 }
 
-static SDL_INLINE int FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
-{
-    return renderer->batching ? 0 : FlushRenderCommands(renderer);
-}
-
 int SDL_RenderFlush(SDL_Renderer *renderer)
 {
     return FlushRenderCommands(renderer);
@@ -813,7 +808,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
     const char *name = SDL_GetStringProperty(props, "name", NULL);
     SDL_Renderer *renderer = NULL;
     const int n = SDL_GetNumRenderDrivers();
-    SDL_bool batching = SDL_TRUE;
     const char *hint;
     int i;
 
@@ -855,9 +849,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
             if (SDL_strcasecmp(name, driver->info.name) == 0) {
                 /* Create a new renderer instance */
                 renderer = driver->CreateRenderer(window, props);
-                if (renderer) {
-                    batching = SDL_FALSE;
-                }
                 break;
             }
         }
@@ -890,14 +881,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
 
     VerifyDrawQueueFunctions(renderer);
 
-    /* let app/user override batching decisions. */
-    if (renderer->always_batch) {
-        batching = SDL_TRUE;
-    } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
-        batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
-    }
-
-    renderer->batching = batching;
     renderer->magic = &SDL_renderer_magic;
     renderer->window = window;
     renderer->target_mutex = SDL_CreateMutex();
@@ -2106,7 +2089,7 @@ static int SDL_SetRenderTargetInternal(SDL_Renderer *renderer, SDL_Texture *text
     }
 
     /* All set! */
-    return FlushRenderCommandsIfNotBatching(renderer);
+    return 0;
 }
 
 int SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
@@ -2506,7 +2489,6 @@ int SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event
 
 int SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect)
 {
-    int retval;
     CHECK_RENDERER_MAGIC(renderer, -1);
 
     if (rect) {
@@ -2520,8 +2502,7 @@ int SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect)
         renderer->view->viewport.w = -1;
         renderer->view->viewport.h = -1;
     }
-    retval = QueueCmdSetViewport(renderer);
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return QueueCmdSetViewport(renderer);
 }
 
 int SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect)
@@ -2563,7 +2544,6 @@ static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect)
 
 int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
 {
-    int retval;
     CHECK_RENDERER_MAGIC(renderer, -1)
 
     if (rect && rect->w >= 0 && rect->h >= 0) {
@@ -2577,8 +2557,7 @@ int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
         SDL_zero(renderer->view->clip_rect);
     }
 
-    retval = QueueCmdSetClipRect(renderer);
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return QueueCmdSetClipRect(renderer);
 }
 
 int SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect)
@@ -2617,7 +2596,7 @@ int SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY)
     /* The scale affects the existing viewport and clip rectangle */
     retval += QueueCmdSetViewport(renderer);
     retval += QueueCmdSetClipRect(renderer);
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY)
@@ -2687,7 +2666,7 @@ int SDL_RenderClear(SDL_Renderer *renderer)
     int retval;
     CHECK_RENDERER_MAGIC(renderer, -1);
     retval = QueueCmdClear(renderer);
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderPoint(SDL_Renderer *renderer, float x, float y)
@@ -2753,7 +2732,7 @@ int SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count
     } else {
         retval = QueueCmdDrawPoints(renderer, points, count);
     }
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2)
@@ -3072,7 +3051,7 @@ int SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count)
         retval = QueueCmdDrawLines(renderer, points, count);
     }
 
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect)
@@ -3181,7 +3160,7 @@ int SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int coun
 
     SDL_small_free(frects, isstack);
 
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)
@@ -3286,7 +3265,7 @@ int SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FR
 
         retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect);
     }
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture,
@@ -3441,7 +3420,7 @@ int SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture,
                                 renderer->view->scale.x,
                                 renderer->view->scale.y);
     }
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderGeometry(SDL_Renderer *renderer,
@@ -3798,8 +3777,6 @@ static int SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer,
                                           renderer->view->scale.y);
                 if (retval < 0) {
                     goto end;
-                } else {
-                    FlushRenderCommandsIfNotBatching(renderer);
                 }
             }
 
@@ -3821,8 +3798,6 @@ static int SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer,
                                   renderer->view->scale.y);
         if (retval < 0) {
             goto end;
-        } else {
-            FlushRenderCommandsIfNotBatching(renderer);
         }
     }
 
@@ -3944,7 +3919,7 @@ int SDL_RenderGeometryRaw(SDL_Renderer *renderer,
                               renderer->view->scale.x,
                               renderer->view->scale.y);
 
-    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
+    return retval;
 }
 
 int SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, Uint32 format, void *pixels, int pitch)
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 187073de1309..54d2d151484e 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -254,8 +254,6 @@ struct SDL_Renderer
     SDL_Color color;         /**< Color for drawing operations values */
     SDL_BlendMode blendMode; /**< The drawing blend mode */
 
-    SDL_bool always_batch;
-    SDL_bool batching;
     SDL_RenderCommand *render_commands;
     SDL_RenderCommand *render_commands_tail;
     SDL_RenderCommand *render_commands_pool;
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 285d42018ec1..dc2b6124b609 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -1917,8 +1917,6 @@ in case we want to use it later (recreating the renderer)
         renderer->info = METAL_RenderDriver.info;
         renderer->info.flags = SDL_RENDERER_ACCELERATED;
 
-        renderer->always_batch = SDL_TRUE;
-
 #if (defined(__MACOS__) && defined(MAC_OS_X_VERSION_10_13)) || TARGET_OS_MACCATALYST
         if (@available(macOS 10.13, *)) {
             data.mtllayer.displaySyncEnabled = SDL_GetBooleanProperty(create_props, "present_vsync", SDL_FALSE);
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 45928628df90..80a7865889f8 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -228,7 +228,6 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
             }
             state->renderdriver = argv[index];
             SDL_SetHint(SDL_HINT_RENDER_DRIVER, state->renderdriver);
-            SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
             return 2;
         }
         if (SDL_strcasecmp(argv[index], "--gldebug") == 0) {