From d4e4229d614cd5acc7c15694e6a225b8ef658387 Mon Sep 17 00:00:00 2001 From: Patedam Date: Fri, 23 Jan 2026 21:43:48 -0500 Subject: [PATCH] Fixing the D3D12DescriptorHeap.cpp memory leak. Was creating descriptor heap every time a pipeline was bound when we only need it once per commandlist. fixed some vibe code weird shit --- .../src/Graphics/D3D12/D3D12CommandList.cpp | 35 +++-- Juliet/src/Graphics/D3D12/D3D12CommandList.h | 12 +- .../Graphics/D3D12/D3D12DescriptorHeap.cpp | 100 +++---------- .../src/Graphics/D3D12/D3D12DescriptorHeap.h | 2 +- .../Graphics/D3D12/D3D12GraphicsDevice.cpp | 6 - .../src/Graphics/D3D12/D3D12InternalTests.cpp | 137 +----------------- .../src/Graphics/D3D12/D3D12InternalTests.h | 1 - Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp | 40 ++--- Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp | 22 +-- Juliet/src/Graphics/DebugDisplayRenderer.cpp | 35 ++--- JulietApp/main.cpp | 2 - 11 files changed, 97 insertions(+), 295 deletions(-) diff --git a/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp b/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp index dc9d099..d5e61fb 100644 --- a/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp @@ -1,7 +1,6 @@ #include -#include -#include #include +#include #include #include #include @@ -28,7 +27,8 @@ namespace Juliet::D3D12 bool CreateAllocator(NonNullPtr driver, NonNullPtr baseData, D3D12_COMMAND_QUEUE_DESC queueDesc) { - HRESULT result = driver->D3D12Device->CreateCommandAllocator(queueDesc.Type, IID_ID3D12CommandAllocator, reinterpret_cast(&baseData->Allocator)); + HRESULT result = driver->D3D12Device->CreateCommandAllocator(queueDesc.Type, IID_ID3D12CommandAllocator, + reinterpret_cast(&baseData->Allocator)); if (FAILED(result)) { AssertHR(result, "Cannot create ID3D12CommandAllocator"); @@ -56,8 +56,8 @@ namespace Juliet::D3D12 ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; HRESULT result = driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type, - D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, - reinterpret_cast(&d3d12GraphicsCommandList)); + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12GraphicsCommandList)); if (FAILED(result)) { Assert(false, "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " @@ -77,8 +77,8 @@ namespace Juliet::D3D12 ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; HRESULT result = driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type, - D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, - reinterpret_cast(&d3d12GraphicsCommandList)); + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12GraphicsCommandList)); if (FAILED(result)) { Assert(false, "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " @@ -96,8 +96,10 @@ namespace Juliet::D3D12 { CreateAllocator(driver, &commandList->CopyCommandList, queueDesc); ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr; - HRESULT result = driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type, - D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList, reinterpret_cast(&d3d12CopyCommandList)); + HRESULT result = + driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList, + reinterpret_cast(&d3d12CopyCommandList)); if (FAILED(result)) { @@ -392,8 +394,8 @@ namespace Juliet::D3D12 { auto d3d12CommandList = reinterpret_cast(commandList.Get()); // For now we assume Graphics Root Signature. Compute support would need a check or separate function. - d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRoot32BitConstants( - rootParameterIndex, numConstants, constants, 0); + d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRoot32BitConstants(rootParameterIndex, + numConstants, constants, 0); } namespace Internal @@ -409,7 +411,7 @@ namespace Juliet::D3D12 samplerHeap = AcquireSamplerHeapFromPool(commandList->Driver); commandList->CRB_SRV_UAV_Heap = viewHeap; - commandList->RTV_Heap = samplerHeap; + commandList->Sampler_Heap = samplerHeap; heaps[0] = viewHeap->Handle; heaps[1] = samplerHeap->Handle; @@ -445,19 +447,16 @@ namespace Juliet::D3D12 return false; } - result = commandList->GraphicsCommandList.CommandList->Reset( - commandList->GraphicsCommandList.Allocator, nullptr); + result = commandList->GraphicsCommandList.CommandList->Reset(commandList->GraphicsCommandList.Allocator, nullptr); if (FAILED(result)) { LogError(driver->D3D12Device, "Could not reset command list", result); return false; } - // Return heap descriptor to pool - // CRB_SRV_UAV_Heap is global bindless, do not return it. - ReturnSamplerHeapToPool(driver, commandList->RTV_Heap); + ReturnSamplerHeapToPool(driver, commandList->Sampler_Heap); + commandList->Sampler_Heap = nullptr; commandList->CRB_SRV_UAV_Heap = nullptr; - commandList->RTV_Heap = nullptr; // Clean up resource tracking for (uint32 idx = 0; idx < commandList->UsedTextureCount; ++idx) diff --git a/Juliet/src/Graphics/D3D12/D3D12CommandList.h b/Juliet/src/Graphics/D3D12/D3D12CommandList.h index 4268732..c1c1b52 100644 --- a/Juliet/src/Graphics/D3D12/D3D12CommandList.h +++ b/Juliet/src/Graphics/D3D12/D3D12CommandList.h @@ -39,10 +39,11 @@ namespace Juliet::D3D12 struct D3D12CommandList { + CommandListHeader Common; + uint64 ID; - CommandListHeader Common; - D3D12Driver* Driver; + D3D12Driver* Driver; D3D12PresentData* PresentDatas; uint32 PresentDataCapacity; @@ -77,7 +78,7 @@ namespace Juliet::D3D12 // D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage]; Internal::D3D12DescriptorHeap* CRB_SRV_UAV_Heap; - Internal::D3D12DescriptorHeap* RTV_Heap; + Internal::D3D12DescriptorHeap* Sampler_Heap; // Resource Tracking D3D12Texture** UsedTextures; @@ -96,8 +97,9 @@ namespace Juliet::D3D12 extern void SetBlendConstants(NonNullPtr commandList, FColor blendConstants); extern void SetBlendConstants(NonNullPtr commandList, FColor blendConstants); extern void SetStencilReference(NonNullPtr commandList, uint8 reference); - extern void SetIndexBuffer(NonNullPtr commandList, NonNullPtr buffer, IndexFormat format); - extern void SetPushConstants(NonNullPtr commandList, ShaderStage stage, uint32 rootParameterIndex, uint32 numConstants, const void* constants); + extern void SetIndexBuffer(NonNullPtr commandList, NonNullPtr buffer, IndexFormat format); + extern void SetPushConstants(NonNullPtr commandList, ShaderStage stage, uint32 rootParameterIndex, + uint32 numConstants, const void* constants); namespace Internal { diff --git a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp index 4979f5d..05fa2d2 100644 --- a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -8,8 +10,6 @@ namespace Juliet::D3D12::Internal { D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging) { - ID3D12DescriptorHeap* handle; - auto heap = ArenaPushType(GetEngineArena(), ConstString("D3D12DescriptorHeap")); if (!heap) { @@ -28,7 +28,9 @@ namespace Juliet::D3D12::Internal heapDesc.Flags = isStaging ? D3D12_DESCRIPTOR_HEAP_FLAG_NONE : D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; heapDesc.NodeMask = 0; - HRESULT result = driver->D3D12Device->CreateDescriptorHeap(&heapDesc, IID_ID3D12DescriptorHeap, (void**)&handle); + ID3D12DescriptorHeap* handle; + HRESULT result = + driver->D3D12Device->CreateDescriptorHeap(&heapDesc, IID_ID3D12DescriptorHeap, reinterpret_cast(&handle)); if (FAILED(result)) { LogError(driver->D3D12Device, "Failed to create descriptor heap!", result); @@ -101,8 +103,10 @@ namespace Juliet::D3D12::Internal if (heap->FreeIndicesCount >= heap->FreeIndicesCapacity) { + size_t oldCapacity = heap->FreeIndicesCapacity; heap->FreeIndicesCapacity *= 2; - heap->FreeIndices = static_cast(Realloc(heap->FreeIndices, heap->FreeIndicesCapacity * sizeof(uint32))); + heap->FreeIndices = ArenaRealloc(GetEngineArena(), heap->FreeIndices, oldCapacity, + heap->FreeIndicesCapacity, ConstString("FreeIndices")); } heap->FreeIndices[heap->FreeIndicesCount] = descriptor.Index; @@ -111,62 +115,20 @@ namespace Juliet::D3D12::Internal D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr d3d12Driver, DescriptorHeapCreator creator) { - D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool; - uint32 count = GPUDriver::kSampler_HeapDescriptorCount; + D3D12DescriptorHeap* result; + D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool; if (pool->Count > 0) { + result = pool->Heaps[pool->Count - 1]; pool->Count -= 1; - return pool->Heaps[pool->Count]; } - - // Pool is exhausted (all heaps are in use). We need to create a new one. - // The pool array might be full of "Used" heaps, or it might have empty slots (nullptr) if we expanded before. - // We look for a free slot to store the new heap. - size_t freeSlotIndex = SIZE_MAX; - - // Scan for nullptr in the entire array. - // Optimization: active heaps are likely at the beginning, but since Count=0, "Used" heaps are 0..Capacity? - // Actually, if Count=0, ALL slots 0..Capacity-1 are effectively "Used" (unless some are nullptr). - for (size_t i = 0; i < pool->Capacity; ++i) + else { - if (pool->Heaps[i] == nullptr) - { - freeSlotIndex = i; - break; - } + result = creator(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, GPUDriver::kSampler_HeapDescriptorCount, false); } - if (freeSlotIndex == SIZE_MAX) - { - // No free slot, expand - size_t oldCapacity = pool->Capacity; - pool->Capacity = pool->Capacity == 0 ? 1 : pool->Capacity * 2; - - pool->Heaps = ArenaRealloc(GetEngineArena(), pool->Heaps, oldCapacity, pool->Capacity, - ConstString("DescriptorHeapArray")); - - // Initialize new slots to nullptr - for (size_t i = oldCapacity; i < pool->Capacity; ++i) - { - pool->Heaps[i] = nullptr; - } - freeSlotIndex = oldCapacity; - } - - // Create the new heap at the free slot - pool->Heaps[freeSlotIndex] = creator(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, count, false); - - // Now we have a new heap at freeSlotIndex. Ideally we want to return it. - // But to maintain the "Stack" invariant (Available are at 0..Count-1), - // and "Used" are at "Count..Capacity-1". - // Currently Count=0. So "Used" is 0..Capacity-1. - // freeSlotIndex is inside "Used" range. - // We can just return it. It is already in the "Used" partition. - // However, we want to ensure it doesn't get lost or overwritten. - // Since we didn't increment Count, it stays in "Used". - - return pool->Heaps[freeSlotIndex]; + return result; } void ReturnSamplerHeapToPool(NonNullPtr d3d12Driver, D3D12DescriptorHeap* heap) @@ -176,40 +138,18 @@ namespace Juliet::D3D12::Internal return; } - Assert(heap->HeapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool; heap->CurrentDescriptorIndex = 0; - // The heap is currently in the "Used" partition (index >= Count). - // We want to move it to the "Available" partition (index < Count). - // Strategy: Swap heap with the element at pool->Heaps[pool->Count], then increment Count. - - // First, find the heap index. - size_t heapIndex = SIZE_MAX; - for (size_t i = pool->Count; i < pool->Capacity; ++i) + if (pool->Count >= pool->Capacity) { - if (pool->Heaps[i] == heap) - { - heapIndex = i; - break; - } + size_t oldCapacity = pool->Capacity; + pool->Capacity *= 2; + pool->Heaps = ArenaRealloc(GetEngineArena(), pool->Heaps, oldCapacity, pool->Capacity, ConstString("Heaps")); } - if (heapIndex != SIZE_MAX) - { - D3D12DescriptorHeap* temp = pool->Heaps[pool->Count]; - pool->Heaps[pool->Count] = pool->Heaps[heapIndex]; - pool->Heaps[heapIndex] = temp; - pool->Count += 1; - } - else - { - // Should not happen if the logic is correct. - // Maybe it was not in the pool? (e.g. error scenario). - // In that case, we should probably add it? - // But per constraints, we assume it was allocated via pool. - Assert(false, "Returning a heap that was not found in the pool!"); - } + pool->Heaps[pool->Count] = heap; + pool->Count += 1; } } // namespace Juliet::D3D12::Internal diff --git a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.h b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.h index 6368cda..c101473 100644 --- a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.h +++ b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.h @@ -21,7 +21,7 @@ namespace Juliet::D3D12::Internal uint32 MaxDescriptors; uint32 DescriptorSize; uint32 CurrentDescriptorIndex; // only used by GPU heaps - + uint32* FreeIndices; uint32 FreeIndicesCapacity; uint32 FreeIndicesCount; diff --git a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp index 3ecfd5e..483499b 100644 --- a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp @@ -742,13 +742,7 @@ namespace Juliet::D3D12 #endif GraphicsDevice* CreateGraphicsDevice(bool enableDebug) - { -#if JULIET_DEBUG - // Unit Tests for D3D12 Logic - UnitTest::TestDescriptorHeapPool(); - -#endif auto driver = static_cast(Calloc(1, sizeof(D3D12Driver))); #if JULIET_DEBUG diff --git a/Juliet/src/Graphics/D3D12/D3D12InternalTests.cpp b/Juliet/src/Graphics/D3D12/D3D12InternalTests.cpp index aa5228b..4bcbbe4 100644 --- a/Juliet/src/Graphics/D3D12/D3D12InternalTests.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12InternalTests.cpp @@ -1,144 +1,13 @@ -#include #include #include #include +#include #if JULIET_DEBUG - + namespace Juliet::D3D12::UnitTest { using namespace Juliet::D3D12; using namespace Juliet::D3D12::Internal; - - static D3D12DescriptorHeap* MockCreateDescriptorHeap(NonNullPtr, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32, bool) - { - D3D12DescriptorHeap* heap = static_cast(Calloc(1, sizeof(D3D12DescriptorHeap))); - if (heap) - { - heap->HeapType = type; - heap->MaxDescriptors = 128; - // Mock other fields as needed to pass verification - } - return heap; - } - - void TestDescriptorHeapPool() - { - D3D12Driver driver = {}; - ZeroStruct(driver); - // Ensure pool is empty - driver.SamplerHeapPool.Count = 0; - driver.SamplerHeapPool.Capacity = 0; - driver.SamplerHeapPool.Heaps = nullptr; - - NonNullPtr driverPtr(&driver); - - // 1. Acquire from empty -> Should create new - D3D12DescriptorHeap* heap1 = AcquireSamplerHeapFromPool(driverPtr, MockCreateDescriptorHeap); - Assert(heap1 != nullptr); - Assert(driver.SamplerHeapPool.Count == 0); // Used heaps are above Count - Assert(driver.SamplerHeapPool.Capacity >= 1); - Assert(driver.SamplerHeapPool.Heaps[0] == heap1); - - // 2. Return -> Should become available - ReturnSamplerHeapToPool(driverPtr, heap1); - Assert(driver.SamplerHeapPool.Count == 1); // One available - Assert(driver.SamplerHeapPool.Heaps[0] == heap1); // Available is at index 0 - - // 3. Acquire again -> Should reuse - D3D12DescriptorHeap* heap2 = AcquireSamplerHeapFromPool(driverPtr, MockCreateDescriptorHeap); - Assert(heap2 == heap1); // Reused - Assert(driver.SamplerHeapPool.Count == 0); // No available left - - // 4. Acquire another -> Should Expand and Create - D3D12DescriptorHeap* heap3 = AcquireSamplerHeapFromPool(driverPtr, MockCreateDescriptorHeap); - Assert(heap3 != nullptr); - Assert(heap3 != heap1); - Assert(driver.SamplerHeapPool.Count == 0); - Assert(driver.SamplerHeapPool.Capacity >= 2); - - // Cleanup - (void)heap1; (void)heap2; (void)heap3; - Free(heap1); - Free(heap3); - - // 5. Test Multiple Allocations (8 heaps) - // Reset pool for clean test or continue? Let's reset to be sure of state, - // effectively leaking previous pool array but that's fine for mock test. - // Actually, let's just continue using the pool, it has Capacity >= 2. - - // Return everything first to have clean slate if possible, but we already freed heap1 and heap3 mentally/physically. - // The pool thinks heap3 is "Used" (outside Count). - // We freed it physically, but pool has a dangling pointer in Heaps[1]. - // This is dangerous if we reuse it. - // So we MUST correct the pool state or reset it. - // Let's reset the pool state for the 8-loop test. - driver.SamplerHeapPool.Count = 0; - driver.SamplerHeapPool.Capacity = 0; - driver.SamplerHeapPool.Heaps = nullptr; // Leaks previous array - - const int kNumHeaps = 32; - D3D12DescriptorHeap* heaps[kNumHeaps]; - - // 5.1 Acquire 8 - for (int i = 0; i < kNumHeaps; ++i) - { - heaps[i] = AcquireSamplerHeapFromPool(driverPtr, MockCreateDescriptorHeap); - Assert(heaps[i] != nullptr); - // Verify uniqueness - for (int j = 0; j < i; ++j) - { - Assert(heaps[i] != heaps[j]); - } - } - Assert(driver.SamplerHeapPool.Capacity >= kNumHeaps); - Assert(driver.SamplerHeapPool.Count == 0); - - // 5.2 Release 8 - for (int i = 0; i < kNumHeaps; ++i) - { - ReturnSamplerHeapToPool(driverPtr, heaps[i]); - } - Assert(driver.SamplerHeapPool.Count == kNumHeaps); - - // 5.3 Acquire 8 Again (Should Reuse) - D3D12DescriptorHeap* heapsAgain[kNumHeaps]; - for (int i = 0; i < kNumHeaps; ++i) - { - heapsAgain[i] = AcquireSamplerHeapFromPool(driverPtr, MockCreateDescriptorHeap); - - // Verify it matches one of the original heaps (order might reverse due to stack-like behavior) - bool found = false; - for (int j = 0; j < kNumHeaps; ++j) - { - if (heapsAgain[i] == heaps[j]) - { - found = true; - break; - } - } - Assert(found); - // Verify uniqueness in the new set - for (int j = 0; j < i; ++j) - { - Assert(heapsAgain[i] != heapsAgain[j]); - } - } - Assert(driver.SamplerHeapPool.Count == 0); - // Capacity should not have increased if we reused - Assert(driver.SamplerHeapPool.Capacity == kNumHeaps); // Or >= 8 - - // Cleanup - for (int i = 0; i < kNumHeaps; ++i) - { - Free(heaps[i]); - } - - // Note: heap2 is heap1. - // Pool array in driver leaked for test scope, acceptable. - - printf("DescriptorHeapPool tests passed.\n"); - } - -} +} // namespace Juliet::D3D12::UnitTest #endif diff --git a/Juliet/src/Graphics/D3D12/D3D12InternalTests.h b/Juliet/src/Graphics/D3D12/D3D12InternalTests.h index 7c19eca..cc7ee8d 100644 --- a/Juliet/src/Graphics/D3D12/D3D12InternalTests.h +++ b/Juliet/src/Graphics/D3D12/D3D12InternalTests.h @@ -4,6 +4,5 @@ #if JULIET_DEBUG namespace Juliet::D3D12::UnitTest { - void TestDescriptorHeapPool(); } #endif diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp index e06e856..f2924e5 100644 --- a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp @@ -45,15 +45,15 @@ namespace Juliet::D3D12 bool hasDSV = false; if (depthStencilTargetInfo && depthStencilTargetInfo->TargetTexture) { - auto* container = reinterpret_cast(depthStencilTargetInfo->TargetTexture); - uint32 width = container->Header.CreateInfo.Width; - uint32 height = container->Header.CreateInfo.Height; + auto* container = reinterpret_cast(depthStencilTargetInfo->TargetTexture); + uint32 width = container->Header.CreateInfo.Width; + uint32 height = container->Header.CreateInfo.Height; frameBufferWidth = Min(width, frameBufferWidth); frameBufferHeight = Min(height, frameBufferHeight); - D3D12TextureSubresource* subresource = Internal::PrepareTextureSubresourceForWrite( - d3d12CommandList, container, 0, 0, false, D3D12_RESOURCE_STATE_DEPTH_WRITE); + D3D12TextureSubresource* subresource = + Internal::PrepareTextureSubresourceForWrite(d3d12CommandList, container, 0, 0, false, D3D12_RESOURCE_STATE_DEPTH_WRITE); DSV = subresource->DSVHandle.CpuHandle; hasDSV = true; @@ -67,9 +67,10 @@ namespace Juliet::D3D12 // TODO: Check if texture has stencil // if (HasStencil(container->Header.CreateInfo.Format)) clearFlags |= D3D12_CLEAR_FLAG_STENCIL; - d3d12CommandList->GraphicsCommandList.CommandList->ClearDepthStencilView(DSV, - clearFlags, depthStencilTargetInfo->ClearDepth, - depthStencilTargetInfo->ClearStencil, 0, nullptr); + d3d12CommandList->GraphicsCommandList.CommandList->ClearDepthStencilView(DSV, clearFlags, + depthStencilTargetInfo->ClearDepth, + depthStencilTargetInfo->ClearStencil, + 0, nullptr); } } @@ -93,8 +94,7 @@ namespace Juliet::D3D12 clearColor[2] = colorTargetInfos[idx].ClearColor.B; clearColor[3] = colorTargetInfos[idx].ClearColor.A; - d3d12CommandList->GraphicsCommandList.CommandList->ClearRenderTargetView(rtv, - clearColor, 0, nullptr); + d3d12CommandList->GraphicsCommandList.CommandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr); } RTVs[idx] = rtv; @@ -119,8 +119,8 @@ namespace Juliet::D3D12 } } - d3d12CommandList->GraphicsCommandList.CommandList->OMSetRenderTargets( - colorTargetInfoCount, RTVs, false, hasDSV ? &DSV : nullptr); + d3d12CommandList->GraphicsCommandList.CommandList->OMSetRenderTargets(colorTargetInfoCount, RTVs, false, + hasDSV ? &DSV : nullptr); // Set defaults graphics states GraphicsViewPort defaultViewport; @@ -191,8 +191,7 @@ namespace Juliet::D3D12 // Reset Depth Stencil state if (d3d12CommandList->DepthStencilSubresource) { - Internal::TextureSubresourceTransitionToDefaultUsage(d3d12CommandList, - d3d12CommandList->DepthStencilSubresource, + Internal::TextureSubresourceTransitionToDefaultUsage(d3d12CommandList, d3d12CommandList->DepthStencilSubresource, D3D12_RESOURCE_STATE_DEPTH_WRITE); d3d12CommandList->DepthStencilSubresource = nullptr; } @@ -232,14 +231,16 @@ namespace Juliet::D3D12 d3d12CommandList->CurrentGraphicsPipeline = pipeline; // Set the Descriptor heap - Internal::SetDescriptorHeaps(d3d12CommandList); + if (d3d12CommandList->CRB_SRV_UAV_Heap == nullptr) + { + Internal::SetDescriptorHeaps(d3d12CommandList); + } // Set the pipeline state d3d12CommandList->GraphicsCommandList.CommandList->SetPipelineState(pipeline->PipelineState); - d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRootSignature( - pipeline->RootSignature->Handle); + d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRootSignature(pipeline->RootSignature->Handle); d3d12CommandList->GraphicsCommandList.CommandList->IASetPrimitiveTopology( - JulietToD3D12_PrimitiveType[ToUnderlying(pipeline->PrimitiveType)]); + JulietToD3D12_PrimitiveType[ToUnderlying(pipeline->PrimitiveType)]); // Mark that bindings are needed d3d12CommandList->NeedVertexSamplerBind = true; @@ -280,7 +281,6 @@ namespace Juliet::D3D12 // TODO : Last missing piece // D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer); - d3d12CommandList->GraphicsCommandList.CommandList->DrawInstanced(numVertices, - numInstances, firstVertex, firstInstance); + d3d12CommandList->GraphicsCommandList.CommandList->DrawInstanced(numVertices, numInstances, firstVertex, firstInstance); } } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp b/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp index 58fbae5..d686711 100644 --- a/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp @@ -41,8 +41,7 @@ namespace Juliet::D3D12 SwapChainComposition composition, NonNullPtr textureContainer, uint8 index) { ID3D12Resource* swapChainTexture = nullptr; - HRESULT result = - swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast(&swapChainTexture)); + HRESULT result = swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast(&swapChainTexture)); if (FAILED(result)) { LogError(driver->D3D12Device, "Cannot get buffer from SwapChain", result); @@ -77,7 +76,7 @@ namespace Juliet::D3D12 texture->Subresources[0].Depth = 1; texture->Subresources[0].Level = 0; - D3D12_RESOURCE_DESC textureDesc = swapChainTexture->GetDesc(); + D3D12_RESOURCE_DESC textureDesc = swapChainTexture->GetDesc(); textureContainer->Header.CreateInfo.Width = static_cast(textureDesc.Width); textureContainer->Header.CreateInfo.Height = static_cast(textureDesc.Height); textureContainer->Header.CreateInfo.LayerCount = 1; @@ -115,7 +114,7 @@ namespace Juliet::D3D12 rtvDesc.Texture2D.PlaneSlice = 0; driver->D3D12Device->CreateRenderTargetView(swapChainTexture, &rtvDesc, - texture->Subresources[0].RTVHandles[0].CpuHandle); + texture->Subresources[0].RTVHandles[0].CpuHandle); swapChainTexture->Release(); @@ -160,16 +159,17 @@ namespace Juliet::D3D12 windowData->InFlightFences[windowData->WindowFrameCounter] = nullptr; } - uint32 swapchainIndex = windowData->SwapChain->GetCurrentBackBufferIndex(); - HRESULT result = - windowData->SwapChain->GetBuffer(swapchainIndex, IID_ID3D12Resource, reinterpret_cast(&windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource)); + uint32 swapchainIndex = windowData->SwapChain->GetCurrentBackBufferIndex(); + HRESULT result = windowData->SwapChain->GetBuffer( + swapchainIndex, IID_ID3D12Resource, + reinterpret_cast(&windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource)); if (FAILED(result)) { LogError(driver->D3D12Device, "Could not acquire swapchain", result); return false; } - // When the swap chain texture is acquired its time to present + // When the swap chain texture is acquired it's time to present if (d3d12CommandList->PresentDataCount == d3d12CommandList->PresentDataCapacity) { d3d12CommandList->PresentDataCapacity += 1; @@ -290,9 +290,9 @@ namespace Juliet::D3D12 swapChainFullscreenDesc.Windowed = true; IDXGISwapChain1* swapChain = nullptr; - HRESULT result = - driver->DXGIFactory->CreateSwapChainForHwnd(static_cast(driver->GraphicsQueue), - windowHandle, &swapChainDesc, &swapChainFullscreenDesc, nullptr, &swapChain); + HRESULT result = driver->DXGIFactory->CreateSwapChainForHwnd(static_cast(driver->GraphicsQueue), + windowHandle, &swapChainDesc, + &swapChainFullscreenDesc, nullptr, &swapChain); if (FAILED(result)) { LogError(driver->D3D12Device, "Failed to create SwapChain", result); diff --git a/Juliet/src/Graphics/DebugDisplayRenderer.cpp b/Juliet/src/Graphics/DebugDisplayRenderer.cpp index 7f86937..af2c337 100644 --- a/Juliet/src/Graphics/DebugDisplayRenderer.cpp +++ b/Juliet/src/Graphics/DebugDisplayRenderer.cpp @@ -26,7 +26,7 @@ namespace Juliet GraphicsPipeline* OverlayPipeline; // Vertex data (CPU side - single array with two regions) - DebugVertex* Vertices; // Single allocation for all vertices + DebugVertex* Vertices; // Single allocation for all vertices uint32 DepthTestedVertexCount; uint32 OverlayVertexCount; @@ -122,14 +122,14 @@ namespace Juliet createInfo.TargetInfo.ColorTargetDescriptions = &colorDesc; createInfo.TargetInfo.NumColorTargets = 1; - + // Now that we support depth-stencil targets in the backend, we can enable them. - createInfo.TargetInfo.HasDepthStencilTarget = true; - createInfo.TargetInfo.DepthStencilFormat = TextureFormat::D32_FLOAT; + createInfo.TargetInfo.HasDepthStencilTarget = true; + createInfo.TargetInfo.DepthStencilFormat = TextureFormat::D32_FLOAT; if (enableDepthTest) { - createInfo.DepthStencilState.EnableDepthTest = true; + createInfo.DepthStencilState.EnableDepthTest = true; createInfo.DepthStencilState.EnableDepthWrite = true; createInfo.DepthStencilState.CompareOperation = CompareOperation::Less; } @@ -160,7 +160,7 @@ namespace Juliet g_DebugState.Device = device; // Allocate single CPU vertex array (depth-tested in first half, overlay in second half) - g_DebugState.Vertices = static_cast(Malloc(kMaxDebugVertices * sizeof(DebugVertex))); + g_DebugState.Vertices = static_cast(Malloc(kMaxDebugVertices * sizeof(DebugVertex))); g_DebugState.DepthTestedVertexCount = 0; g_DebugState.OverlayVertexCount = 0; @@ -264,9 +264,9 @@ namespace Juliet // Copy overlay vertices (at kMaxDebugVertices/2 offset) if (g_DebugState.OverlayVertexCount > 0) { - uint32 halfMax = kMaxDebugVertices / 2; - auto* overlayDest = static_cast(ptr) + halfMax * sizeof(DebugVertex); - auto* overlaySrc = g_DebugState.Vertices + halfMax; + uint32 halfMax = kMaxDebugVertices / 2; + auto* overlayDest = static_cast(ptr) + halfMax * sizeof(DebugVertex); + auto* overlaySrc = g_DebugState.Vertices + halfMax; MemCopy(overlayDest, overlaySrc, g_DebugState.OverlayVertexCount * sizeof(DebugVertex)); } UnmapGraphicsTransferBuffer(g_DebugState.Device, g_DebugState.TransferBuffer); @@ -280,7 +280,6 @@ namespace Juliet void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera) { - if (!g_DebugState.Initialized) { return; @@ -291,7 +290,7 @@ namespace Juliet { // Use B8G8R8A8_UNORM which matches the SDR swapchain g_DebugState.DepthTestedPipeline = CreateDebugPipeline(g_DebugState.Device, TextureFormat::B8G8R8A8_UNORM, true); - g_DebugState.OverlayPipeline = CreateDebugPipeline(g_DebugState.Device, TextureFormat::B8G8R8A8_UNORM, false); + g_DebugState.OverlayPipeline = CreateDebugPipeline(g_DebugState.Device, TextureFormat::B8G8R8A8_UNORM, false); } uint32 bufferIndex = GetDescriptorIndex(g_DebugState.Device, g_DebugState.VertexBuffer); @@ -300,9 +299,10 @@ namespace Juliet if (g_DebugState.DepthTestedVertexCount > 0 && g_DebugState.DepthTestedPipeline && g_DebugState.VertexBuffer) { BindGraphicsPipeline(renderPass, g_DebugState.DepthTestedPipeline); - + // Pack VP matrix + buffer index into push constants - struct { + struct + { Matrix vp; uint32 bufferIndex; uint32 vertexOffset; // Offset in vertices (not bytes) @@ -312,7 +312,7 @@ namespace Juliet pushData.bufferIndex = bufferIndex; pushData.vertexOffset = 0; // Depth-tested vertices start at 0 SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / sizeof(uint32), &pushData); - + DrawPrimitives(renderPass, g_DebugState.DepthTestedVertexCount, 1, 0, 0); } @@ -320,9 +320,10 @@ namespace Juliet if (g_DebugState.OverlayVertexCount > 0 && g_DebugState.OverlayPipeline && g_DebugState.VertexBuffer) { BindGraphicsPipeline(renderPass, g_DebugState.OverlayPipeline); - + // Pack VP matrix + buffer index into push constants - struct { + struct + { Matrix vp; uint32 bufferIndex; uint32 vertexOffset; // Offset in vertices (not bytes) @@ -332,7 +333,7 @@ namespace Juliet pushData.bufferIndex = bufferIndex; pushData.vertexOffset = kMaxDebugVertices / 2; // Overlay vertices start at half SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / sizeof(uint32), &pushData); - + DrawPrimitives(renderPass, g_DebugState.OverlayVertexCount, 1, 0, 0); } diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index cb946ab..8bfe4f1 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -173,7 +173,6 @@ void JulietApplication::Init() CopyBuffer(initCmd, ConstantBuffer, TransferBuffer, 256); TransitionBufferToReadable(initCmd, ConstantBuffer); SubmitCommandLists(initCmd); - WaitUntilGPUIsIdle(GraphicsDevice); } } @@ -284,7 +283,6 @@ void JulietApplication::Update() ImGui::ShowDemoWindow(); #endif - // Debug display shapes - can be called from anywhere before engine flush DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 10.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true);