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
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <Graphics/D3D12/D3D12CommandList.h>
|
||||
#include <Graphics/D3D12/D3D12CommandList.h>
|
||||
#include <Graphics/D3D12/D3D12Buffer.h>
|
||||
#include <Graphics/D3D12/D3D12CommandList.h>
|
||||
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
||||
#include <Graphics/D3D12/D3D12Synchronization.h>
|
||||
#include <Graphics/D3D12/D3D12Utils.h>
|
||||
@@ -28,7 +27,8 @@ namespace Juliet::D3D12
|
||||
bool CreateAllocator(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12CommandListBaseData> baseData,
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc)
|
||||
{
|
||||
HRESULT result = driver->D3D12Device->CreateCommandAllocator(queueDesc.Type, IID_ID3D12CommandAllocator, reinterpret_cast<void**>(&baseData->Allocator));
|
||||
HRESULT result = driver->D3D12Device->CreateCommandAllocator(queueDesc.Type, IID_ID3D12CommandAllocator,
|
||||
reinterpret_cast<void**>(&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<void**>(&d3d12GraphicsCommandList));
|
||||
D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6,
|
||||
reinterpret_cast<void**>(&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<void**>(&d3d12GraphicsCommandList));
|
||||
D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6,
|
||||
reinterpret_cast<void**>(&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<void**>(&d3d12CopyCommandList));
|
||||
HRESULT result =
|
||||
driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type,
|
||||
D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList,
|
||||
reinterpret_cast<void**>(&d3d12CopyCommandList));
|
||||
|
||||
if (FAILED(result))
|
||||
{
|
||||
@@ -392,8 +394,8 @@ namespace Juliet::D3D12
|
||||
{
|
||||
auto d3d12CommandList = reinterpret_cast<D3D12CommandList*>(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)
|
||||
|
||||
@@ -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> commandList, FColor blendConstants);
|
||||
extern void SetBlendConstants(NonNullPtr<CommandList> commandList, FColor blendConstants);
|
||||
extern void SetStencilReference(NonNullPtr<CommandList> commandList, uint8 reference);
|
||||
extern void SetIndexBuffer(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer, IndexFormat format);
|
||||
extern void SetPushConstants(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex, uint32 numConstants, const void* constants);
|
||||
extern void SetIndexBuffer(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer, IndexFormat format);
|
||||
extern void SetPushConstants(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex,
|
||||
uint32 numConstants, const void* constants);
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Logging/LogTypes.h>
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <Core/Memory/EngineArena.h>
|
||||
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
|
||||
@@ -8,8 +10,6 @@ namespace Juliet::D3D12::Internal
|
||||
{
|
||||
D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)
|
||||
{
|
||||
ID3D12DescriptorHeap* handle;
|
||||
|
||||
auto heap = ArenaPushType<D3D12DescriptorHeap>(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<void**>(&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<uint32*>(Realloc(heap->FreeIndices, heap->FreeIndicesCapacity * sizeof(uint32)));
|
||||
heap->FreeIndices = ArenaRealloc<uint32>(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> 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<D3D12DescriptorHeap*>(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> 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
|
||||
|
||||
@@ -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<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver)));
|
||||
|
||||
#if JULIET_DEBUG
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <Graphics/D3D12/D3D12InternalTests.h>
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
|
||||
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
||||
#include <Graphics/D3D12/D3D12InternalTests.h>
|
||||
|
||||
#if JULIET_DEBUG
|
||||
|
||||
@@ -9,136 +9,5 @@ namespace Juliet::D3D12::UnitTest
|
||||
{
|
||||
using namespace Juliet::D3D12;
|
||||
using namespace Juliet::D3D12::Internal;
|
||||
|
||||
static D3D12DescriptorHeap* MockCreateDescriptorHeap(NonNullPtr<D3D12Driver>, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32, bool)
|
||||
{
|
||||
D3D12DescriptorHeap* heap = static_cast<D3D12DescriptorHeap*>(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<D3D12Driver> 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
|
||||
|
||||
@@ -4,6 +4,5 @@
|
||||
#if JULIET_DEBUG
|
||||
namespace Juliet::D3D12::UnitTest
|
||||
{
|
||||
void TestDescriptorHeapPool();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -45,15 +45,15 @@ namespace Juliet::D3D12
|
||||
bool hasDSV = false;
|
||||
if (depthStencilTargetInfo && depthStencilTargetInfo->TargetTexture)
|
||||
{
|
||||
auto* container = reinterpret_cast<D3D12TextureContainer*>(depthStencilTargetInfo->TargetTexture);
|
||||
uint32 width = container->Header.CreateInfo.Width;
|
||||
uint32 height = container->Header.CreateInfo.Height;
|
||||
auto* container = reinterpret_cast<D3D12TextureContainer*>(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
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace Juliet::D3D12
|
||||
SwapChainComposition composition, NonNullPtr<D3D12TextureContainer> textureContainer, uint8 index)
|
||||
{
|
||||
ID3D12Resource* swapChainTexture = nullptr;
|
||||
HRESULT result =
|
||||
swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast<void**>(&swapChainTexture));
|
||||
HRESULT result = swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast<void**>(&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<uint32>(textureDesc.Width);
|
||||
textureContainer->Header.CreateInfo.Height = static_cast<uint32>(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<void**>(&windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource));
|
||||
uint32 swapchainIndex = windowData->SwapChain->GetCurrentBackBufferIndex();
|
||||
HRESULT result = windowData->SwapChain->GetBuffer(
|
||||
swapchainIndex, IID_ID3D12Resource,
|
||||
reinterpret_cast<void**>(&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<IUnknown*>(driver->GraphicsQueue),
|
||||
windowHandle, &swapChainDesc, &swapChainFullscreenDesc, nullptr, &swapChain);
|
||||
HRESULT result = driver->DXGIFactory->CreateSwapChainForHwnd(static_cast<IUnknown*>(driver->GraphicsQueue),
|
||||
windowHandle, &swapChainDesc,
|
||||
&swapChainFullscreenDesc, nullptr, &swapChain);
|
||||
if (FAILED(result))
|
||||
{
|
||||
LogError(driver->D3D12Device, "Failed to create SwapChain", result);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -124,8 +124,8 @@ namespace Juliet
|
||||
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)
|
||||
{
|
||||
@@ -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<DebugVertex*>(Malloc(kMaxDebugVertices * sizeof(DebugVertex)));
|
||||
g_DebugState.Vertices = static_cast<DebugVertex*>(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<uint8*>(ptr) + halfMax * sizeof(DebugVertex);
|
||||
auto* overlaySrc = g_DebugState.Vertices + halfMax;
|
||||
uint32 halfMax = kMaxDebugVertices / 2;
|
||||
auto* overlayDest = static_cast<uint8*>(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);
|
||||
@@ -302,7 +301,8 @@ namespace Juliet
|
||||
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)
|
||||
@@ -322,7 +322,8 @@ namespace Juliet
|
||||
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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user