SDL: Add SDL_SetGPUBlendConstants, SDL_SetGPUStencilReference (#10704)

From 2d4eb29c379c02f9a9a8031e61b8b060d420e419 Mon Sep 17 00:00:00 2001
From: Caleb Cornett <[EMAIL REDACTED]>
Date: Thu, 5 Sep 2024 17:41:23 -0500
Subject: [PATCH] Add SDL_SetGPUBlendConstants, SDL_SetGPUStencilReference
 (#10704)

---
 include/SDL3/SDL_gpu.h            |  30 ++++-
 src/dynapi/SDL_dynapi.sym         |   2 +
 src/dynapi/SDL_dynapi_overrides.h |   2 +
 src/dynapi/SDL_dynapi_procs.h     |   2 +
 src/gpu/SDL_gpu.c                 |  41 ++++++-
 src/gpu/SDL_sysgpu.h              |  10 ++
 src/gpu/d3d11/SDL_gpu_d3d11.c     | 181 ++++++++++++++++++------------
 src/gpu/d3d12/SDL_gpu_d3d12.c     |  48 ++++----
 src/gpu/metal/SDL_gpu_metal.m     | 161 ++++++++++++++------------
 src/gpu/vulkan/SDL_gpu_vulkan.c   |  95 +++++++++++++---
 10 files changed, 383 insertions(+), 189 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 211d298f1073b..09f46738b4167 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -1207,8 +1207,8 @@ typedef struct SDL_GPUDepthStencilState
     SDL_GPUStencilOpState frontStencilState;
     Uint8 compareMask;
     Uint8 writeMask;
-    Uint8 reference;
     Uint8 padding2;
+    Uint8 padding3;
 } SDL_GPUDepthStencilState;
 
 typedef struct SDL_GPUColorAttachmentDescription
@@ -1238,7 +1238,6 @@ typedef struct SDL_GPUGraphicsPipelineCreateInfo
     SDL_GPUMultisampleState multisampleState;
     SDL_GPUDepthStencilState depthStencilState;
     SDL_GPUGraphicsPipelineAttachmentInfo attachmentInfo;
-    float blendConstants[4];
 
     SDL_PropertiesID props;
 } SDL_GPUGraphicsPipelineCreateInfo;
@@ -2140,6 +2139,33 @@ extern SDL_DECLSPEC void SDLCALL SDL_SetGPUScissor(
     SDL_GPURenderPass *renderPass,
     const SDL_Rect *scissor);
 
+/**
+ * Sets the current blend constants on a command buffer.
+ *
+ * \param renderPass a render pass handle.
+ * \param blendConstants the blend constant color.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GPU_BLENDFACTOR_CONSTANT_COLOR
+ * \sa SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_SetGPUBlendConstants(
+    SDL_GPURenderPass *renderPass,
+    SDL_FColor blendConstants);
+
+/**
+ * Sets the current stencil reference value on a command buffer.
+ *
+ * \param renderPass a render pass handle.
+ * \param reference the stencil reference value to set.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_SetGPUStencilReference(
+    SDL_GPURenderPass *renderPass,
+    Uint8 reference);
+
 /**
  * Binds vertex buffers on a command buffer for use with subsequent draw
  * calls.
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index f3b8de3c440a4..35ef0041f165f 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -778,8 +778,10 @@ SDL3_0.0.0 {
     SDL_SetEventEnabled;
     SDL_SetEventFilter;
     SDL_SetFloatProperty;
+    SDL_SetGPUBlendConstants;
     SDL_SetGPUBufferName;
     SDL_SetGPUScissor;
+    SDL_SetGPUStencilReference;
     SDL_SetGPUSwapchainParameters;
     SDL_SetGPUTextureName;
     SDL_SetGPUViewport;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 66e618b7789df..d04960d01066a 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -803,8 +803,10 @@
 #define SDL_SetEventEnabled SDL_SetEventEnabled_REAL
 #define SDL_SetEventFilter SDL_SetEventFilter_REAL
 #define SDL_SetFloatProperty SDL_SetFloatProperty_REAL
+#define SDL_SetGPUBlendConstants SDL_SetGPUBlendConstants_REAL
 #define SDL_SetGPUBufferName SDL_SetGPUBufferName_REAL
 #define SDL_SetGPUScissor SDL_SetGPUScissor_REAL
+#define SDL_SetGPUStencilReference SDL_SetGPUStencilReference_REAL
 #define SDL_SetGPUSwapchainParameters SDL_SetGPUSwapchainParameters_REAL
 #define SDL_SetGPUTextureName SDL_SetGPUTextureName_REAL
 #define SDL_SetGPUViewport SDL_SetGPUViewport_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 54f542f37f643..0d1ed3b05e8d0 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -813,8 +813,10 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_SetCursor,(SDL_Cursor *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_SetEventEnabled,(Uint32 a, SDL_bool b),(a,b),)
 SDL_DYNAPI_PROC(void,SDL_SetEventFilter,(SDL_EventFilter a, void *b),(a,b),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetFloatProperty,(SDL_PropertiesID a, const char *b, float c),(a,b,c),return)
+SDL_DYNAPI_PROC(void,SDL_SetGPUBlendConstants,(SDL_GPURenderPass *a, SDL_FColor b),(a,b),)
 SDL_DYNAPI_PROC(void,SDL_SetGPUBufferName,(SDL_GPUDevice *a, SDL_GPUBuffer *b, const char *c),(a,b,c),)
 SDL_DYNAPI_PROC(void,SDL_SetGPUScissor,(SDL_GPURenderPass *a, const SDL_Rect *b),(a,b),)
+SDL_DYNAPI_PROC(void,SDL_SetGPUStencilReference,(SDL_GPURenderPass *a, Uint8 b),(a,b),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetGPUSwapchainParameters,(SDL_GPUDevice *a, SDL_Window *b, SDL_GPUSwapchainComposition c, SDL_GPUPresentMode d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(void,SDL_SetGPUTextureName,(SDL_GPUDevice *a, SDL_GPUTexture *b, const char *c),(a,b,c),)
 SDL_DYNAPI_PROC(void,SDL_SetGPUViewport,(SDL_GPURenderPass *a, const SDL_GPUViewport *b),(a,b),)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index c737b082041ff..8f8a3593fec15 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -194,11 +194,6 @@ SDL_GPUGraphicsPipeline *SDL_GPU_FetchBlitPipeline(
 
     blitPipelineCreateInfo.primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
 
-    blitPipelineCreateInfo.blendConstants[0] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[1] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[2] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[3] = 1.0f;
-
     pipeline = SDL_CreateGPUGraphicsPipeline(
         device,
         &blitPipelineCreateInfo);
@@ -1281,6 +1276,42 @@ void SDL_SetGPUScissor(
         scissor);
 }
 
+void SDL_SetGPUBlendConstants(
+    SDL_GPURenderPass *renderPass,
+    SDL_FColor blendConstants)
+{
+    if (renderPass == NULL) {
+        SDL_InvalidParamError("renderPass");
+        return;
+    }
+
+    if (RENDERPASS_DEVICE->debugMode) {
+        CHECK_RENDERPASS
+    }
+
+    RENDERPASS_DEVICE->SetBlendConstants(
+        RENDERPASS_COMMAND_BUFFER,
+        blendConstants);
+}
+
+void SDL_SetGPUStencilReference(
+    SDL_GPURenderPass *renderPass,
+    Uint8 reference)
+{
+    if (renderPass == NULL) {
+        SDL_InvalidParamError("renderPass");
+        return;
+    }
+
+    if (RENDERPASS_DEVICE->debugMode) {
+        CHECK_RENDERPASS
+    }
+
+    RENDERPASS_DEVICE->SetStencilReference(
+        RENDERPASS_COMMAND_BUFFER,
+        reference);
+}
+
 void SDL_BindGPUVertexBuffers(
     SDL_GPURenderPass *renderPass,
     Uint32 firstBinding,
diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h
index 7bb63198381ae..f9585e5b4befd 100644
--- a/src/gpu/SDL_sysgpu.h
+++ b/src/gpu/SDL_sysgpu.h
@@ -398,6 +398,14 @@ struct SDL_GPUDevice
         SDL_GPUCommandBuffer *commandBuffer,
         const SDL_Rect *scissor);
 
+    void (*SetBlendConstants)(
+        SDL_GPUCommandBuffer *commandBuffer,
+        SDL_FColor blendConstants);
+
+    void (*SetStencilReference)(
+        SDL_GPUCommandBuffer *commandBuffer,
+        Uint8 reference);
+
     void (*BindVertexBuffers)(
         SDL_GPUCommandBuffer *commandBuffer,
         Uint32 firstBinding,
@@ -716,6 +724,8 @@ struct SDL_GPUDevice
     ASSIGN_DRIVER_FUNC(BindGraphicsPipeline, name)          \
     ASSIGN_DRIVER_FUNC(SetViewport, name)                   \
     ASSIGN_DRIVER_FUNC(SetScissor, name)                    \
+    ASSIGN_DRIVER_FUNC(SetBlendConstants, name)             \
+    ASSIGN_DRIVER_FUNC(SetStencilReference, name)           \
     ASSIGN_DRIVER_FUNC(BindVertexBuffers, name)             \
     ASSIGN_DRIVER_FUNC(BindIndexBuffer, name)               \
     ASSIGN_DRIVER_FUNC(BindVertexSamplers, name)            \
diff --git a/src/gpu/d3d11/SDL_gpu_d3d11.c b/src/gpu/d3d11/SDL_gpu_d3d11.c
index 61bb1c67e050e..741a593b9551b 100644
--- a/src/gpu/d3d11/SDL_gpu_d3d11.c
+++ b/src/gpu/d3d11/SDL_gpu_d3d11.c
@@ -476,7 +476,6 @@ typedef struct D3D11Shader
 
 typedef struct D3D11GraphicsPipeline
 {
-    float blendConstants[4];
     Sint32 numColorAttachments;
     DXGI_FORMAT colorAttachmentFormats[MAX_COLOR_TARGET_BINDINGS];
     ID3D11BlendState *colorAttachmentBlendState;
@@ -486,7 +485,6 @@ typedef struct D3D11GraphicsPipeline
     Uint8 hasDepthStencilAttachment;
     DXGI_FORMAT depthStencilAttachmentFormat;
     ID3D11DepthStencilState *depthStencilState;
-    Uint8 stencilRef;
 
     SDL_GPUPrimitiveType primitiveType;
     ID3D11RasterizerState *rasterizerState;
@@ -615,6 +613,8 @@ typedef struct D3D11CommandBuffer
 
     // Render Pass
     D3D11GraphicsPipeline *graphicsPipeline;
+    Uint8 stencilRef;
+    SDL_FColor blendConstants;
 
     // Render Pass MSAA resolve
     D3D11Texture *colorTargetResolveTexture[MAX_COLOR_TARGET_BINDINGS];
@@ -1541,11 +1541,6 @@ static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline(
         pipeline->colorAttachmentFormats[i] = SDLToD3D11_TextureFormat[pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].format];
     }
 
-    pipeline->blendConstants[0] = pipelineCreateInfo->blendConstants[0];
-    pipeline->blendConstants[1] = pipelineCreateInfo->blendConstants[1];
-    pipeline->blendConstants[2] = pipelineCreateInfo->blendConstants[2];
-    pipeline->blendConstants[3] = pipelineCreateInfo->blendConstants[3];
-
     // Multisample
 
     pipeline->multisampleState = pipelineCreateInfo->multisampleState;
@@ -1558,7 +1553,6 @@ static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline(
 
     pipeline->hasDepthStencilAttachment = pipelineCreateInfo->attachmentInfo.hasDepthStencilAttachment;
     pipeline->depthStencilAttachmentFormat = SDLToD3D11_TextureFormat[pipelineCreateInfo->attachmentInfo.depthStencilFormat];
-    pipeline->stencilRef = pipelineCreateInfo->depthStencilState.reference;
 
     // Rasterizer
 
@@ -3204,6 +3198,8 @@ static SDL_GPUCommandBuffer *D3D11_AcquireCommandBuffer(
 
     commandBuffer = D3D11_INTERNAL_GetInactiveCommandBufferFromPool(renderer);
     commandBuffer->graphicsPipeline = NULL;
+    commandBuffer->stencilRef = 0;
+    commandBuffer->blendConstants = (SDL_FColor){ 1.0f, 1.0f, 1.0f, 1.0f };
     commandBuffer->computePipeline = NULL;
     for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
         commandBuffer->colorTargetResolveTexture[i] = NULL;
@@ -3384,6 +3380,78 @@ static void D3D11_INTERNAL_PushUniformData(
     }
 }
 
+static void D3D11_SetViewport(
+    SDL_GPUCommandBuffer *commandBuffer,
+    const SDL_GPUViewport *viewport)
+{
+    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
+    D3D11_VIEWPORT vp = {
+        viewport->x,
+        viewport->y,
+        viewport->w,
+        viewport->h,
+        viewport->minDepth,
+        viewport->maxDepth
+    };
+
+    ID3D11DeviceContext_RSSetViewports(
+        d3d11CommandBuffer->context,
+        1,
+        &vp);
+}
+
+static void D3D11_SetScissor(
+    SDL_GPUCommandBuffer *commandBuffer,
+    const SDL_Rect *scissor)
+{
+    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
+    D3D11_RECT rect = {
+        scissor->x,
+        scissor->y,
+        scissor->x + scissor->w,
+        scissor->y + scissor->h
+    };
+
+    ID3D11DeviceContext_RSSetScissorRects(
+        d3d11CommandBuffer->context,
+        1,
+        &rect);
+}
+
+static void D3D11_SetBlendConstants(
+    SDL_GPUCommandBuffer *commandBuffer,
+    SDL_FColor blendConstants)
+{
+    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
+    FLOAT blendFactor[4] = { blendConstants.r, blendConstants.g, blendConstants.b, blendConstants.a };
+
+    d3d11CommandBuffer->blendConstants = blendConstants;
+
+    if (d3d11CommandBuffer->graphicsPipeline != NULL) {
+        ID3D11DeviceContext_OMSetBlendState(
+            d3d11CommandBuffer->context,
+            d3d11CommandBuffer->graphicsPipeline->colorAttachmentBlendState,
+            blendFactor,
+            d3d11CommandBuffer->graphicsPipeline->multisampleState.sampleMask);
+    }
+}
+
+static void D3D11_SetStencilReference(
+    SDL_GPUCommandBuffer *commandBuffer,
+    Uint8 reference)
+{
+    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
+
+    d3d11CommandBuffer->stencilRef = reference;
+
+    if (d3d11CommandBuffer->graphicsPipeline != NULL) {
+        ID3D11DeviceContext_OMSetDepthStencilState(
+            d3d11CommandBuffer->context,
+            d3d11CommandBuffer->graphicsPipeline->depthStencilState,
+            reference);
+    }
+}
+
 static void D3D11_BeginRenderPass(
     SDL_GPUCommandBuffer *commandBuffer,
     const SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
@@ -3396,8 +3464,8 @@ static void D3D11_BeginRenderPass(
     ID3D11DepthStencilView *dsv = NULL;
     Uint32 vpWidth = SDL_MAX_UINT32;
     Uint32 vpHeight = SDL_MAX_UINT32;
-    D3D11_VIEWPORT viewport;
-    D3D11_RECT scissorRect;
+    SDL_GPUViewport viewport;
+    SDL_Rect scissorRect;
 
     d3d11CommandBuffer->needVertexSamplerBind = true;
     d3d11CommandBuffer->needVertexResourceBind = true;
@@ -3524,28 +3592,34 @@ static void D3D11_BeginRenderPass(
         }
     }
 
-    // Set default viewport and scissor state
-    viewport.TopLeftX = 0;
-    viewport.TopLeftY = 0;
-    viewport.Width = (FLOAT)vpWidth;
-    viewport.Height = (FLOAT)vpHeight;
-    viewport.MinDepth = 0;
-    viewport.MaxDepth = 1;
+    // Set sensible default states
+    viewport.x = 0;
+    viewport.y = 0;
+    viewport.w = (float)vpWidth;
+    viewport.h = (float)vpHeight;
+    viewport.minDepth = 0;
+    viewport.maxDepth = 1;
 
-    ID3D11DeviceContext_RSSetViewports(
-        d3d11CommandBuffer->context,
-        1,
+    D3D11_SetViewport(
+        commandBuffer,
         &viewport);
 
-    scissorRect.left = 0;
-    scissorRect.right = (LONG)viewport.Width;
-    scissorRect.top = 0;
-    scissorRect.bottom = (LONG)viewport.Height;
+    scissorRect.x = 0;
+    scissorRect.y = 0;
+    scissorRect.w = (int)vpWidth;
+    scissorRect.h = (int)vpHeight;
 
-    ID3D11DeviceContext_RSSetScissorRects(
-        d3d11CommandBuffer->context,
-        1,
+    D3D11_SetScissor(
+        commandBuffer,
         &scissorRect);
+
+    D3D11_SetStencilReference(
+        commandBuffer,
+        0);
+
+    D3D11_SetBlendConstants(
+        commandBuffer,
+        (SDL_FColor){ 1.0f, 1.0f, 1.0f, 1.0f });
 }
 
 static void D3D11_BindGraphicsPipeline(
@@ -3554,19 +3628,25 @@ static void D3D11_BindGraphicsPipeline(
 {
     D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
     D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline *)graphicsPipeline;
+    FLOAT blendFactor[4] = {
+        d3d11CommandBuffer->blendConstants.r,
+        d3d11CommandBuffer->blendConstants.g,
+        d3d11CommandBuffer->blendConstants.b,
+        d3d11CommandBuffer->blendConstants.a
+    };
 
     d3d11CommandBuffer->graphicsPipeline = pipeline;
 
     ID3D11DeviceContext_OMSetBlendState(
         d3d11CommandBuffer->context,
         pipeline->colorAttachmentBlendState,
-        pipeline->blendConstants,
+        blendFactor,
         pipeline->multisampleState.sampleMask);
 
     ID3D11DeviceContext_OMSetDepthStencilState(
         d3d11CommandBuffer->context,
         pipeline->depthStencilState,
-        pipeline->stencilRef);
+        d3d11CommandBuffer->stencilRef);
 
     ID3D11DeviceContext_IASetPrimitiveTopology(
         d3d11CommandBuffer->context,
@@ -3612,44 +3692,6 @@ static void D3D11_BindGraphicsPipeline(
     d3d11CommandBuffer->needFragmentUniformBufferBind = true;
 }
 
-static void D3D11_SetViewport(
-    SDL_GPUCommandBuffer *commandBuffer,
-    const SDL_GPUViewport *viewport)
-{
-    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
-    D3D11_VIEWPORT vp = {
-        viewport->x,
-        viewport->y,
-        viewport->w,
-        viewport->h,
-        viewport->minDepth,
-        viewport->maxDepth
-    };
-
-    ID3D11DeviceContext_RSSetViewports(
-        d3d11CommandBuffer->context,
-        1,
-        &vp);
-}
-
-static void D3D11_SetScissor(
-    SDL_GPUCommandBuffer *commandBuffer,
-    const SDL_Rect *scissor)
-{
-    D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
-    D3D11_RECT rect = {
-        scissor->x,
-        scissor->y,
-        scissor->x + scissor->w,
-        scissor->y + scissor->h
-    };
-
-    ID3D11DeviceContext_RSSetScissorRects(
-        d3d11CommandBuffer->context,
-        1,
-        &rect);
-}
-
 static void D3D11_BindVertexBuffers(
     SDL_GPUCommandBuffer *commandBuffer,
     Uint32 firstBinding,
@@ -5786,11 +5828,6 @@ static void D3D11_INTERNAL_InitBlitPipelines(
 
     blitPipelineCreateInfo.primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
 
-    blitPipelineCreateInfo.blendConstants[0] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[1] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[2] = 1.0f;
-    blitPipelineCreateInfo.blendConstants[3] = 1.0f;
-
     blitPipeline = D3D11_CreateGraphicsPipeline(
         (SDL_GPURenderer *)renderer,
         &blitPipelineCreateInfo);
diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c
index f219e75cc6d27..c3a98dacb0e9a 100644
--- a/src/gpu/d3d12/SDL_gpu_d3d12.c
+++ b/src/gpu/d3d12/SDL_gpu_d3d12.c
@@ -775,9 +775,6 @@ struct D3D12GraphicsPipeline
 
     Uint32 vertexStrides[MAX_BUFFER_BINDINGS];
 
-    float blendConstants[4];
-    Uint8 stencilRef;
-
     Uint32 vertexSamplerCount;
     Uint32 vertexUniformBufferCount;
     Uint32 vertexStorageBufferCount;
@@ -2603,11 +2600,6 @@ static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline(
     }
 
     pipeline->primitiveType = pipelineCreateInfo->primitiveType;
-    pipeline->blendConstants[0] = pipelineCreateInfo->blendConstants[0];
-    pipeline->blendConstants[1] = pipelineCreateInfo->blendConstants[1];
-    pipeline->blendConstants[2] = pipelineCreateInfo->blendConstants[2];
-    pipeline->blendConstants[3] = pipelineCreateInfo->blendConstants[3];
-    pipeline->stencilRef = pipelineCreateInfo->depthStencilState.reference;
 
     pipeline->vertexSamplerCount = vertShader->samplerCount;
     pipeline->vertexStorageTextureCount = vertShader->storageTextureCount;
@@ -3611,6 +3603,23 @@ static void D3D12_SetScissor(
     ID3D12GraphicsCommandList_RSSetScissorRects(d3d12CommandBuffer->graphicsCommandList, 1, &scissorRect);
 }
 
+static void D3D12_SetBlendConstants(
+    SDL_GPUCommandBuffer *commandBuffer,
+    SDL_FColor blendConstants)
+{
+    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
+    FLOAT blendFactor[4] = { blendConstants.r, blendConstants.g, blendConstants.b, blendConstants.a };
+    ID3D12GraphicsCommandList_OMSetBlendFactor(d3d12CommandBuffer->graphicsCommandList, blendFactor);
+}
+
+static void D3D12_SetStencilReference(
+    SDL_GPUCommandBuffer *commandBuffer,
+    Uint8 reference
+) {
+    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
+    ID3D12GraphicsCommandList_OMSetStencilRef(d3d12CommandBuffer->graphicsCommandList, reference);
+}
+
 static D3D12TextureSubresource *D3D12_INTERNAL_FetchTextureSubresource(
     D3D12TextureContainer *container,
     Uint32 layer,
@@ -3783,7 +3792,6 @@ static void D3D12_BeginRenderPass(
     const SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
 {
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
-    /* D3D12Renderer *renderer = d3d12CommandBuffer->renderer; */
 
     Uint32 framebufferWidth = SDL_MAX_UINT32;
     Uint32 framebufferHeight = SDL_MAX_UINT32;
@@ -3915,7 +3923,7 @@ static void D3D12_BeginRenderPass(
         false,
         (depthStencilAttachmentInfo == NULL) ? NULL : &dsv);
 
-    // Set sensible default viewport state
+    // Set sensible default states
     SDL_GPUViewport defaultViewport;
     defaultViewport.x = 0;
     defaultViewport.y = 0;
@@ -3937,6 +3945,14 @@ static void D3D12_BeginRenderPass(
     D3D12_SetScissor(
         commandBuffer,
         &defaultScissor);
+
+    D3D12_SetStencilReference(
+        commandBuffer,
+        0);
+
+    D3D12_SetBlendConstants(
+        commandBuffer,
+        (SDL_FColor){ 1.0f, 1.0f, 1.0f, 1.0f });
 }
 
 static void D3D12_INTERNAL_TrackUniformBuffer(
@@ -4120,21 +4136,9 @@ static void D3D12_BindGraphicsPipeline(
 
     // Set the pipeline state
     ID3D12GraphicsCommandList_SetPipelineState(d3d12CommandBuffer->graphicsCommandList, pipeline->pipelineState);
-
     ID3D12GraphicsCommandList_SetGraphicsRootSignature(d3d12CommandBuffer->graphicsCommandList, pipeline->rootSignature->handle);
-
     ID3D12GraphicsCommandList_IASetPrimitiveTopology(d3d12CommandBuffer->graphicsCommandList, SDLToD3D12_PrimitiveType[pipeline->primitiveType]);
 
-    float blendFactor[4] = {
-        pipeline->blendConstants[0],
-        pipeline->blendConstants[1],
-        pipeline->blendConstants[2],
-        pipeline->blendConstants[3]
-    };
-    ID3D12GraphicsCommandList_OMSetBlendFactor(d3d12CommandBuffer->graphicsCommandList, blendFactor);
-
-    ID3D12GraphicsCommandList_OMSetStencilRef(d3d12CommandBuffer->graphicsCommandList, pipeline->stencilRef);
-
     // Mark that bindings are needed
     d3d12CommandBuffer->needVertexSamplerBind = true;
     d3d12CommandBuffer->needVertexStorageTextureBind = true;
diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m
index 7c1e36b599545..84eb1513ae5f0 100644
--- a/src/gpu/metal/SDL_gpu_metal.m
+++ b/src/gpu/metal/SDL_gpu_metal.m
@@ -81,14 +81,14 @@ static void METAL_ReleaseWindow(
     MTLPixelFormatABGR4Unorm,   // B4G4R4A4_UNORM
     MTLPixelFormatBGRA8Unorm,   // B8G8R8A8_UNORM
 #ifdef SDL_PLATFORM_MACOS
-    MTLPixelFormatBC1_RGBA,      // BC1_UNORM
-    MTLPixelFormatBC2_RGBA,      // BC2_UNORM
-    MTLPixelFormatBC3_RGBA,      // BC3_UNORM
-    MTLPixelFormatBC4_RUnorm,    // BC4_UNORM
-    MTLPixelFormatBC5_RGUnorm,   // BC5_UNORM
-    MTLPixelFormatBC7_RGBAUnorm, // BC7_UNORM
-    MTLPixelFormatBC6H_RGBFloat, // BC6H_FLOAT
-    MTLPixelFormatBC6H_RGBUfloat,// BC6H_UFLOAT
+    MTLPixelFormatBC1_RGBA,       // BC1_UNORM
+    MTLPixelFormatBC2_RGBA,       // BC2_UNORM
+    MTLPixelFormatBC3_RGBA,       // BC3_UNORM
+    MTLPixelFormatBC4_RUnorm,     // BC4_UNORM
+    MTLPixelFormatBC5_RGUnorm,    // BC5_UNORM
+    MTLPixelFormatBC7_RGBAUnorm,  // BC7_UNORM
+    MTLPixelFormatBC6H_RGBFloat,  // BC6H_FLOAT
+    MTLPixelFormatBC6H_RGBUfloat, // BC6H_UFLOAT
 #else
     MTLPixelFormatInvalid, // BC1_UNORM
     MTLPixelFormatInvalid, // BC2_UNORM
@@ -402,14 +402,12 @@ static MTLColorWriteMask SDLToMetal_ColorWriteMask(
 {
     id<MTLRenderPipelineState> handle;
 
-    float blendConstants[4];
     Uint32 sampleMask;
 
     SDL_GPURasterizerState rasterizerState;
     SDL_GPUPrimitiveType primitiveType;
 
     id<MTLDepthStencilState> depthStencilState;
-    Uint8 stencilReference;
 
     Uint32 vertexSamplerCount;
     Uint32 vertexUniformBufferCount;
@@ -1103,13 +1101,8 @@ static void METAL_ReleaseGraphicsPipeline(
 
         result = SDL_malloc(sizeof(MetalGraphicsPipeline));
         result->handle = pipelineState;
-        result->blendConstants[0] = pipelineCreateInfo->blendConstants[0];
-        result->blendConstants[1] = pipelineCreateInfo->blendConstants[1];
-        result->blendConstants[2] = pipelineCreateInfo->blendConstants[2];
-        result->blendConstants[3] = pipelineCreateInfo->blendConstants[3];
         result->sampleMask = pipelineCreateInfo->multisampleState.sampleMask;
         result->depthStencilState = depthStencilState;
-        result->stencilReference = pipelineCreateInfo->depthStencilState.reference;
         result->rasterizerState = pipelineCreateInfo->rasterizerState;
         result->primitiveType = pipelineCreateInfo->primitiveType;
         result->vertexSamplerCount = vertexShader->samplerCount;
@@ -2087,6 +2080,65 @@ static void METAL_INTERNAL_ReturnUniformBufferToPool(
     uniformBuffer->drawOffset = 0;
 }
 
+static void METAL_SetViewport(
+    SDL_GPUCommandBuffer *commandBuffer,
+    const SDL_GPUViewport *viewport)
+{
+    @autoreleasepool {
+        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
+        MTLViewport metalViewport;
+
+        metalViewport.originX = viewport->x;
+        metalViewport.originY = viewport->y;
+        metalViewport.width = viewport->w;
+        metalViewport.height = viewport->h;
+        metalViewport.znear = viewport->minDepth;
+        metalViewport.zfar = viewport->maxDepth;
+
+        [metalCommandBuffer->renderEncoder setViewport:metalViewport];
+    }
+}
+
+static void METAL_SetScissor(
+    SDL_GPUCommandBuffer *commandBuffer,
+    const SDL_Rect *scissor)
+{
+    @autoreleasepool {
+        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
+        MTLScissorRect metalScissor;
+
+        metalScissor.x = scissor->x;
+        metalScissor.y = scissor->y;
+        metalScissor.width = scissor->w;
+        metalScissor.height = scissor->h;
+
+        [metalCommandBuffer->renderEncoder setScissorRect:metalScissor];
+    }
+}
+
+static void METAL_SetBlendConstants(
+    SDL_GPUCommandBuffer *commandBuffer,
+    SDL_FColor blendConstants)
+{
+    @autoreleasepool {
+        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
+        [metalCommandBuffer->renderEncoder setBlendColorRed:blendConstants.r
+                                                      green:blendConstants.g
+                                                       blue:blendConstants.b
+                                                      alpha:blendConstants.a];
+    }
+}
+
+static void METAL_SetStencilReference(
+    SDL_GPUCommandBuffer *commandBuffer,
+    Uint8 reference
+) {
+    @autoreleasepool {
+        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
+        [metalCommandBuffer->renderEncoder setStencilReferenceValue:reference];
+    }
+}
+
 static void METAL_BeginRenderPass(
     SDL_GPUCommandBuffer *commandBuffer,
     const SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
@@ -2099,8 +2151,8 @@ static void METAL_BeginRenderPass(
         MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
         Uint32 vpWidth = UINT_MAX;
         Uint32 vpHeight = UINT_MAX;
-        MTLViewport viewport;
-        MTLScissorRect scissorRect;
+        SDL_GPUViewport viewport;
+        SDL_Rect scissorRect;
 
         for (Uint32 i = 0; i < colorAttachmentCount; i += 1) {
             MetalTextureContainer *container = (MetalTextureContainer *)colorAttachmentInfos[i].texture;
@@ -2201,20 +2253,28 @@ static void METAL_BeginRenderPass(
             }
         }
 
-        // Set default viewport and scissor state
-        viewport.originX = 0;
-        viewport.originY = 0;
-        viewport.width = vpWidth;
-        viewport.height = vpHeight;
-        viewport.znear = 0;
-        viewport.zfar = 1;
-        [metalCommandBuffer->renderEncoder setViewport:viewport];
+        // Set sensible default states
+        viewport.x = 0;
+        viewport.y = 0;
+        viewport.w = vpWidth;
+        viewport.h = vpHeight;
+        viewport.minDepth = 0;
+        viewport.maxDepth = 1;
+        METAL_SetViewport(commandBuffer, &viewport);
 
         scissorRect.x = 0;
         scissorRect.y = 0;
-        scissorRect.width = vpWidth;
-        scissorRect.height = vpHeight;
-        [metalCommandBuffer->renderEncoder setScissorRect:scissorRect];
+        scissorRect.w = vpWidth;
+        scissorRect.h = vpHeight;
+        METAL_SetScissor(commandBuffer, &scissorRect);
+
+        METAL_SetBlendConstants(
+            commandBuffer,
+            (SDL_FColor){ 1.0f, 1.0f, 1.0f, 1.0f });
+
+        METAL_SetStencilReference(
+            commandBuffer,
+            0);
     }
 }
 
@@ -2240,19 +2300,10 @@ static void METAL_BindGraphicsPipeline(
               slopeScale:((rast->depthBiasEnable) ? rast->depthBiasSlopeFactor : 0)
               clamp:((rast->depthBiasEnable) ? rast->depthBiasClamp : 0)];
 
-        // Apply blend constants
-        [metalCommandBuffer->renderEncoder
-            setBlendColorRed:metalGraphicsPipeline->blendConstants[0]
-                       green:metalGraphicsPipeline->blendConstants[1]
-                        blue:metalGraphicsPipeline->blendConstants[2]
-                       alpha:metalGraphicsPipeline->blendConstants[3]];
-
         // Apply depth-stencil state
         if (metalGraphicsPipeline->depthStencilState != NULL) {
             [metalCommandBuffer->renderEncoder
                 setDepthStencilState:metalGraphicsPipeline->depthStencilState];
-            [metalCommandBuffer->renderEncoder
-                setStencilReferenceValue:metalGraphicsPipeline->stencilReference];
         }
 
         for (Uint32 i = 0; i < metalGraphicsPipeline->vertexUniformBufferCount; i += 1) {
@@ -2274,42 +2325,6 @@ static void METAL_BindGraphicsPipeline(
     }
 }
 
-static void METAL_SetViewport(
-    SDL_GPUCommandBuffer *commandBuffer,
-    const SDL_GPUViewport *viewport)
-{
-    @autoreleasepool {
-        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
-        MTLViewport metalViewport;
-
-        metalViewport.originX = viewport->x;
-        metalViewport.originY = viewport->y;
-        metalViewport.width = viewport->w;
-        metalViewport.height = viewport->h;
-        metalViewport.znear = viewport->minDepth;
-        metalViewport.zfar = viewport->maxDepth;
-
-        [metalCommandBuffer->renderEncoder setViewport:metalViewport];
-    }
-}
-
-static void METAL_SetScissor(
-    SDL_GPUCommandBuffer *commandBuffer,
-    const SDL_Rect *scissor)
-{
-    @autoreleasepool {
-        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
-        MTLScissorRect metalScissor;
-
-        metalScissor.x = scissor->x;
-        metalScissor.y = scissor->y;
-        metalScissor.width = scissor->w;
-        metalScissor.height = scissor->h;
-
-        [metalCommandBuffer->renderEncoder setScissorRect:metalScissor];
-    }
-}
-
 static void METAL_BindVertexBuffers(
     SDL_GPUCommandBuffer *commandBuffer,
     Uint32 firstBinding,
diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c
index d555fd88416bb..8fcea98422830 100644
--- a/src/gpu/vulkan/SDL_gpu_vulkan.c
+++ b/src/gpu/vulkan/SDL_gpu_vulkan.c
@@ -996,10 +996,12 @@ typedef struct VulkanCommandBuffer
 
     VulkanTextureSubresource *depthStencilAttachmentSubresource; // may be NULL
 
-    // Viewport/scissor state
+    // Dynamic state
 
     VkViewport currentViewport;
     VkRect2D currentScissor;
+    float blendConstants[4];
+    Uint8 stencilRef;
 
     // Resource bind state
 
@@ -6425,7 +6427,9 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
 
     static const VkDynamicState dynamicStates[] = {
         VK_DYNAMIC_STATE_VIEWPORT,
-        VK_DYNAMIC_STATE_SCISSOR
+        VK_DYNAMIC_STATE_SCISSOR,
+        VK_DYNAMIC_STATE_BLEND_CONSTANTS,
+        VK_DYNAMIC_STATE_STENCIL_REFERENCE
     };
     VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo;
 
@@ -6590,8 +6594,7 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
         pipelineCreateInfo->depthStencilState.compareMask;
     frontStencilState.writeMask =
         pipelineCreateInfo->depthStencilState.writeMask;
-    frontStencilState.reference =
-        pipe

(Patch may be truncated, please check the link at the top of this post.)