From 1fb9e4a7aa89de19e28ab3ad5f95e64a7f7ee31f Mon Sep 17 00:00:00 2001 From: KHeartz Date: Mon, 15 Jun 2026 11:24:11 -0400 Subject: [PATCH] Graphics: D3D12 SRV descriptor slot allocator Adds a bounded, shader-visible SRV slot pool on the D3D12 backend for upcoming web-view textures. The heap grows by a fixed kExtraSrvSlots (64) past the ImGui-reserved slots; AllocateSRVSlot/FreeSRVSlot hand out and reclaim indices into that range, and the slots share ImGui's heap so handles double as ImTextureID. Guards, since the eventual consumer (CEF views) renders partly untrusted content into a heap shared with the trusted in-game UI: - the slot getters bounds-check and return a null handle for any out-of-range index, so an exhausted -1 or a stray id can never produce an out-of-heap descriptor - FreeSRVSlot rejects out-of-pool indices and double-frees via an in-use bitmap, so a bad id can't re-enter the free list and alias a live slot - a mutex serializes allocate/free against concurrent view lifecycle Bounded by the fixed 64-slot cap. Gating (opt-in reservation) is left as a follow-up pending the rendering backend's configuration story; no consumer allocates slots yet. --- code/framework/src/graphics/backend/d3d12.cpp | 68 ++++++++++++++++++- code/framework/src/graphics/backend/d3d12.h | 29 ++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/code/framework/src/graphics/backend/d3d12.cpp b/code/framework/src/graphics/backend/d3d12.cpp index 64de633df..51f172f38 100644 --- a/code/framework/src/graphics/backend/d3d12.cpp +++ b/code/framework/src/graphics/backend/d3d12.cpp @@ -41,12 +41,22 @@ namespace Framework::Graphics { { D3D12_DESCRIPTOR_HEAP_DESC desc {}; desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - desc.NumDescriptors = _frameBufferCount; + desc.NumDescriptors = _frameBufferCount + kExtraSrvSlots; desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; if (pD3DDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_srvHeap)) != S_OK) { return false; } + + // Manage slots that are not reserved for ImGui and headroom + _srvDescriptorSize = pD3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + _srvHeapSize = desc.NumDescriptors; + _srvSlotInUse.assign(_srvHeapSize, false); + _freeSrvSlots.clear(); + const auto extraSrvStart = _frameBufferCount - 1; + for (UINT i = kExtraSrvSlots; i > 0; i--) { + _freeSrvSlots.push_back(extraSrvStart + i); + } } // #3 create rtv heap @@ -99,6 +109,7 @@ namespace Framework::Graphics { // release objects _rtvHeap->Release(); _srvHeap->Release(); + _srvHeap = nullptr; _commandList->Release(); for (const auto &frameContext : _frameContext) { frameContext._commandAllocator->Release(); @@ -140,4 +151,59 @@ namespace Framework::Graphics { int D3D12Backend::NumFramesInFlight() const { return _frameBufferCount; } + + int D3D12Backend::AllocateSRVSlot() { + std::lock_guard lock(_srvMutex); + if (!_srvHeap) { + Framework::Logging::GetLogger(FRAMEWORK_INNER_GRAPHICS)->error("D3D12Backend::AllocateSRVSlot, no descriptor heap"); + return -1; + } + if (_freeSrvSlots.empty()) { + Framework::Logging::GetLogger(FRAMEWORK_INNER_GRAPHICS)->error("D3D12Backend::AllocateSRVSlot, heap exhausted"); + return -1; + } + const auto slot = _freeSrvSlots.back(); + _freeSrvSlots.pop_back(); + _srvSlotInUse[slot] = true; + return static_cast(slot); + } + + void D3D12Backend::FreeSRVSlot(int slot) { + std::lock_guard lock(_srvMutex); + // reject out-of-pool indices and double-frees so a bad id can never + // re-enter the free list and alias a live slot + if (slot < static_cast(_frameBufferCount) || slot >= static_cast(_srvHeapSize)) { + Framework::Logging::GetLogger(FRAMEWORK_INNER_GRAPHICS)->error("D3D12Backend::FreeSRVSlot, slot {} out of pool range", slot); + return; + } + if (!_srvSlotInUse[slot]) { + Framework::Logging::GetLogger(FRAMEWORK_INNER_GRAPHICS)->error("D3D12Backend::FreeSRVSlot, slot {} not allocated", slot); + return; + } + _srvSlotInUse[slot] = false; + _freeSrvSlots.push_back(static_cast(slot)); + } + + D3D12_CPU_DESCRIPTOR_HANDLE D3D12Backend::GetSRVSlotCPUHandle(int slot) const { + if (!_srvHeap || slot < 0 || slot >= static_cast(_srvHeapSize)) { + return {}; + } + auto handle = _srvHeap->GetCPUDescriptorHandleForHeapStart(); + handle.ptr += static_cast(slot) * _srvDescriptorSize; + return handle; + } + + D3D12_GPU_DESCRIPTOR_HANDLE D3D12Backend::GetSRVSlotGPUHandle(int slot) const { + if (!_srvHeap || slot < 0 || slot >= static_cast(_srvHeapSize)) { + return {}; + } + auto handle = _srvHeap->GetGPUDescriptorHandleForHeapStart(); + handle.ptr += static_cast(slot) * _srvDescriptorSize; + return handle; + } + + size_t D3D12Backend::GetFreeSRVSlotCount() const { + std::lock_guard lock(_srvMutex); + return _freeSrvSlots.size(); + } } // namespace Framework::Graphics diff --git a/code/framework/src/graphics/backend/d3d12.h b/code/framework/src/graphics/backend/d3d12.h index a885fd3e4..1cbb507f9 100644 --- a/code/framework/src/graphics/backend/d3d12.h +++ b/code/framework/src/graphics/backend/d3d12.h @@ -11,10 +11,16 @@ #include "backend.h" #include +#include #include namespace Framework::Graphics { class D3D12Backend: public Backend { + public: + // Shader-visible SRV slots reserved past the ImGui font descriptors, + // handed to web views via AllocateSRVSlot. + static constexpr UINT kExtraSrvSlots = 64; + private: IDXGISwapChain3 *_swapChain = nullptr; UINT _frameBufferCount = 0; @@ -23,6 +29,12 @@ namespace Framework::Graphics { ID3D12GraphicsCommandList *_commandList = nullptr; ID3D12CommandQueue *_commandQueue = nullptr; + UINT _srvDescriptorSize = 0; + UINT _srvHeapSize = 0; + std::vector _freeSrvSlots; + std::vector _srvSlotInUse; + mutable std::mutex _srvMutex; + struct FrameContext { ID3D12CommandAllocator *_commandAllocator = nullptr; ID3D12Resource *_mainRenderTargetResource = nullptr; @@ -40,6 +52,23 @@ namespace Framework::Graphics { void End(); int NumFramesInFlight() const; + // Bounded, shader-visible SRV slot pool shared with ImGui's heap (so handles + // double as ImTextureID). AllocateSRVSlot returns -1 when exhausted; the + // getters return a null handle for any out-of-range slot. + int AllocateSRVSlot(); + + // Frees an SRV slot from a given slot handle + void FreeSRVSlot(int slot); + + // Retrieves a CPU Descriptor handle from a given @p slot handle + D3D12_CPU_DESCRIPTOR_HANDLE GetSRVSlotCPUHandle(int slot) const; + + // Retrieves a GPU Descriptor handle from a given @p slot handle + D3D12_GPU_DESCRIPTOR_HANDLE GetSRVSlotGPUHandle(int slot) const; + + // Returns number of free SRV slots + size_t GetFreeSRVSlotCount() const; + // TODO: Backend not implemented yet void BeginDrawing() {} void EndDrawing() {}