From f01c8c3ccb72a207269859ea2c477051d5766b92 Mon Sep 17 00:00:00 2001 From: Patedam Date: Thu, 13 Mar 2025 23:07:01 -0400 Subject: [PATCH] Added missing functions to allow wait for swap chain. Proper resource tracking to allow for destruction of graphics pipeline Textures are tracked but not released so nothing has been done yet various changes --- Juliet/include/Core/Common/CoreUtils.h | 8 ++ Juliet/include/Graphics/Graphics.h | 3 + .../src/Graphics/D3D12/D3D12CommandList.cpp | 131 ++++++++++++------ Juliet/src/Graphics/D3D12/D3D12CommandList.h | 19 ++- .../Graphics/D3D12/D3D12GraphicsDevice.cpp | 70 ++++++---- Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp | 6 +- Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp | 55 +++++++- Juliet/src/Graphics/D3D12/D3D12SwapChain.h | 4 +- .../Graphics/D3D12/D3D12Synchronization.cpp | 48 ++++++- .../src/Graphics/D3D12/D3D12Synchronization.h | 1 + Juliet/src/Graphics/D3D12/D3D12Texture.cpp | 2 +- Juliet/src/Graphics/D3D12/D3D12Texture.h | 2 +- Juliet/src/Graphics/Graphics.cpp | 66 ++++++++- Juliet/src/Graphics/GraphicsDevice.h | 4 +- JulietApp/main.cpp | 2 +- 15 files changed, 333 insertions(+), 88 deletions(-) diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h index a2afa97..dbc78ab 100644 --- a/Juliet/include/Core/Common/CoreUtils.h +++ b/Juliet/include/Core/Common/CoreUtils.h @@ -19,8 +19,16 @@ namespace Juliet } \ while (0) +#define Unimplemented() \ + do \ + { \ + Juliet::JulietAssert("Unimplemented!"); \ + } \ + while (0) + #else #define Assert(Expression) +#define Unimplemented() #endif extern void JULIET_API JulietAssert(const char* expression); diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index f04cf8a..d804f75 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -93,6 +93,9 @@ namespace Juliet // SwapChain extern JULIET_API bool AcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture); + extern JULIET_API bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, + NonNullPtr window, Texture** swapChainTexture); + extern JULIET_API bool WaitForSwapchain(NonNullPtr device, NonNullPtr window); extern JULIET_API TextureFormat GetSwapChainTextureFormat(NonNullPtr device, NonNullPtr window); // Command List diff --git a/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp b/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp index 9514fe2..7193c73 100644 --- a/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12CommandList.cpp @@ -24,16 +24,15 @@ namespace Juliet::D3D12 bool CreateAllocator(NonNullPtr driver, NonNullPtr baseData, D3D12_COMMAND_QUEUE_DESC queueDesc) { - for (auto& buffer : baseData->Allocator) + HRESULT result = ID3D12Device5_CreateCommandAllocator(driver->D3D12Device, queueDesc.Type, IID_ID3D12CommandAllocator, + reinterpret_cast(&baseData->Allocator)); + if (FAILED(result)) { - HRESULT result = ID3D12Device5_CreateCommandAllocator(driver->D3D12Device, queueDesc.Type, IID_ID3D12CommandAllocator, - reinterpret_cast(&buffer)); - if (FAILED(result)) - { - Assert(false && "Error not implemented: cannot create ID3D12CommandAllocator"); - return false; - } + Assert(false && "Error not implemented: cannot create ID3D12CommandAllocator"); + return false; } + + ID3D12CommandAllocator_Reset(baseData->Allocator); return true; } @@ -43,6 +42,8 @@ namespace Juliet::D3D12 std::wstring wide_str = L"CommandList ID:" + std::to_wstring(commandList->ID); // TODO: Factorize this. Flemme + + // Get Proper allocator for the frame. Reset all allocators and the command list with current frame allocator auto& queueDesc = driver->QueueDesc[ToUnderlying(queueType)]; switch (queueType) { @@ -63,6 +64,8 @@ namespace Juliet::D3D12 commandList->GraphicsCommandList.CommandList = d3d12GraphicsCommandList; ID3D12GraphicsCommandList6_SetName(d3d12GraphicsCommandList, wide_str.c_str()); + ID3D12GraphicsCommandList6_Reset(d3d12GraphicsCommandList, commandList->GraphicsCommandList.Allocator, nullptr); + return true; } case QueueType::Compute: @@ -82,6 +85,8 @@ namespace Juliet::D3D12 commandList->ComputeCommandList.CommandList = d3d12GraphicsCommandList; ID3D12GraphicsCommandList6_SetName(d3d12GraphicsCommandList, wide_str.c_str()); + ID3D12GraphicsCommandList6_Reset(d3d12GraphicsCommandList, commandList->ComputeCommandList.Allocator, nullptr); + return true; } case QueueType::Copy: @@ -99,6 +104,8 @@ namespace Juliet::D3D12 } commandList->CopyCommandList.CommandList = d3d12CopyCommandList; ID3D12GraphicsCommandList_SetName(d3d12CopyCommandList, wide_str.c_str()); + ID3D12GraphicsCommandList_Reset(d3d12CopyCommandList, commandList->CopyCommandList.Allocator, nullptr); + return true; } default: return false; @@ -136,11 +143,23 @@ namespace Juliet::D3D12 commandList->ID = id; commandList->Driver = driver; + // Window Handling commandList->PresentDataCapacity = 1; commandList->PresentDataCount = 0; commandList->PresentDatas = static_cast(Calloc(commandList->PresentDataCapacity, sizeof(D3D12PresentData))); + // Resource tracking + commandList->UsedTextureCapacity = 4; + commandList->UsedTextureCount = 0; + commandList->UsedTextures = + static_cast(Calloc(commandList->UsedTextureCapacity, sizeof(D3D12Texture*))); + + commandList->UsedGraphicsPipelineCapacity = 4; + commandList->UsedGraphicsPipelineCount = 0; + commandList->UsedGraphicsPipelines = static_cast( + Calloc(commandList->UsedGraphicsPipelineCapacity, sizeof(D3D12GraphicsPipeline*))); + // TODO : Simplify this if (!HasD3D12CommandListForQueueType(commandList, queueType)) { @@ -178,34 +197,6 @@ namespace Juliet::D3D12 D3D12CommandList* commandList = AcquireCommandListFromPool(d3d12Driver, queueType); - // Get Proper allocator for the frame and reset both it and the requested queue - uint8 bufferIndex = d3d12Driver->FrameCounter % GPUDriver::kResourceBufferCount; - switch (queueType) - { - case QueueType::Graphics: - { - auto* allocator = commandList->GraphicsCommandList.Allocator[bufferIndex]; - ID3D12CommandAllocator_Reset(allocator); - ID3D12GraphicsCommandList6_Reset(commandList->GraphicsCommandList.CommandList, allocator, nullptr); - break; - } - case QueueType::Compute: - { - auto* allocator = commandList->ComputeCommandList.Allocator[bufferIndex]; - ID3D12CommandAllocator_Reset(allocator); - ID3D12GraphicsCommandList6_Reset(commandList->ComputeCommandList.CommandList, allocator, nullptr); - break; - } - case QueueType::Copy: - { - auto* allocator = commandList->CopyCommandList.Allocator[bufferIndex]; - ID3D12CommandAllocator_Reset(allocator); - ID3D12GraphicsCommandList6_Reset(commandList->CopyCommandList.CommandList, allocator, nullptr); - break; - } - default: Assert(false && "Unsupported QueueType"); return nullptr; - } - commandList->AutoReleaseFence = true; return reinterpret_cast(commandList); @@ -384,15 +375,11 @@ namespace Juliet::D3D12 ID3D12GraphicsCommandList_Release(commandList->GraphicsCommandList.CommandList); } - for (auto* allocator : commandList->GraphicsCommandList.Allocator) - { - if (allocator) - { - ID3D12CommandAllocator_Release(allocator); - } - } + ID3D12CommandAllocator_Release(commandList->GraphicsCommandList.Allocator); SafeFree(commandList->PresentDatas); + SafeFree(commandList->UsedTextures); + SafeFree(commandList->UsedGraphicsPipelines); Free(commandList.Get()); } @@ -401,6 +388,34 @@ namespace Juliet::D3D12 // No more presentation data commandList->PresentDataCount = 0; + HRESULT result = ID3D12CommandAllocator_Reset(commandList->GraphicsCommandList.Allocator); + if (FAILED(result)) + { + LogError(driver, "Could not reset command allocator", result); + return false; + } + + result = ID3D12GraphicsCommandList_Reset(commandList->GraphicsCommandList.CommandList, + commandList->GraphicsCommandList.Allocator, NULL); + if (FAILED(result)) + { + LogError(driver, "Could not reset command list", result); + return false; + } + + // Clean up resource tracking + for (uint32 idx = 0; idx < commandList->UsedTextureCount; ++idx) + { + --commandList->UsedTextures[idx]->ReferenceCount; + } + commandList->UsedTextureCount = 0; + + for (uint32 idx = 0; idx < commandList->UsedGraphicsPipelineCount; ++idx) + { + --commandList->UsedGraphicsPipelines[idx]->ReferenceCount; + } + commandList->UsedGraphicsPipelineCount = 0; + // Release Fence if needed if (commandList->AutoReleaseFence) { @@ -434,5 +449,35 @@ namespace Juliet::D3D12 return true; } +#define TRACK_RESOURCE(resource, type, array, count, capacity) \ + uint32 i; \ + \ + for (i = 0; i < commandList->count; i += 1) \ + { \ + if (commandList->array[i] == (resource)) \ + { \ + return; \ + } \ + } \ + \ + if (commandList->count == commandList->capacity) \ + { \ + commandList->capacity += 1; \ + commandList->array = (type*)Realloc(commandList->array, commandList->capacity * sizeof(type)); \ + } \ + commandList->array[commandList->count] = resource; \ + commandList->count += 1; \ + ++(resource)->ReferenceCount; + + void TrackGraphicsPipeline(NonNullPtr commandList, NonNullPtr pipeline) + { + TRACK_RESOURCE(pipeline, D3D12GraphicsPipeline*, UsedGraphicsPipelines, UsedGraphicsPipelineCount, UsedGraphicsPipelineCapacity) + } + + void TrackTexture(NonNullPtr commandList, NonNullPtr texture) + { + TRACK_RESOURCE(texture, D3D12Texture*, UsedTextures, UsedTextureCount, UsedTextureCapacity) + } + } // namespace Internal } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12CommandList.h b/Juliet/src/Graphics/D3D12/D3D12CommandList.h index 12b5d07..06dfd71 100644 --- a/Juliet/src/Graphics/D3D12/D3D12CommandList.h +++ b/Juliet/src/Graphics/D3D12/D3D12CommandList.h @@ -11,12 +11,13 @@ namespace Juliet::D3D12 // Forward Declare struct D3D12Driver; struct D3D12Fence; + struct D3D12Texture; struct D3D12TextureSubresource; struct D3D12WindowData; struct D3D12CommandListBaseData { - ID3D12CommandAllocator* Allocator[GPUDriver::kResourceBufferCount]; + ID3D12CommandAllocator* Allocator; }; struct D3D12CopyCommandListData : D3D12CommandListBaseData @@ -70,8 +71,17 @@ namespace Juliet::D3D12 bool NeedVertexUniformBufferBind[GPUDriver::kMaxUniformBuffersPerStage]; bool NeedFragmentUniformBufferBind[GPUDriver::kMaxUniformBuffersPerStage]; - //D3D12UniformBuffer *vertexUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage]; - //D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage]; + // D3D12UniformBuffer *vertexUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage]; + // D3D12UniformBuffer *fragmentUniformBuffers[GPUDriver::kMaxUniformBuffersPerStage]; + + // Resource Tracking + D3D12Texture** UsedTextures; + uint32 UsedTextureCount; + uint32 UsedTextureCapacity; + + D3D12GraphicsPipeline** UsedGraphicsPipelines; + uint32 UsedGraphicsPipelineCount; + uint32 UsedGraphicsPipelineCapacity; }; extern CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType); @@ -85,5 +95,8 @@ namespace Juliet::D3D12 { extern void DestroyCommandList(NonNullPtr commandList); extern bool CleanCommandList(NonNullPtr driver, NonNullPtr commandList, bool cancel); + + extern void TrackGraphicsPipeline(NonNullPtr commandList, NonNullPtr pipeline); + extern void TrackTexture(NonNullPtr commandList, NonNullPtr texture); } // namespace Internal } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp index 268b9e9..2d893f8 100644 --- a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp @@ -310,6 +310,7 @@ namespace Juliet::D3D12 // Clean allocations SafeFree(driver->AvailableCommandLists); SafeFree(driver->SubmittedCommandLists); + SafeFree(driver->GraphicsPipelinesToDispose); // Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now SafeFree(driver->AvailableFences); @@ -709,6 +710,20 @@ namespace Juliet::D3D12 } } + // Deferred dispose vectors + driver->GraphicsPipelinesToDisposeCapacity = 4; + driver->GraphicsPipelinesToDisposeCount = 0; + driver->GraphicsPipelinesToDispose = static_cast( + Calloc(driver->GraphicsPipelinesToDisposeCapacity, sizeof(D3D12GraphicsPipeline*))); + if (!driver->GraphicsPipelinesToDispose) + { + DestroyDriver_Internal(driver); + return nullptr; + } + + driver->Semantic = WrapString("TEXCOORD"); + driver->FramesInFlight = 2; + auto device = static_cast(Calloc(1, sizeof(GraphicsDevice))); if (!device) { @@ -717,36 +732,35 @@ namespace Juliet::D3D12 } // Assign Functions to the device - device->DestroyDevice = DestroyGraphicsDevice; - device->AttachToWindow = AttachToWindow; - device->DetachFromWindow = DetachFromWindow; - device->AcquireSwapChainTexture = AcquireSwapChainTexture; - device->GetSwapChainTextureFormat = GetSwapChainTextureFormat; - device->AcquireCommandList = AcquireCommandList; - device->SubmitCommandLists = SubmitCommandLists; - device->BeginRenderPass = BeginRenderPass; - device->EndRenderPass = EndRenderPass; - device->SetViewPort = SetViewPort; - device->SetScissorRect = SetScissorRect; - device->SetBlendConstants = SetBlendConstants; - device->SetStencilReference = SetStencilReference; - device->BindGraphicsPipeline = BindGraphicsPipeline; - device->DrawPrimitives = DrawPrimitives; - device->Wait = Wait; - device->QueryFence = QueryFence; - device->ReleaseFence = ReleaseFence; - device->CreateShader = CreateShader; - device->DestroyShader = DestroyShader; - device->CreateGraphicsPipeline = CreateGraphicsPipeline; - device->DestroyGraphicsPipeline = DestroyGraphicsPipeline; + device->DestroyDevice = DestroyGraphicsDevice; + device->AttachToWindow = AttachToWindow; + device->DetachFromWindow = DetachFromWindow; + device->AcquireSwapChainTexture = AcquireSwapChainTexture; + device->WaitAndAcquireSwapChainTexture = WaitAndAcquireSwapChainTexture; + device->GetSwapChainTextureFormat = GetSwapChainTextureFormat; + device->AcquireCommandList = AcquireCommandList; + device->SubmitCommandLists = SubmitCommandLists; + device->BeginRenderPass = BeginRenderPass; + device->EndRenderPass = EndRenderPass; + device->SetViewPort = SetViewPort; + device->SetScissorRect = SetScissorRect; + device->SetBlendConstants = SetBlendConstants; + device->SetStencilReference = SetStencilReference; + device->BindGraphicsPipeline = BindGraphicsPipeline; + device->DrawPrimitives = DrawPrimitives; + device->Wait = Wait; + device->QueryFence = QueryFence; + device->ReleaseFence = ReleaseFence; + device->CreateShader = CreateShader; + device->DestroyShader = DestroyShader; + device->CreateGraphicsPipeline = CreateGraphicsPipeline; + device->DestroyGraphicsPipeline = DestroyGraphicsPipeline; + + device->Driver = driver; + device->DebugEnabled = enableDebug; - device->Driver = driver; driver->GraphicsDevice = device; - driver->Semantic = WrapString("TEXCOORD"); - - driver->FramesInFlight = 2; - return device; } } // namespace @@ -758,7 +772,7 @@ namespace Juliet::D3D12 // TODO Destroy anything (buffer, texture, etc...) for (int32 idx = driver->GraphicsPipelinesToDisposeCount - 1; idx >= 0; --idx) { - if ((--(driver->GraphicsPipelinesToDispose[idx]->ReferenceCount)) == 0) + if (driver->GraphicsPipelinesToDispose[idx]->ReferenceCount == 0) { ReleaseGraphicsPipeline(driver->GraphicsPipelinesToDispose[idx]); diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp index 450b726..26bc840 100644 --- a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp @@ -70,7 +70,7 @@ namespace Juliet::D3D12 RTVs[idx] = rtv; d3d12CommandList->ColorTargetSubresources[idx] = subresource; - // TODO: TrackTexture + Internal::TrackTexture(d3d12CommandList, subresource->Parent); if (colorTargetInfos[idx].StoreOperation == StoreOperation::Resolve || colorTargetInfos[idx].StoreOperation == StoreOperation::ResolveAndStore) @@ -85,7 +85,7 @@ namespace Juliet::D3D12 d3d12CommandList->ColorResolveSubresources[idx] = resolveSubresource; - // TODO: TrackTexture Resolve + Internal::TrackTexture(d3d12CommandList, resolveSubresource->Parent); } } @@ -233,6 +233,8 @@ namespace Juliet::D3D12 // d3d12CommandList->FragmentUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(d3d12CommandBuffer); // } } + + Internal::TrackGraphicsPipeline(d3d12CommandList, pipeline); } void DrawPrimitives(NonNullPtr commandList, uint32 numVertices, uint32 numInstances, uint32 firstVertex, uint32 firstInstance) diff --git a/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp b/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp index 84a168a..2c9a1af 100644 --- a/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12SwapChain.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace Juliet::D3D12 { @@ -56,7 +57,7 @@ namespace Juliet::D3D12 return false; } - texture->RefCount += 1; + texture->ReferenceCount += 1; texture->SubresourceCount = 1; texture->Subresources = static_cast(Calloc(1, sizeof(D3D12TextureSubresource))); if (!texture->Subresources) @@ -145,8 +146,30 @@ namespace Juliet::D3D12 auto* windowData = driver->WindowData; Assert(windowData->Window == window.Get()); - // TODO : FENCES - Assert(!block); + if (windowData->InFlightFences[windowData->WindowFrameCounter] != nullptr) + { + if (block) + { + // Wait until the fence for the frame is signaled. + // In VSYNC this means waiting that the least recent presented frame is done + if (!Wait(driver, true, &windowData->InFlightFences[windowData->WindowFrameCounter], 1)) + { + return false; + } + } + else + { + // If work is not done, the least recent fence wont be signaled. + // In that case we return true to notify that there is no error, but rendering should be skipped as their will be no swapchainTexture + if (!QueryFence(driver, windowData->InFlightFences[windowData->WindowFrameCounter])) + { + return true; + } + } + + ReleaseFence(driver, windowData->InFlightFences[windowData->WindowFrameCounter]); + windowData->InFlightFences[windowData->WindowFrameCounter] = nullptr; + } uint32 swapchainIndex = IDXGISwapChain3_GetCurrentBackBufferIndex(windowData->SwapChain); HRESULT result = @@ -192,6 +215,32 @@ namespace Juliet::D3D12 return AcquireSwapChainTexture(false, commandList, window, swapChainTexture); } + bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture) + { + return AcquireSwapChainTexture(true, commandList, window, swapChainTexture); + } + + bool WaitForSwapchain(NonNullPtr driver, NonNullPtr window) + { + auto* d3d12Driver = static_cast(driver.Get()); + auto* windowData = d3d12Driver->WindowData; + if (!windowData) + { + LogError(LogCategory::Graphics, "Cannot wait for swapchain. Window has no Swapchain"); + return false; + } + + if (windowData->InFlightFences[windowData->WindowFrameCounter] != nullptr) + { + if (!Wait(d3d12Driver, true, &windowData->InFlightFences[windowData->WindowFrameCounter], 1)) + { + return false; + } + } + + return true; + } + TextureFormat GetSwapChainTextureFormat(NonNullPtr driver, NonNullPtr window) { auto* d3d12Driver = static_cast(driver.Get()); diff --git a/Juliet/src/Graphics/D3D12/D3D12SwapChain.h b/Juliet/src/Graphics/D3D12/D3D12SwapChain.h index c9f95e4..5c5da43 100644 --- a/Juliet/src/Graphics/D3D12/D3D12SwapChain.h +++ b/Juliet/src/Graphics/D3D12/D3D12SwapChain.h @@ -8,7 +8,9 @@ namespace Juliet::D3D12 struct D3D12WindowData; extern bool AcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture); -extern TextureFormat GetSwapChainTextureFormat(NonNullPtr driver, NonNullPtr window); + extern bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture); + extern bool WaitForSwapchain(NonNullPtr driver, NonNullPtr window); + extern TextureFormat GetSwapChainTextureFormat(NonNullPtr driver, NonNullPtr window); namespace Internal { diff --git a/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp b/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp index e10b389..345e8cf 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp @@ -61,7 +61,7 @@ namespace Juliet::D3D12 bool result = true; // Clean up - for (int32 idx = d3d12driver->SubmittedCommandListCount - 1; idx >= 0; idx -= 1) + for (int32 idx = d3d12driver->SubmittedCommandListCount - 1; idx >= 0; --idx) { result &= Internal::CleanCommandList(d3d12driver, d3d12driver->SubmittedCommandLists[idx], false); } @@ -71,8 +71,54 @@ namespace Juliet::D3D12 return result; } + bool Wait(NonNullPtr driver, bool waitForAll, Fence* const* fences, uint32 numFences) + { + auto d3d12driver = static_cast(driver.Get()); + + // TODO: use scratch allocator for alloca (stack alloc) + auto events = static_cast(alloca(sizeof(HANDLE) * numFences)); + + for (uint32 i = 0; i < numFences; ++i) + { + auto fence = reinterpret_cast(fences[i]); + + HRESULT res = ID3D12Fence_SetEventOnCompletion(fence->Handle, D3D12_FENCE_SIGNAL_VALUE, fence->Event); + if (FAILED(res)) + { + LogError(d3d12driver, "Setting fence event failed!", res); + return false; + } + + events[i] = fence->Event; + } + + DWORD waitResult = WaitForMultipleObjects(numFences, events, waitForAll, INFINITE); + if (waitResult == WAIT_FAILED) + { + LogError(LogCategory::Graphics, "Wait failed"); + return false; + } + + bool result = true; + + // Clean up + for (int32 idx = d3d12driver->SubmittedCommandListCount - 1; idx >= 0; --idx -= 1) + { + uint64 fenceValue = ID3D12Fence_GetCompletedValue(d3d12driver->SubmittedCommandLists[idx]->InFlightFence->Handle); + if (fenceValue == D3D12_FENCE_SIGNAL_VALUE) + { + result &= Internal::CleanCommandList(d3d12driver, d3d12driver->SubmittedCommandLists[idx], false); + } + } + + Internal::DisposePendingResourcces(d3d12driver); + + return result; + } + bool QueryFence(NonNullPtr driver, NonNullPtr fence) { + Unimplemented(); return true; } diff --git a/Juliet/src/Graphics/D3D12/D3D12Synchronization.h b/Juliet/src/Graphics/D3D12/D3D12Synchronization.h index 443734b..ab23f61 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Synchronization.h +++ b/Juliet/src/Graphics/D3D12/D3D12Synchronization.h @@ -26,6 +26,7 @@ namespace Juliet::D3D12 }; extern bool Wait(NonNullPtr driver); + extern bool Wait(NonNullPtr driver, bool waitForAll, Fence* const* fences, uint32 numFences); extern bool QueryFence(NonNullPtr driver, NonNullPtr fence); extern void ReleaseFence(NonNullPtr driver, NonNullPtr fence); diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp index 1fcc016..1f228ff 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp @@ -276,7 +276,7 @@ namespace Juliet::D3D12 D3D12_RESOURCE_STATES newTextureUsage) { D3D12TextureSubresource* subresource = Internal::FetchTextureSubresource(container, layer, level); - if (shouldCycle and container->CanBeCycled and subresource->Parent->RefCount > 0) + if (shouldCycle and container->CanBeCycled and subresource->Parent->ReferenceCount > 0) { // TODO: Cycle the active texture to an available one. Not needed for swap chain (current objective) // CycleActiveTexture(commandList->Driver, container); diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.h b/Juliet/src/Graphics/D3D12/D3D12Texture.h index f936163..2da1673 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.h +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.h @@ -59,7 +59,7 @@ namespace Juliet::D3D12 D3D12StagingDescriptor SRVHandle; // TODO: Should be atomic to support multithreading - int32 RefCount; + int32 ReferenceCount; }; namespace Internal diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index 2ef00b4..8d439e2 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -79,6 +79,27 @@ namespace Juliet { auto header = reinterpret_cast(commandList.Get()); + if (header->Device->DebugEnabled) + { + bool error = false; + if (header->Submitted) + { + error = true; + Assert(false && "Cannot submit command list twice"); + } + + if (header->RenderPass.IsInProgress) + { + error = true; + Assert(false && "Cannot submit command list twice"); + } + + if (error) + { + return false; + } + } + header->Device->AcquireSwapChainTexture(commandList, window, swapChainTexture); if (swapChainTexture) { @@ -88,6 +109,45 @@ namespace Juliet return true; } + bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture) + { + auto header = reinterpret_cast(commandList.Get()); + + if (header->Device->DebugEnabled) + { + bool error = false; + if (header->Submitted) + { + error = true; + Assert(false && "Cannot submit command list twice"); + } + + if (header->RenderPass.IsInProgress) + { + error = true; + Assert(false && "Cannot submit command list twice"); + } + + if (error) + { + return false; + } + } + + header->Device->WaitAndAcquireSwapChainTexture(commandList, window, swapChainTexture); + if (swapChainTexture) + { + header->AcquiredSwapChain = true; + } + + return true; + } + + bool WaitForSwapchain(NonNullPtr device, NonNullPtr window) + { + return device->WaitForSwapchain(device->Driver, window); + } + TextureFormat GetSwapChainTextureFormat(NonNullPtr device, NonNullPtr window) { return device->GetSwapChainTextureFormat(device->Driver, window); @@ -105,6 +165,7 @@ namespace Juliet auto header = reinterpret_cast(cmdList); header->Device = device.Get(); header->RenderPass.CommandList = cmdList; + header->Submitted = false; return cmdList; } @@ -181,7 +242,7 @@ namespace Juliet commandListHeader->Device->SetStencilReference(commandList, reference); } - void BindGraphicsPipeline(NonNullPtr renderPass, NonNullPtr graphicsPipeline) + void BindGraphicsPipeline(NonNullPtr renderPass, NonNullPtr graphicsPipeline) { auto* commandList = reinterpret_cast(renderPass.Get())->CommandList; auto* commandListHeader = reinterpret_cast(commandList); @@ -189,8 +250,7 @@ namespace Juliet commandListHeader->Device->BindGraphicsPipeline(commandList, graphicsPipeline); } - void DrawPrimitives(NonNullPtr renderPass, uint32 numVertices, uint32 numInstances, - uint32 firstVertex, uint32 firstInstance) + void DrawPrimitives(NonNullPtr renderPass, uint32 numVertices, uint32 numInstances, uint32 firstVertex, uint32 firstInstance) { auto* commandList = reinterpret_cast(renderPass.Get())->CommandList; auto* commandListHeader = reinterpret_cast(commandList); diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index e1b08fe..abed932 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -30,7 +30,6 @@ namespace Juliet struct GPUDriver { - static constexpr uint8 kResourceBufferCount = 2; static constexpr uint8 kMaxFramesInFlight = 3; static constexpr uint8 kMaxColorTargetInfo = 4; static constexpr uint8 kMaxUniformBuffersPerStage = 4; @@ -47,6 +46,8 @@ namespace Juliet // SwapChain bool (*AcquireSwapChainTexture)(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture); + bool (*WaitAndAcquireSwapChainTexture)(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture); + bool (*WaitForSwapchain)(NonNullPtr driver, NonNullPtr window); TextureFormat (*GetSwapChainTextureFormat)(NonNullPtr driver, NonNullPtr window); // CommandLists @@ -82,6 +83,7 @@ namespace Juliet const char* Name = "Unknown"; GPUDriver* Driver = nullptr; + bool DebugEnabled : 1; }; struct GraphicsDeviceFactory diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 0b75973..0086a4c 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -188,7 +188,7 @@ void JulietApplication::Update() } Texture* swapChainTexture = nullptr; - if (!AcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture)) + if (!WaitAndAcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture)) { Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire swapchain texture."); Running = false;