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 <Core/Memory/Allocator.h>
|
||||||
#include <Graphics/D3D12/D3D12CommandList.h>
|
|
||||||
#include <Graphics/D3D12/D3D12CommandList.h>
|
|
||||||
#include <Graphics/D3D12/D3D12Buffer.h>
|
#include <Graphics/D3D12/D3D12Buffer.h>
|
||||||
|
#include <Graphics/D3D12/D3D12CommandList.h>
|
||||||
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
||||||
#include <Graphics/D3D12/D3D12Synchronization.h>
|
#include <Graphics/D3D12/D3D12Synchronization.h>
|
||||||
#include <Graphics/D3D12/D3D12Utils.h>
|
#include <Graphics/D3D12/D3D12Utils.h>
|
||||||
@@ -28,7 +27,8 @@ namespace Juliet::D3D12
|
|||||||
bool CreateAllocator(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12CommandListBaseData> baseData,
|
bool CreateAllocator(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12CommandListBaseData> baseData,
|
||||||
D3D12_COMMAND_QUEUE_DESC queueDesc)
|
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))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
AssertHR(result, "Cannot create ID3D12CommandAllocator");
|
AssertHR(result, "Cannot create ID3D12CommandAllocator");
|
||||||
@@ -96,8 +96,10 @@ namespace Juliet::D3D12
|
|||||||
{
|
{
|
||||||
CreateAllocator(driver, &commandList->CopyCommandList, queueDesc);
|
CreateAllocator(driver, &commandList->CopyCommandList, queueDesc);
|
||||||
ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr;
|
ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr;
|
||||||
HRESULT result = driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type,
|
HRESULT result =
|
||||||
D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList, reinterpret_cast<void**>(&d3d12CopyCommandList));
|
driver->D3D12Device->CreateCommandList1(queueDesc.NodeMask, queueDesc.Type,
|
||||||
|
D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList,
|
||||||
|
reinterpret_cast<void**>(&d3d12CopyCommandList));
|
||||||
|
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
@@ -392,8 +394,8 @@ namespace Juliet::D3D12
|
|||||||
{
|
{
|
||||||
auto d3d12CommandList = reinterpret_cast<D3D12CommandList*>(commandList.Get());
|
auto d3d12CommandList = reinterpret_cast<D3D12CommandList*>(commandList.Get());
|
||||||
// For now we assume Graphics Root Signature. Compute support would need a check or separate function.
|
// For now we assume Graphics Root Signature. Compute support would need a check or separate function.
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRoot32BitConstants(
|
d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRoot32BitConstants(rootParameterIndex,
|
||||||
rootParameterIndex, numConstants, constants, 0);
|
numConstants, constants, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Internal
|
namespace Internal
|
||||||
@@ -409,7 +411,7 @@ namespace Juliet::D3D12
|
|||||||
samplerHeap = AcquireSamplerHeapFromPool(commandList->Driver);
|
samplerHeap = AcquireSamplerHeapFromPool(commandList->Driver);
|
||||||
|
|
||||||
commandList->CRB_SRV_UAV_Heap = viewHeap;
|
commandList->CRB_SRV_UAV_Heap = viewHeap;
|
||||||
commandList->RTV_Heap = samplerHeap;
|
commandList->Sampler_Heap = samplerHeap;
|
||||||
|
|
||||||
heaps[0] = viewHeap->Handle;
|
heaps[0] = viewHeap->Handle;
|
||||||
heaps[1] = samplerHeap->Handle;
|
heaps[1] = samplerHeap->Handle;
|
||||||
@@ -445,19 +447,16 @@ namespace Juliet::D3D12
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = commandList->GraphicsCommandList.CommandList->Reset(
|
result = commandList->GraphicsCommandList.CommandList->Reset(commandList->GraphicsCommandList.Allocator, nullptr);
|
||||||
commandList->GraphicsCommandList.Allocator, nullptr);
|
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
LogError(driver->D3D12Device, "Could not reset command list", result);
|
LogError(driver->D3D12Device, "Could not reset command list", result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return heap descriptor to pool
|
ReturnSamplerHeapToPool(driver, commandList->Sampler_Heap);
|
||||||
// CRB_SRV_UAV_Heap is global bindless, do not return it.
|
commandList->Sampler_Heap = nullptr;
|
||||||
ReturnSamplerHeapToPool(driver, commandList->RTV_Heap);
|
|
||||||
commandList->CRB_SRV_UAV_Heap = nullptr;
|
commandList->CRB_SRV_UAV_Heap = nullptr;
|
||||||
commandList->RTV_Heap = nullptr;
|
|
||||||
|
|
||||||
// Clean up resource tracking
|
// Clean up resource tracking
|
||||||
for (uint32 idx = 0; idx < commandList->UsedTextureCount; ++idx)
|
for (uint32 idx = 0; idx < commandList->UsedTextureCount; ++idx)
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ namespace Juliet::D3D12
|
|||||||
|
|
||||||
struct D3D12CommandList
|
struct D3D12CommandList
|
||||||
{
|
{
|
||||||
|
CommandListHeader Common;
|
||||||
|
|
||||||
uint64 ID;
|
uint64 ID;
|
||||||
|
|
||||||
CommandListHeader Common;
|
|
||||||
D3D12Driver* Driver;
|
D3D12Driver* Driver;
|
||||||
|
|
||||||
D3D12PresentData* PresentDatas;
|
D3D12PresentData* PresentDatas;
|
||||||
@@ -77,7 +78,7 @@ namespace Juliet::D3D12
|
|||||||
// D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage];
|
// D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage];
|
||||||
|
|
||||||
Internal::D3D12DescriptorHeap* CRB_SRV_UAV_Heap;
|
Internal::D3D12DescriptorHeap* CRB_SRV_UAV_Heap;
|
||||||
Internal::D3D12DescriptorHeap* RTV_Heap;
|
Internal::D3D12DescriptorHeap* Sampler_Heap;
|
||||||
|
|
||||||
// Resource Tracking
|
// Resource Tracking
|
||||||
D3D12Texture** UsedTextures;
|
D3D12Texture** UsedTextures;
|
||||||
@@ -97,7 +98,8 @@ 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 SetStencilReference(NonNullPtr<CommandList> commandList, uint8 reference);
|
||||||
extern void SetIndexBuffer(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer, IndexFormat format);
|
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 SetPushConstants(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex,
|
||||||
|
uint32 numConstants, const void* constants);
|
||||||
|
|
||||||
namespace Internal
|
namespace Internal
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <Core/Logging/LogManager.h>
|
||||||
|
#include <Core/Logging/LogTypes.h>
|
||||||
#include <Core/Memory/Allocator.h>
|
#include <Core/Memory/Allocator.h>
|
||||||
#include <Core/Memory/EngineArena.h>
|
#include <Core/Memory/EngineArena.h>
|
||||||
#include <Graphics/D3D12/D3D12DescriptorHeap.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)
|
D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)
|
||||||
{
|
{
|
||||||
ID3D12DescriptorHeap* handle;
|
|
||||||
|
|
||||||
auto heap = ArenaPushType<D3D12DescriptorHeap>(GetEngineArena(), ConstString("D3D12DescriptorHeap"));
|
auto heap = ArenaPushType<D3D12DescriptorHeap>(GetEngineArena(), ConstString("D3D12DescriptorHeap"));
|
||||||
if (!heap)
|
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.Flags = isStaging ? D3D12_DESCRIPTOR_HEAP_FLAG_NONE : D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||||
heapDesc.NodeMask = 0;
|
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))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
LogError(driver->D3D12Device, "Failed to create descriptor heap!", result);
|
LogError(driver->D3D12Device, "Failed to create descriptor heap!", result);
|
||||||
@@ -101,8 +103,10 @@ namespace Juliet::D3D12::Internal
|
|||||||
|
|
||||||
if (heap->FreeIndicesCount >= heap->FreeIndicesCapacity)
|
if (heap->FreeIndicesCount >= heap->FreeIndicesCapacity)
|
||||||
{
|
{
|
||||||
|
size_t oldCapacity = heap->FreeIndicesCapacity;
|
||||||
heap->FreeIndicesCapacity *= 2;
|
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;
|
heap->FreeIndices[heap->FreeIndicesCount] = descriptor.Index;
|
||||||
@@ -111,62 +115,20 @@ namespace Juliet::D3D12::Internal
|
|||||||
|
|
||||||
D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver, DescriptorHeapCreator creator)
|
D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver, DescriptorHeapCreator creator)
|
||||||
{
|
{
|
||||||
|
D3D12DescriptorHeap* result;
|
||||||
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
|
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
|
||||||
uint32 count = GPUDriver::kSampler_HeapDescriptorCount;
|
|
||||||
|
|
||||||
if (pool->Count > 0)
|
if (pool->Count > 0)
|
||||||
{
|
{
|
||||||
|
result = pool->Heaps[pool->Count - 1];
|
||||||
pool->Count -= 1;
|
pool->Count -= 1;
|
||||||
return pool->Heaps[pool->Count];
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
if (pool->Heaps[i] == nullptr)
|
result = creator(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, GPUDriver::kSampler_HeapDescriptorCount, false);
|
||||||
{
|
|
||||||
freeSlotIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freeSlotIndex == SIZE_MAX)
|
return result;
|
||||||
{
|
|
||||||
// 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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, D3D12DescriptorHeap* heap)
|
void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, D3D12DescriptorHeap* heap)
|
||||||
@@ -176,40 +138,18 @@ namespace Juliet::D3D12::Internal
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(heap->HeapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
|
||||||
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
|
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
|
||||||
|
|
||||||
heap->CurrentDescriptorIndex = 0;
|
heap->CurrentDescriptorIndex = 0;
|
||||||
|
|
||||||
// The heap is currently in the "Used" partition (index >= Count).
|
if (pool->Count >= pool->Capacity)
|
||||||
// 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->Heaps[i] == heap)
|
size_t oldCapacity = pool->Capacity;
|
||||||
{
|
pool->Capacity *= 2;
|
||||||
heapIndex = i;
|
pool->Heaps = ArenaRealloc(GetEngineArena(), pool->Heaps, oldCapacity, pool->Capacity, ConstString("Heaps"));
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heapIndex != SIZE_MAX)
|
pool->Heaps[pool->Count] = heap;
|
||||||
{
|
|
||||||
D3D12DescriptorHeap* temp = pool->Heaps[pool->Count];
|
|
||||||
pool->Heaps[pool->Count] = pool->Heaps[heapIndex];
|
|
||||||
pool->Heaps[heapIndex] = temp;
|
|
||||||
pool->Count += 1;
|
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!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace Juliet::D3D12::Internal
|
} // namespace Juliet::D3D12::Internal
|
||||||
|
|||||||
@@ -742,13 +742,7 @@ namespace Juliet::D3D12
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
GraphicsDevice* CreateGraphicsDevice(bool enableDebug)
|
GraphicsDevice* CreateGraphicsDevice(bool enableDebug)
|
||||||
|
|
||||||
{
|
{
|
||||||
#if JULIET_DEBUG
|
|
||||||
// Unit Tests for D3D12 Logic
|
|
||||||
UnitTest::TestDescriptorHeapPool();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
auto driver = static_cast<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver)));
|
auto driver = static_cast<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver)));
|
||||||
|
|
||||||
#if JULIET_DEBUG
|
#if JULIET_DEBUG
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <Graphics/D3D12/D3D12InternalTests.h>
|
|
||||||
#include <Core/Memory/Allocator.h>
|
#include <Core/Memory/Allocator.h>
|
||||||
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
|
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
|
||||||
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
|
||||||
|
#include <Graphics/D3D12/D3D12InternalTests.h>
|
||||||
|
|
||||||
#if JULIET_DEBUG
|
#if JULIET_DEBUG
|
||||||
|
|
||||||
@@ -9,136 +9,5 @@ namespace Juliet::D3D12::UnitTest
|
|||||||
{
|
{
|
||||||
using namespace Juliet::D3D12;
|
using namespace Juliet::D3D12;
|
||||||
using namespace Juliet::D3D12::Internal;
|
using namespace Juliet::D3D12::Internal;
|
||||||
|
} // namespace Juliet::D3D12::UnitTest
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,6 +4,5 @@
|
|||||||
#if JULIET_DEBUG
|
#if JULIET_DEBUG
|
||||||
namespace Juliet::D3D12::UnitTest
|
namespace Juliet::D3D12::UnitTest
|
||||||
{
|
{
|
||||||
void TestDescriptorHeapPool();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ namespace Juliet::D3D12
|
|||||||
frameBufferWidth = Min(width, frameBufferWidth);
|
frameBufferWidth = Min(width, frameBufferWidth);
|
||||||
frameBufferHeight = Min(height, frameBufferHeight);
|
frameBufferHeight = Min(height, frameBufferHeight);
|
||||||
|
|
||||||
D3D12TextureSubresource* subresource = Internal::PrepareTextureSubresourceForWrite(
|
D3D12TextureSubresource* subresource =
|
||||||
d3d12CommandList, container, 0, 0, false, D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
Internal::PrepareTextureSubresourceForWrite(d3d12CommandList, container, 0, 0, false, D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||||
|
|
||||||
DSV = subresource->DSVHandle.CpuHandle;
|
DSV = subresource->DSVHandle.CpuHandle;
|
||||||
hasDSV = true;
|
hasDSV = true;
|
||||||
@@ -67,9 +67,10 @@ namespace Juliet::D3D12
|
|||||||
// TODO: Check if texture has stencil
|
// TODO: Check if texture has stencil
|
||||||
// if (HasStencil(container->Header.CreateInfo.Format)) clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
// if (HasStencil(container->Header.CreateInfo.Format)) clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
||||||
|
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->ClearDepthStencilView(DSV,
|
d3d12CommandList->GraphicsCommandList.CommandList->ClearDepthStencilView(DSV, clearFlags,
|
||||||
clearFlags, depthStencilTargetInfo->ClearDepth,
|
depthStencilTargetInfo->ClearDepth,
|
||||||
depthStencilTargetInfo->ClearStencil, 0, nullptr);
|
depthStencilTargetInfo->ClearStencil,
|
||||||
|
0, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +94,7 @@ namespace Juliet::D3D12
|
|||||||
clearColor[2] = colorTargetInfos[idx].ClearColor.B;
|
clearColor[2] = colorTargetInfos[idx].ClearColor.B;
|
||||||
clearColor[3] = colorTargetInfos[idx].ClearColor.A;
|
clearColor[3] = colorTargetInfos[idx].ClearColor.A;
|
||||||
|
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->ClearRenderTargetView(rtv,
|
d3d12CommandList->GraphicsCommandList.CommandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
|
||||||
clearColor, 0, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RTVs[idx] = rtv;
|
RTVs[idx] = rtv;
|
||||||
@@ -119,8 +119,8 @@ namespace Juliet::D3D12
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->OMSetRenderTargets(
|
d3d12CommandList->GraphicsCommandList.CommandList->OMSetRenderTargets(colorTargetInfoCount, RTVs, false,
|
||||||
colorTargetInfoCount, RTVs, false, hasDSV ? &DSV : nullptr);
|
hasDSV ? &DSV : nullptr);
|
||||||
|
|
||||||
// Set defaults graphics states
|
// Set defaults graphics states
|
||||||
GraphicsViewPort defaultViewport;
|
GraphicsViewPort defaultViewport;
|
||||||
@@ -191,8 +191,7 @@ namespace Juliet::D3D12
|
|||||||
// Reset Depth Stencil state
|
// Reset Depth Stencil state
|
||||||
if (d3d12CommandList->DepthStencilSubresource)
|
if (d3d12CommandList->DepthStencilSubresource)
|
||||||
{
|
{
|
||||||
Internal::TextureSubresourceTransitionToDefaultUsage(d3d12CommandList,
|
Internal::TextureSubresourceTransitionToDefaultUsage(d3d12CommandList, d3d12CommandList->DepthStencilSubresource,
|
||||||
d3d12CommandList->DepthStencilSubresource,
|
|
||||||
D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||||
d3d12CommandList->DepthStencilSubresource = nullptr;
|
d3d12CommandList->DepthStencilSubresource = nullptr;
|
||||||
}
|
}
|
||||||
@@ -232,12 +231,14 @@ namespace Juliet::D3D12
|
|||||||
d3d12CommandList->CurrentGraphicsPipeline = pipeline;
|
d3d12CommandList->CurrentGraphicsPipeline = pipeline;
|
||||||
|
|
||||||
// Set the Descriptor heap
|
// Set the Descriptor heap
|
||||||
|
if (d3d12CommandList->CRB_SRV_UAV_Heap == nullptr)
|
||||||
|
{
|
||||||
Internal::SetDescriptorHeaps(d3d12CommandList);
|
Internal::SetDescriptorHeaps(d3d12CommandList);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the pipeline state
|
// Set the pipeline state
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->SetPipelineState(pipeline->PipelineState);
|
d3d12CommandList->GraphicsCommandList.CommandList->SetPipelineState(pipeline->PipelineState);
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRootSignature(
|
d3d12CommandList->GraphicsCommandList.CommandList->SetGraphicsRootSignature(pipeline->RootSignature->Handle);
|
||||||
pipeline->RootSignature->Handle);
|
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->IASetPrimitiveTopology(
|
d3d12CommandList->GraphicsCommandList.CommandList->IASetPrimitiveTopology(
|
||||||
JulietToD3D12_PrimitiveType[ToUnderlying(pipeline->PrimitiveType)]);
|
JulietToD3D12_PrimitiveType[ToUnderlying(pipeline->PrimitiveType)]);
|
||||||
|
|
||||||
@@ -280,7 +281,6 @@ namespace Juliet::D3D12
|
|||||||
// TODO : Last missing piece
|
// TODO : Last missing piece
|
||||||
// D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
// D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
||||||
|
|
||||||
d3d12CommandList->GraphicsCommandList.CommandList->DrawInstanced(numVertices,
|
d3d12CommandList->GraphicsCommandList.CommandList->DrawInstanced(numVertices, numInstances, firstVertex, firstInstance);
|
||||||
numInstances, firstVertex, firstInstance);
|
|
||||||
}
|
}
|
||||||
} // namespace Juliet::D3D12
|
} // namespace Juliet::D3D12
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ namespace Juliet::D3D12
|
|||||||
SwapChainComposition composition, NonNullPtr<D3D12TextureContainer> textureContainer, uint8 index)
|
SwapChainComposition composition, NonNullPtr<D3D12TextureContainer> textureContainer, uint8 index)
|
||||||
{
|
{
|
||||||
ID3D12Resource* swapChainTexture = nullptr;
|
ID3D12Resource* swapChainTexture = nullptr;
|
||||||
HRESULT result =
|
HRESULT result = swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast<void**>(&swapChainTexture));
|
||||||
swapChain->GetBuffer(index, IID_ID3D12Resource, reinterpret_cast<void**>(&swapChainTexture));
|
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
LogError(driver->D3D12Device, "Cannot get buffer from SwapChain", result);
|
LogError(driver->D3D12Device, "Cannot get buffer from SwapChain", result);
|
||||||
@@ -161,15 +160,16 @@ namespace Juliet::D3D12
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32 swapchainIndex = windowData->SwapChain->GetCurrentBackBufferIndex();
|
uint32 swapchainIndex = windowData->SwapChain->GetCurrentBackBufferIndex();
|
||||||
HRESULT result =
|
HRESULT result = windowData->SwapChain->GetBuffer(
|
||||||
windowData->SwapChain->GetBuffer(swapchainIndex, IID_ID3D12Resource, reinterpret_cast<void**>(&windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource));
|
swapchainIndex, IID_ID3D12Resource,
|
||||||
|
reinterpret_cast<void**>(&windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource));
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
LogError(driver->D3D12Device, "Could not acquire swapchain", result);
|
LogError(driver->D3D12Device, "Could not acquire swapchain", result);
|
||||||
return false;
|
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)
|
if (d3d12CommandList->PresentDataCount == d3d12CommandList->PresentDataCapacity)
|
||||||
{
|
{
|
||||||
d3d12CommandList->PresentDataCapacity += 1;
|
d3d12CommandList->PresentDataCapacity += 1;
|
||||||
@@ -290,9 +290,9 @@ namespace Juliet::D3D12
|
|||||||
swapChainFullscreenDesc.Windowed = true;
|
swapChainFullscreenDesc.Windowed = true;
|
||||||
|
|
||||||
IDXGISwapChain1* swapChain = nullptr;
|
IDXGISwapChain1* swapChain = nullptr;
|
||||||
HRESULT result =
|
HRESULT result = driver->DXGIFactory->CreateSwapChainForHwnd(static_cast<IUnknown*>(driver->GraphicsQueue),
|
||||||
driver->DXGIFactory->CreateSwapChainForHwnd(static_cast<IUnknown*>(driver->GraphicsQueue),
|
windowHandle, &swapChainDesc,
|
||||||
windowHandle, &swapChainDesc, &swapChainFullscreenDesc, nullptr, &swapChain);
|
&swapChainFullscreenDesc, nullptr, &swapChain);
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
LogError(driver->D3D12Device, "Failed to create SwapChain", result);
|
LogError(driver->D3D12Device, "Failed to create SwapChain", result);
|
||||||
|
|||||||
@@ -280,7 +280,6 @@ namespace Juliet
|
|||||||
|
|
||||||
void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera)
|
void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!g_DebugState.Initialized)
|
if (!g_DebugState.Initialized)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -302,7 +301,8 @@ namespace Juliet
|
|||||||
BindGraphicsPipeline(renderPass, g_DebugState.DepthTestedPipeline);
|
BindGraphicsPipeline(renderPass, g_DebugState.DepthTestedPipeline);
|
||||||
|
|
||||||
// Pack VP matrix + buffer index into push constants
|
// Pack VP matrix + buffer index into push constants
|
||||||
struct {
|
struct
|
||||||
|
{
|
||||||
Matrix vp;
|
Matrix vp;
|
||||||
uint32 bufferIndex;
|
uint32 bufferIndex;
|
||||||
uint32 vertexOffset; // Offset in vertices (not bytes)
|
uint32 vertexOffset; // Offset in vertices (not bytes)
|
||||||
@@ -322,7 +322,8 @@ namespace Juliet
|
|||||||
BindGraphicsPipeline(renderPass, g_DebugState.OverlayPipeline);
|
BindGraphicsPipeline(renderPass, g_DebugState.OverlayPipeline);
|
||||||
|
|
||||||
// Pack VP matrix + buffer index into push constants
|
// Pack VP matrix + buffer index into push constants
|
||||||
struct {
|
struct
|
||||||
|
{
|
||||||
Matrix vp;
|
Matrix vp;
|
||||||
uint32 bufferIndex;
|
uint32 bufferIndex;
|
||||||
uint32 vertexOffset; // Offset in vertices (not bytes)
|
uint32 vertexOffset; // Offset in vertices (not bytes)
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ void JulietApplication::Init()
|
|||||||
CopyBuffer(initCmd, ConstantBuffer, TransferBuffer, 256);
|
CopyBuffer(initCmd, ConstantBuffer, TransferBuffer, 256);
|
||||||
TransitionBufferToReadable(initCmd, ConstantBuffer);
|
TransitionBufferToReadable(initCmd, ConstantBuffer);
|
||||||
SubmitCommandLists(initCmd);
|
SubmitCommandLists(initCmd);
|
||||||
WaitUntilGPUIsIdle(GraphicsDevice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +283,6 @@ void JulietApplication::Update()
|
|||||||
ImGui::ShowDemoWindow();
|
ImGui::ShowDemoWindow();
|
||||||
#endif
|
#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 }, { 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, 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);
|
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