SDL: Change the batch if the color scale changes

From 4e5ba722fd6de78dbf8636ae9777049d72c940ed Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 9 Nov 2025 15:01:37 -0800
Subject: [PATCH] Change the batch if the color scale changes

The color scale is a shader constant so it can be applied in linear space
---
 src/render/direct3d11/SDL_render_d3d11.c | 8 ++++++--
 src/render/direct3d12/SDL_render_d3d12.c | 8 ++++++--
 src/render/gpu/SDL_render_gpu.c          | 4 ++++
 src/render/metal/SDL_render_metal.m      | 8 ++++++--
 src/render/vulkan/SDL_render_vulkan.c    | 8 ++++++--
 5 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index 7788ae7481487..72d5c2fe15a56 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -2646,6 +2646,7 @@ static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                 // let's group non joined lines
                 SDL_RenderCommand *finalcmd = cmd;
                 SDL_RenderCommand *nextcmd;
+                float thiscolorscale = cmd->data.draw.color_scale;
                 SDL_BlendMode thisblend = cmd->data.draw.blend;
 
                 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
@@ -2658,7 +2659,8 @@ static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                         break; // can't go any further on this draw call, different render command up next.
                     } else if (nextcmd->data.draw.count != 2) {
                         break; // can't go any further on this draw call, those are joined lines
-                    } else if (nextcmd->data.draw.blend != thisblend) {
+                    } else if (nextcmd->data.draw.blend != thisblend ||
+                               nextcmd->data.draw.color_scale != thiscolorscale) {
                         break; // can't go any further on this draw call, different blendmode copy up next.
                     } else {
                         finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
@@ -2693,6 +2695,7 @@ static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
         {
             /* as long as we have the same copy command in a row, with the
                same texture, we can combine them all into a single draw call. */
+            float thiscolorscale = cmd->data.draw.color_scale;
             SDL_Texture *thistexture = cmd->data.draw.texture;
             SDL_BlendMode thisblend = cmd->data.draw.blend;
             SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
@@ -2716,7 +2719,8 @@ static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                            nextcmd->data.draw.texture_scale_mode != thisscalemode ||
                            nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
                            nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
-                           nextcmd->data.draw.blend != thisblend) {
+                           nextcmd->data.draw.blend != thisblend ||
+                           nextcmd->data.draw.color_scale != thiscolorscale) {
                     break; // can't go any further on this draw call, different texture/blendmode copy up next.
                 } else {
                     finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index 1e1951a18026d..ec0a9eac7661d 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -3109,6 +3109,7 @@ static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                 // let's group non joined lines
                 SDL_RenderCommand *finalcmd = cmd;
                 SDL_RenderCommand *nextcmd;
+                float thiscolorscale = cmd->data.draw.color_scale;
                 SDL_BlendMode thisblend = cmd->data.draw.blend;
 
                 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
@@ -3121,7 +3122,8 @@ static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                         break; // can't go any further on this draw call, different render command up next.
                     } else if (nextcmd->data.draw.count != 2) {
                         break; // can't go any further on this draw call, those are joined lines
-                    } else if (nextcmd->data.draw.blend != thisblend) {
+                    } else if (nextcmd->data.draw.blend != thisblend ||
+                               nextcmd->data.draw.color_scale != thiscolorscale) {
                         break; // can't go any further on this draw call, different blendmode copy up next.
                     } else {
                         finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
@@ -3161,6 +3163,7 @@ static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
         {
             /* as long as we have the same copy command in a row, with the
                same texture, we can combine them all into a single draw call. */
+            float thiscolorscale = cmd->data.draw.color_scale;
             SDL_Texture *thistexture = cmd->data.draw.texture;
             SDL_BlendMode thisblend = cmd->data.draw.blend;
             SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
@@ -3184,7 +3187,8 @@ static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                            nextcmd->data.draw.texture_scale_mode != thisscalemode ||
                            nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
                            nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
-                           nextcmd->data.draw.blend != thisblend) {
+                           nextcmd->data.draw.blend != thisblend ||
+                           nextcmd->data.draw.color_scale != thiscolorscale) {
                     break; // can't go any further on this draw call, different texture/blendmode copy up next.
                 } else {
                     finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c
index d2f729017946f..74960319f51ab 100644
--- a/src/render/gpu/SDL_render_gpu.c
+++ b/src/render/gpu/SDL_render_gpu.c
@@ -1247,6 +1247,7 @@ static bool GPU_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd,
                 // let's group non joined lines
                 SDL_RenderCommand *finalcmd = cmd;
                 SDL_RenderCommand *nextcmd;
+                float thiscolorscale = cmd->data.draw.color_scale;
                 SDL_BlendMode thisblend = cmd->data.draw.blend;
                 SDL_GPURenderState *thisrenderstate = cmd->data.draw.gpu_render_state;
 
@@ -1261,6 +1262,7 @@ static bool GPU_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd,
                     } else if (nextcmd->data.draw.count != 2) {
                         break; // can't go any further on this draw call, those are joined lines
                     } else if (nextcmd->data.draw.blend != thisblend ||
+                               nextcmd->data.draw.color_scale != thiscolorscale ||
                                nextcmd->data.draw.gpu_render_state != thisrenderstate) {
                         break; // can't go any further on this draw call, different blendmode copy up next.
                     } else {
@@ -1280,6 +1282,7 @@ static bool GPU_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd,
         {
             /* as long as we have the same copy command in a row, with the
                same texture, we can combine them all into a single draw call. */
+            float thiscolorscale = cmd->data.draw.color_scale;
             SDL_Texture *thistexture = cmd->data.draw.texture;
             SDL_BlendMode thisblend = cmd->data.draw.blend;
             SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
@@ -1305,6 +1308,7 @@ static bool GPU_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd,
                            nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
                            nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
                            nextcmd->data.draw.blend != thisblend ||
+                           nextcmd->data.draw.color_scale != thiscolorscale ||
                            nextcmd->data.draw.gpu_render_state != thisrenderstate) {
                     break; // can't go any further on this draw call, different texture/blendmode copy up next.
                 } else {
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index bd1aaf9d69117..948678859907b 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -1795,6 +1795,7 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                         // let's group non joined lines
                         SDL_RenderCommand *finalcmd = cmd;
                         SDL_RenderCommand *nextcmd;
+                        float thiscolorscale = cmd->data.draw.color_scale;
                         SDL_BlendMode thisblend = cmd->data.draw.blend;
 
                         for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
@@ -1807,7 +1808,8 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                                 break; // can't go any further on this draw call, different render command up next.
                             } else if (nextcmd->data.draw.count != 2) {
                                 break; // can't go any further on this draw call, those are joined lines
-                            } else if (nextcmd->data.draw.blend != thisblend) {
+                            } else if (nextcmd->data.draw.blend != thisblend ||
+                                       nextcmd->data.draw.color_scale != thiscolorscale) {
                                 break; // can't go any further on this draw call, different blendmode copy up next.
                             } else {
                                 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
@@ -1834,6 +1836,7 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
             case SDL_RENDERCMD_DRAW_POINTS:
             case SDL_RENDERCMD_GEOMETRY:
             {
+                float thiscolorscale = cmd->data.draw.color_scale;
                 SDL_Texture *thistexture = cmd->data.draw.texture;
                 SDL_BlendMode thisblend = cmd->data.draw.blend;
                 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
@@ -1855,7 +1858,8 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
                                nextcmd->data.draw.texture_scale_mode != thisscalemode ||
                                nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
                                nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
-                               nextcmd->data.draw.blend != thisblend) {
+                               nextcmd->data.draw.blend != thisblend ||
+                               nextcmd->data.draw.color_scale != thiscolorscale) {
                         break; // can't go any further on this draw call, different texture/blendmode copy up next.
                     } else {
                         finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
index 9838e0ef54c10..3dbaa510a5742 100644
--- a/src/render/vulkan/SDL_render_vulkan.c
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -4096,6 +4096,7 @@ static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cm
                 // let's group non joined lines
                 SDL_RenderCommand *finalcmd = cmd;
                 SDL_RenderCommand *nextcmd;
+                float thiscolorscale = cmd->data.draw.color_scale;
                 SDL_BlendMode thisblend = cmd->data.draw.blend;
 
                 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
@@ -4108,7 +4109,8 @@ static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cm
                         break; // can't go any further on this draw call, different render command up next.
                     } else if (nextcmd->data.draw.count != 2) {
                         break; // can't go any further on this draw call, those are joined lines
-                    } else if (nextcmd->data.draw.blend != thisblend) {
+                    } else if (nextcmd->data.draw.blend != thisblend ||
+                               nextcmd->data.draw.color_scale != thiscolorscale) {
                         break; // can't go any further on this draw call, different blendmode copy up next.
                     } else {
                         finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
@@ -4149,6 +4151,7 @@ static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cm
         {
             /* as long as we have the same copy command in a row, with the
                same texture, we can combine them all into a single draw call. */
+            float thiscolorscale = cmd->data.draw.color_scale;
             SDL_Texture *thistexture = cmd->data.draw.texture;
             SDL_BlendMode thisblend = cmd->data.draw.blend;
             SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
@@ -4172,7 +4175,8 @@ static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cm
                            nextcmd->data.draw.texture_scale_mode != thisscalemode ||
                            nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
                            nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
-                           nextcmd->data.draw.blend != thisblend) {
+                           nextcmd->data.draw.blend != thisblend ||
+                           nextcmd->data.draw.color_scale != thiscolorscale) {
                     break; // can't go any further on this draw call, different texture/blendmode copy up next.
                 } else {
                     finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.