From 0681d08817d8b5b3506cd23c8e03574c0c9af0f6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 19 Mar 2025 21:48:55 -0700
Subject: [PATCH] Don't update a texture twice in the same batch on D3D12
Fixes https://github.com/libsdl-org/SDL/issues/12544
---
src/render/direct3d12/SDL_render_d3d12.c | 34 +++++++++++++++---------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index 8b2886320ac8d..5e29835699282 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -130,6 +130,7 @@ typedef struct
DXGI_FORMAT mainTextureFormat;
ID3D12Resource *stagingBuffer;
D3D12_RESOURCE_STATES stagingResourceState;
+ Uint32 textureUpdateSequence;
D3D12_Shader shader;
const float *YCbCr_matrix;
#ifdef SDL_HAVE_YUV
@@ -198,6 +199,7 @@ typedef struct
ID3D12Debug *debugInterface;
ID3D12CommandQueue *commandQueue;
ID3D12GraphicsCommandList2 *commandList;
+ Uint32 commandListSequence;
DXGI_SWAP_EFFECT swapEffect;
UINT swapFlags;
UINT syncInterval;
@@ -558,6 +560,7 @@ static void D3D12_ResetCommandList(D3D12_RenderData *data)
D3D_SAFE_RELEASE(data->uploadBuffers[i]);
}
data->currentUploadBuffer = 0;
+ ++data->commandListSequence;
ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps);
}
@@ -1044,6 +1047,7 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer)
WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommandList"), result);
goto done;
}
+ data->commandListSequence = 1;
// Set the descriptor heaps to the correct initial value
ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps);
@@ -1814,7 +1818,7 @@ static void D3D12_DestroyTexture(SDL_Renderer *renderer,
texture->internal = NULL;
}
-static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState)
+static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, Uint32 textureUpdateSequence, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState)
{
const Uint8 *src;
Uint8 *dst;
@@ -1945,7 +1949,8 @@ static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Re
rendererData->currentUploadBuffer++;
// If we've used up all the upload buffers, we need to issue the batch
- if (rendererData->currentUploadBuffer == SDL_D3D12_NUM_UPLOAD_BUFFERS) {
+ if (textureUpdateSequence == rendererData->commandListSequence ||
+ rendererData->currentUploadBuffer == SDL_D3D12_NUM_UPLOAD_BUFFERS) {
D3D12_IssueBatch(rendererData);
}
@@ -1963,7 +1968,7 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("Texture is not currently available");
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceState)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceState)) {
return false;
}
#ifdef SDL_HAVE_YUV
@@ -1971,13 +1976,13 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
// Skip to the correct offset into the next texture
srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
- if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) {
return false;
}
// Skip to the correct offset into the next texture
srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2));
- if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) {
return false;
}
}
@@ -1991,11 +1996,12 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
} else {
srcPitch = (srcPitch + 1) & ~1;
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, srcPitch, &textureData->mainResourceState)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, srcPitch, &textureData->mainResourceState)) {
return false;
}
}
#endif // SDL_HAVE_YUV
+ textureData->textureUpdateSequence = rendererData->commandListSequence;
return true;
}
@@ -2013,15 +2019,16 @@ static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("Texture is not currently available");
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
return false;
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainResourceStateU)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainResourceStateU)) {
return false;
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) {
return false;
}
+ textureData->textureUpdateSequence = rendererData->commandListSequence;
return true;
}
@@ -2037,13 +2044,14 @@ static bool D3D12_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("Texture is not currently available");
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
return false;
}
- if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, rect->w, rect->h, UVplane, UVpitch, &textureData->mainResourceState)) {
+ if (!D3D12_UpdateTextureInternal(rendererData, textureData->textureUpdateSequence, textureData->mainTexture, 1, rect->x, rect->y, rect->w, rect->h, UVplane, UVpitch, &textureData->mainResourceState)) {
return false;
}
+ textureData->textureUpdateSequence = rendererData->commandListSequence;
return true;
}
#endif
@@ -2704,7 +2712,7 @@ static bool D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *
rendererData->currentSampler = *sampler;
}
- if (updateSubresource == true || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) {
+ if (updateSubresource || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) {
SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix));
ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList,
0,
@@ -2718,7 +2726,7 @@ static bool D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *
shader_constants = &solid_constants;
}
- if (updateSubresource == true ||
+ if (updateSubresource ||
SDL_memcmp(shader_constants, ¤tPipelineState->shader_constants, sizeof(*shader_constants)) != 0) {
ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList,
1,