Backends: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)

This commit is contained in:
Rémy Tassoux
2025-10-10 17:56:31 +02:00
committed by ocornut
parent 00251c8921
commit 9be4f150ef
2 changed files with 53 additions and 33 deletions

View File

@@ -20,6 +20,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)
// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965)
// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963)
@@ -108,6 +109,9 @@ struct ImGui_ImplDX12_Data
ID3D12CommandAllocator* pTexCmdAllocator;
ID3D12GraphicsCommandList* pTexCmdList;
ID3D12Resource* pTexUploadBuffer;
UINT pTexUploadBufferSize;
void* pTexUploadBufferMapped;
ImGui_ImplDX12_RenderBuffers* pFrameResources;
UINT frameIndex;
@@ -438,6 +442,16 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
UINT upload_size = upload_pitch_dst * upload_h;
if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize)
{
if (bd->pTexUploadBufferMapped)
{
D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
bd->pTexUploadBuffer->Unmap(0, &range);
bd->pTexUploadBufferMapped = nullptr;
}
SafeRelease(bd->pTexUploadBuffer);
D3D12_RESOURCE_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
@@ -458,24 +472,23 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
// FIXME-OPT: Could upload buffer be kept around, reused, and grown only when needed? Would that be worth it?
ID3D12Resource* uploadBuffer = nullptr;
HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer));
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer));
IM_ASSERT(SUCCEEDED(hr));
D3D12_RANGE range = {0, upload_size};
hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped);
IM_ASSERT(SUCCEEDED(hr));
bd->pTexUploadBufferSize = upload_size;
}
bd->pTexCmdAllocator->Reset();
bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr);
ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList;
// Copy to upload buffer
void* mapped = nullptr;
D3D12_RANGE range = { 0, upload_size };
hr = uploadBuffer->Map(0, &range, &mapped);
IM_ASSERT(SUCCEEDED(hr));
for (int y = 0; y < upload_h; y++)
memcpy((void*)((uintptr_t)mapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src);
uploadBuffer->Unmap(0, &range);
memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src);
if (need_barrier_before_copy)
{
@@ -492,7 +505,7 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
{
srcLocation.pResource = uploadBuffer;
srcLocation.pResource = bd->pTexUploadBuffer;
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srcLocation.PlacedFootprint.Footprint.Width = upload_w;
@@ -516,9 +529,8 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
cmdList->ResourceBarrier(1, &barrier);
}
hr = cmdList->Close();
HRESULT hr = cmdList->Close();
IM_ASSERT(SUCCEEDED(hr));
ID3D12CommandQueue* cmdQueue = bd->pCommandQueue;
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
@@ -531,7 +543,6 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent);
::WaitForSingleObject(bd->FenceEvent, INFINITE);
uploadBuffer->Release();
tex->SetStatus(ImTextureStatus_OK);
}
@@ -804,6 +815,13 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
bd->commandQueueOwned = false;
SafeRelease(bd->pRootSignature);
SafeRelease(bd->pPipelineState);
if (bd->pTexUploadBufferMapped)
{
D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
bd->pTexUploadBuffer->Unmap(0, &range);
bd->pTexUploadBufferMapped = nullptr;
}
SafeRelease(bd->pTexUploadBuffer);
SafeRelease(bd->pTexCmdList);
SafeRelease(bd->pTexCmdAllocator);
SafeRelease(bd->Fence);

View File

@@ -99,6 +99,8 @@ Other Changes:
of recreating them each time. (#8963, #8465) [@RT2Code]
- Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code]
(presumably fixes old hard-to-repro crash issues such as #3463, #5018)
- Backends: DirectX12: Reuse texture upload buffer and grow it only when
necessary. (#9002) [@RT2Code]
- Backends: DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code]
- Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support
`RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes]