From d5e09e28bf2f272cc8462c209c80d6d312ee418c Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 9 Feb 2025 16:15:59 -0500 Subject: [PATCH] First iteration of dx12 command lists. Doing nothing for now. --- Juliet/Juliet.vcxproj | 5 +- Juliet/include/Graphics/Graphics.h | 24 +- Juliet/include/Graphics/GraphicsConfig.h | 4 +- Juliet/src/Graphics/D3D12/DX12CommandList.cpp | 129 ++++++++ Juliet/src/Graphics/D3D12/DX12CommandList.h | 25 ++ .../src/Graphics/D3D12/DX12GraphicsDevice.cpp | 276 ++++++++---------- .../src/Graphics/D3D12/DX12GraphicsDevice.h | 50 ++++ Juliet/src/Graphics/Graphics.cpp | 22 +- Juliet/src/Graphics/GraphicsDevice.h | 18 +- JulietApp/Editor/EditorMain_win32.cpp | 4 + 10 files changed, 392 insertions(+), 165 deletions(-) create mode 100644 Juliet/src/Graphics/D3D12/DX12CommandList.cpp create mode 100644 Juliet/src/Graphics/D3D12/DX12CommandList.h create mode 100644 Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj index a018e53..354d512 100644 --- a/Juliet/Juliet.vcxproj +++ b/Juliet/Juliet.vcxproj @@ -151,7 +151,8 @@ - + + @@ -231,7 +232,7 @@ - + MultiThreadedDebugDll diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index 0bdf79f..48ab593 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -21,14 +21,14 @@ namespace Juliet // Parameters of an INDEXED indirect draw command struct IndexedIndirectDrawCommand { - uint32 VertexCount; // Number of vertices to draw - uint32 InstanceCount; // Number of instanced to draw + uint32 VertexCount; // Number of vertices to draw + uint32 InstanceCount; // Number of instanced to draw uint32 FirstIndex; // Base Index within the index buffer - int32 VertexOffset; // Offset the vertex index into the buffer + int32 VertexOffset; // Offset the vertex index into the buffer uint32 FirstInstance; // ID of the first instance to draw }; -// Parameters of an INDEXED Indirect Dispatch Command + // Parameters of an INDEXED Indirect Dispatch Command struct IndirectDispatchCommand { uint32 X_WorkGroupCount; // Number of Workgroup to dispatch on dimension X @@ -36,6 +36,20 @@ namespace Juliet uint32 Z_WorkGroupCount; // Number of Workgroup to dispatch on dimension Z }; + enum class QueueType : uint8 + { + Graphics = 0, + Compute, + Copy, + Count + }; + + // Opaque types + struct CommandList {}; + extern JULIET_API GraphicsDevice* CreateGraphicsDevice(GraphicsConfig config); - extern JULIET_API void DestroyGraphicsDevice(NonNullPtr device); + extern JULIET_API void DestroyGraphicsDevice(NonNullPtr device); + + extern JULIET_API CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType = QueueType::Graphics); + extern JULIET_API void SubmitCommandLists(NonNullPtr device); } // namespace Juliet diff --git a/Juliet/include/Graphics/GraphicsConfig.h b/Juliet/include/Graphics/GraphicsConfig.h index 4838eac..d3a2311 100644 --- a/Juliet/include/Graphics/GraphicsConfig.h +++ b/Juliet/include/Graphics/GraphicsConfig.h @@ -2,7 +2,7 @@ namespace Juliet { - enum class RendererType : uint8 + enum class DriverType : uint8 { Any = 0, DX12 = 1, @@ -10,6 +10,6 @@ namespace Juliet struct GraphicsConfig { - RendererType PreferredRenderer = RendererType::DX12; + DriverType PreferredDriver = DriverType::DX12; }; } // namespace Juliet diff --git a/Juliet/src/Graphics/D3D12/DX12CommandList.cpp b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp new file mode 100644 index 0000000..4653ccd --- /dev/null +++ b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp @@ -0,0 +1,129 @@ +#include + +#include +#include +#include + +namespace Juliet::D3D12 +{ + namespace + { + bool HasD3D12CommandList(NonNullPtr commandList, QueueType queueType) + { + return commandList->CommandLists[ToUnderlying(queueType)] != nullptr; + } + + bool CreateD3D12CommandList(NonNullPtr driver, NonNullPtr commandList, QueueType queueType) + { + HRESULT res = 0; + auto& queueDesc = driver->QueueDesc[ToUnderlying(queueType)]; + for (auto& buffer : commandList->CommandAllocator) + { + res = ID3D12Device5_CreateCommandAllocator(driver->D3D12Device, queueDesc.Type, IID_ID3D12CommandAllocator, + reinterpret_cast(&buffer[ToUnderlying(queueType)])); + + if (FAILED(res)) + { + Assert(false && "Error not implemented: cannot create ID3D12CommandAllocator"); + return false; + } + } + + auto& commandListSlot = commandList->CommandLists[ToUnderlying(queueType)]; + if (queueType == QueueType::Graphics || queueType == QueueType::Compute) + { + ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; + res = ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12GraphicsCommandList)); + if (FAILED(res)) + { + Assert(false && "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " + "compute command list"); + return false; + } + commandListSlot = reinterpret_cast(d3d12GraphicsCommandList); + } + else if (queueType == QueueType::Copy) + { + ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr; + res = ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12CopyCommandList)); + if (FAILED(res)) + { + Assert(false && + "Error not implemented: cannot create ID3D12GraphicsCommandList (copy command list)"); + return false; + } + commandListSlot = reinterpret_cast(d3d12CopyCommandList); + } + + // TODO: String library + std::wstring wide_str = L"CommandList ID:" + std::to_wstring(commandList->ID); + ID3D12GraphicsCommandList_SetName(commandListSlot, wide_str.c_str()); + + return true; + } + + bool AllocateCommandList(NonNullPtr driver, QueueType queueType) + { + auto* commandList = static_cast(Calloc(1, sizeof(D3D12CommandList))); + if (!commandList) + { + Log(LogLevel::Error, LogCategory::Graphics, "Cannot allocate D3D12CommandList: Out of memory"); + return false; + } + + auto resizedArray = static_cast( + Realloc(driver->AvailableCommandLists, sizeof(D3D12CommandList*) * (driver->AvailableCommandListCapacity + 1))); + + if (!resizedArray) + { + Assert(false && + "Error not implemented, out of memory, handle that by deallocating stuff and returning false"); + return false; + } + + driver->AvailableCommandLists = resizedArray; + driver->AvailableCommandLists[driver->AvailableCommandListCapacity] = commandList; + commandList->ID = driver->AvailableCommandListCapacity; + driver->AvailableCommandListCapacity += 1; + + return true; + } + } // namespace + + CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType) + { + auto* d3d12Driver = static_cast(driver.Get()); + + uint8 currentCommandListIndex = d3d12Driver->CommandListCount; + if (currentCommandListIndex >= d3d12Driver->AvailableCommandListCapacity) + { + if (!AllocateCommandList(d3d12Driver, queueType)) + { + return nullptr; + } + } + + D3D12CommandList* commandList = d3d12Driver->AvailableCommandLists[currentCommandListIndex]; + if (!HasD3D12CommandList(commandList, queueType)) + { + if (!CreateD3D12CommandList(d3d12Driver, commandList, queueType)) + { + // TODO Shoud destroy the command list here + return nullptr; + } + } + + return reinterpret_cast(commandList); + } + + void SubmitCommandLists(NonNullPtr driver) + { + auto* d3d12Driver = static_cast(driver.Get()); + uint8 commandLastIndex = d3d12Driver->CommandListCount; + } + +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/DX12CommandList.h b/Juliet/src/Graphics/D3D12/DX12CommandList.h new file mode 100644 index 0000000..7d227c4 --- /dev/null +++ b/Juliet/src/Graphics/D3D12/DX12CommandList.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace Juliet::D3D12 +{ + struct D3D12Driver; + + struct D3D12CommandList + { + D3D12Driver* Driver; + + uint64 ID; + + // We create kResourceBufferCount allocator per queue to allow reusing the command list every N frames + ID3D12CommandAllocator* CommandAllocator[GPUDriver::kResourceBufferCount][ToUnderlying(QueueType::Count)]; + ID3D12CommandList* CommandLists[ToUnderlying(QueueType::Count)]; + }; + + extern CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType); + extern void SubmitCommandLists(NonNullPtr driver); +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp index 0e17e5f..887e911 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp @@ -3,55 +3,23 @@ #include #include #include -#include +#include +#include #include #include #include -// TODO : Use LoadLibrary and not link to the lib. Allows failing earlier if Dx12 is not installed for some reason -// + Will load the dll when needed -// This will prevent us from using IID_ variables as they are defined in dxguid.lib -namespace Juliet -{ #define D3D12_DLL "d3d12.dll" #define D3D12_CREATEDEVICE_FUNC "D3D12CreateDevice" #define D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC "D3D12SerializeRootSignature" #define DXGIDEBUG_DLL "dxgidebug.dll" #define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" - struct D3D12Renderer : GPURenderer - { - GraphicsDevice* GraphicsDevice; - - // D3D12 - DynamicLibrary* D3D12DLL; - ID3D12Device* D3D12Device; - PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFct; - ID3D12CommandQueue* CommandQueue; - - // Indirect commands signature - ID3D12CommandSignature* IndirectDrawCommandSignature; - ID3D12CommandSignature* IndirectIndexedDrawCommandSignature; - ID3D12CommandSignature* IndirectDispatchCommandSignature; - - // DXGI - IDXGIFactory4* DXGIFactory; - IDXGIAdapter1* DXGIAdapter; - DynamicLibrary* DXGIDebugDLL; - IDXGIDebug* DXGIDebug; -#ifdef IDXGIINFOQUEUE_SUPPORTED - IDXGIInfoQueue* DXGIInfoQueue; -#endif - - bool IsTearingSupported : 1; - - // UMA - bool IsUMAAvailable : 1; - bool IsUMACacheCoherent : 1; - - bool GPUUploadHeapSupported : 1; - }; - +// TODO : Use LoadLibrary and not link to the lib. Allows failing earlier if Dx12 is not installed for some reason +// + Will load the dll when needed +// This will prevent us from using IID_ variables as they are defined in dxguid.lib +namespace Juliet::D3D12 +{ namespace { // Note: This is the highest my Gfx Card supports (5700XT) @@ -128,7 +96,7 @@ namespace Juliet if (SUCCEEDED(result)) { - ID3D12Device_Release(device); + ID3D12Device5_Release(device); } IDXGIAdapter1_Release(adapter); IDXGIFactory1_Release(factory1); @@ -144,129 +112,130 @@ namespace Juliet } #ifdef IDXGIINFOQUEUE_SUPPORTED - void InitializeDXGIDebug(NonNullPtr renderer) + void InitializeDXGIDebug(NonNullPtr driver) { // See https://github.com/microsoft/DirectX-Graphics-Samples/blob/7aa24663f26e547a5bc437db028dfcfdb4b3c8f3/TechniqueDemos/D3D12MemoryManagement/src/Framework.cpp#L957 // For win10 only we can just use dxgiGetDebugInterface1 using LPDXGIGETDEBUGINTERFACE = HRESULT(WINAPI*)(REFIID, void**); - renderer->DXGIDebugDLL = LoadDynamicLibrary(DXGIDEBUG_DLL); - if (renderer->DXGIDebugDLL) + driver->DXGIDebugDLL = LoadDynamicLibrary(DXGIDEBUG_DLL); + if (driver->DXGIDebugDLL) { - auto dxgiGetDebugInterface = reinterpret_cast( - LoadFunction(renderer->DXGIDebugDLL, DXGI_GET_DEBUG_INTERFACE_FUNC)); - HRESULT result = dxgiGetDebugInterface(IID_IDXGIDebug, (void**)&renderer->DXGIDebug); + auto dxgiGetDebugInterface = + reinterpret_cast(LoadFunction(driver->DXGIDebugDLL, DXGI_GET_DEBUG_INTERFACE_FUNC)); + HRESULT result = dxgiGetDebugInterface(IID_IDXGIDebug, (void**)&driver->DXGIDebug); if (FAILED(result)) { Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIDebug interface"); } - result = dxgiGetDebugInterface(IID_IDXGIInfoQueue, (void**)&renderer->DXGIInfoQueue); + result = dxgiGetDebugInterface(IID_IDXGIInfoQueue, (void**)&driver->DXGIInfoQueue); if (FAILED(result)) { Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIInfoQueue interface"); } else { - IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, + IDXGIInfoQueue_SetBreakOnSeverity(driver->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); - IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, + IDXGIInfoQueue_SetBreakOnSeverity(driver->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); - IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, + IDXGIInfoQueue_SetBreakOnSeverity(driver->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, TRUE); } } } - void ShutdownDXGIDebug(NonNullPtr renderer) + void ShutdownDXGIDebug(NonNullPtr driver) { - if (renderer->DXGIDebug) + if (driver->DXGIDebug) { - IDXGIDebug_ReportLiveObjects(renderer->DXGIDebug, DXGI_DEBUG_ALL, + IDXGIDebug_ReportLiveObjects(driver->DXGIDebug, DXGI_DEBUG_ALL, static_cast(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL)); - IDXGIDebug_Release(renderer->DXGIDebug); - renderer->DXGIDebug = nullptr; + IDXGIDebug_Release(driver->DXGIDebug); + driver->DXGIDebug = nullptr; } - if (renderer->DXGIDebugDLL) + if (driver->DXGIDebugDLL) { - UnloadDynamicLibrary(renderer->DXGIDebugDLL); - renderer->DXGIDebugDLL = nullptr; + UnloadDynamicLibrary(driver->DXGIDebugDLL); + driver->DXGIDebugDLL = nullptr; } } #endif - void DestroyRenderer_Internal(NonNullPtr renderer) + void DestroyDriver_Internal(NonNullPtr driver) { - if (renderer->IndirectDrawCommandSignature) + if (driver->IndirectDrawCommandSignature) { - ID3D12CommandSignature_Release(renderer->IndirectDrawCommandSignature); - renderer->IndirectDrawCommandSignature = nullptr; + ID3D12CommandSignature_Release(driver->IndirectDrawCommandSignature); + driver->IndirectDrawCommandSignature = nullptr; } - if (renderer->IndirectIndexedDrawCommandSignature) + if (driver->IndirectIndexedDrawCommandSignature) { - ID3D12CommandSignature_Release(renderer->IndirectIndexedDrawCommandSignature); - renderer->IndirectIndexedDrawCommandSignature = nullptr; + ID3D12CommandSignature_Release(driver->IndirectIndexedDrawCommandSignature); + driver->IndirectIndexedDrawCommandSignature = nullptr; } - if (renderer->IndirectDispatchCommandSignature) + if (driver->IndirectDispatchCommandSignature) { - ID3D12CommandSignature_Release(renderer->IndirectDispatchCommandSignature); - renderer->IndirectDispatchCommandSignature = nullptr; + ID3D12CommandSignature_Release(driver->IndirectDispatchCommandSignature); + driver->IndirectDispatchCommandSignature = nullptr; } - if (renderer->CommandQueue) + if (driver->GraphicsQueue) { - ID3D12CommandQueue_Release(renderer->CommandQueue); - renderer->CommandQueue = nullptr; + ID3D12CommandQueue_Release(driver->GraphicsQueue); + driver->GraphicsQueue = nullptr; } - if (renderer->D3D12Device) + if (driver->D3D12Device) { - ID3D12Device_Release(renderer->D3D12Device); - renderer->D3D12Device = nullptr; + ID3D12Device5_Release(driver->D3D12Device); + driver->D3D12Device = nullptr; } - if (renderer->DXGIAdapter) + if (driver->DXGIAdapter) { - IDXGIAdapter1_Release(renderer->DXGIAdapter); - renderer->DXGIAdapter = nullptr; + IDXGIAdapter1_Release(driver->DXGIAdapter); + driver->DXGIAdapter = nullptr; } - if (renderer->DXGIFactory) + if (driver->DXGIFactory) { - IDXGIFactory4_Release(renderer->DXGIFactory); - renderer->DXGIFactory = nullptr; + IDXGIFactory4_Release(driver->DXGIFactory); + driver->DXGIFactory = nullptr; } - ShutdownDXGIDebug(renderer); + ShutdownDXGIDebug(driver); - if (renderer->D3D12DLL) + if (driver->D3D12DLL) { - UnloadDynamicLibrary(renderer->D3D12DLL); - renderer->D3D12DLL = nullptr; + UnloadDynamicLibrary(driver->D3D12DLL); + driver->D3D12DLL = nullptr; } - renderer->D3D12SerializeRootSignatureFct = nullptr; + driver->D3D12SerializeRootSignatureFct = nullptr; - Free(renderer.Get()); + Free(driver.Get()); } void DestroyGraphicsDevice(NonNullPtr device) { - auto* renderer = static_cast(device->Renderer); - DestroyRenderer_Internal(renderer); + // Note: Its a down cast so clang suggest not to do it but we are totally sure about it. + auto* driver = static_cast(device->Driver); + DestroyDriver_Internal(driver); Free(device.Get()); } GraphicsDevice* CreateGraphicsDevice() { - auto renderer = static_cast(Calloc(1, sizeof(D3D12Renderer))); + auto driver = static_cast(Calloc(1, sizeof(D3D12Driver))); #ifdef IDXGIINFOQUEUE_SUPPORTED static const bool kDebug = true; if (kDebug) { - InitializeDXGIDebug(renderer); + InitializeDXGIDebug(driver); } #endif @@ -274,15 +243,15 @@ namespace Juliet HRESULT result = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&factory1)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Assert(false && "DX12: Cannot create DXGIFactory1"); return nullptr; } - result = IDXGIFactory1_QueryInterface(factory1, IID_IDXGIFactory4, reinterpret_cast(&renderer->DXGIFactory)); + result = IDXGIFactory1_QueryInterface(factory1, IID_IDXGIFactory4, reinterpret_cast(&driver->DXGIFactory)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Assert(false && "DX12: Cannot create DXGIFactory4. Need DXGI1.4 support. Weird because it has been " "checked in CheckDriver"); return nullptr; @@ -291,16 +260,16 @@ namespace Juliet // Query DXGI1.5 and check for monitor Tearing support IDXGIFactory5* factory5 = nullptr; - result = IDXGIFactory4_QueryInterface(renderer->DXGIFactory, IID_IDXGIFactory5, reinterpret_cast(&factory5)); + result = IDXGIFactory4_QueryInterface(driver->DXGIFactory, IID_IDXGIFactory5, reinterpret_cast(&factory5)); if (SUCCEEDED(result)) { bool isTearingSupported = false; result = IDXGIFactory5_CheckFeatureSupport(factory5, DXGI_FEATURE_PRESENT_ALLOW_TEARING, &isTearingSupported, sizeof(isTearingSupported)); - renderer->IsTearingSupported = isTearingSupported; + driver->IsTearingSupported = isTearingSupported; if (FAILED(result)) { - renderer->IsTearingSupported = false; + driver->IsTearingSupported = false; } IDXGIFactory5_Release(factory5); } @@ -309,7 +278,7 @@ namespace Juliet // 1.6 should be available on most Win10 PC if they didnt their windows update. // Lets support not having it for now... IDXGIFactory6* factory6 = nullptr; - result = IDXGIFactory4_QueryInterface(renderer->DXGIFactory, IID_IDXGIFactory6, (void**)&factory6); + result = IDXGIFactory4_QueryInterface(driver->DXGIFactory, IID_IDXGIFactory6, (void**)&factory6); if (SUCCEEDED(result)) { // TODO: Put into the config @@ -318,81 +287,81 @@ namespace Juliet useLowPower ? DXGI_GPU_PREFERENCE_MINIMUM_POWER : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_IDXGIAdapter1, - reinterpret_cast(&renderer->DXGIAdapter)); + reinterpret_cast(&driver->DXGIAdapter)); IDXGIFactory6_Release(factory6); } else { - result = IDXGIFactory4_EnumAdapters1(renderer->DXGIFactory, 0, &renderer->DXGIAdapter); + result = IDXGIFactory4_EnumAdapters1(driver->DXGIFactory, 0, &driver->DXGIAdapter); } if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Assert(false && "Could not find adapter for D3D12Device"); return nullptr; } // Adapter is setup, get all the relevant info in the descriptor DXGI_ADAPTER_DESC1 adapterDesc; - result = IDXGIAdapter1_GetDesc1(renderer->DXGIAdapter, &adapterDesc); + result = IDXGIAdapter1_GetDesc1(driver->DXGIAdapter, &adapterDesc); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Assert(false && "Could not get DXGIAdapter description"); return nullptr; } // Driver version LARGE_INTEGER umdVersion; - result = IDXGIAdapter1_CheckInterfaceSupport(renderer->DXGIAdapter, IID_IDXGIDevice, &umdVersion); + result = IDXGIAdapter1_CheckInterfaceSupport(driver->DXGIAdapter, IID_IDXGIDevice, &umdVersion); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Assert(false && "Could not get DXGIAdapter driver version"); return nullptr; } - Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Renderer Infos:"); + Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Driver Infos:"); Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Adapter: %S", adapterDesc.Description); Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Driver Version: %d.%d.%d.%d", HIWORD(umdVersion.HighPart), LOWORD(umdVersion.HighPart), HIWORD(umdVersion.LowPart), LOWORD(umdVersion.LowPart)); - renderer->D3D12DLL = LoadDynamicLibrary(D3D12_DLL); - if (renderer->D3D12DLL == nullptr) + driver->D3D12DLL = LoadDynamicLibrary(D3D12_DLL); + if (driver->D3D12DLL == nullptr) { Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't find " D3D12_DLL); return nullptr; } auto* D3D12CreateDeviceFuncPtr = - reinterpret_cast(LoadFunction(renderer->D3D12DLL, D3D12_CREATEDEVICE_FUNC)); + reinterpret_cast(LoadFunction(driver->D3D12DLL, D3D12_CREATEDEVICE_FUNC)); if (D3D12CreateDeviceFuncPtr == nullptr) { Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't Load function " D3D12_CREATEDEVICE_FUNC " in " D3D12_DLL); - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); return nullptr; } - renderer->D3D12SerializeRootSignatureFct = reinterpret_cast( - LoadFunction(renderer->D3D12DLL, D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC)); - if (renderer->D3D12SerializeRootSignatureFct == nullptr) + driver->D3D12SerializeRootSignatureFct = reinterpret_cast( + LoadFunction(driver->D3D12DLL, D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC)); + if (driver->D3D12SerializeRootSignatureFct == nullptr) { Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't Load function " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC " in " D3D12_DLL); - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); return nullptr; } // TODO : D3D12 Debug Layer here // InitializeD3D12DebugLayer() - result = D3D12CreateDeviceFuncPtr(reinterpret_cast(renderer->DXGIAdapter), kD3DFeatureLevel, - IID_ID3D12Device, reinterpret_cast(&renderer->D3D12Device)); + result = D3D12CreateDeviceFuncPtr(reinterpret_cast(driver->DXGIAdapter), kD3DFeatureLevel, + IID_ID3D12Device5, reinterpret_cast(&driver->D3D12Device)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); - Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12Device"); + DestroyDriver_Internal(driver); + Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12Device5"); return nullptr; } @@ -402,16 +371,16 @@ namespace Juliet // Check if UMA (unified memory architecture) is available. Used on APU i think ?? D3D12_FEATURE_DATA_ARCHITECTURE architecture; architecture.NodeIndex = 0; - result = ID3D12Device_CheckFeatureSupport(renderer->D3D12Device, D3D12_FEATURE_ARCHITECTURE, &architecture, - sizeof(D3D12_FEATURE_DATA_ARCHITECTURE)); + result = ID3D12Device5_CheckFeatureSupport(driver->D3D12Device, D3D12_FEATURE_ARCHITECTURE, &architecture, + sizeof(D3D12_FEATURE_DATA_ARCHITECTURE)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not get the device architecture"); return nullptr; } - renderer->IsUMAAvailable = architecture.UMA; - renderer->IsUMACacheCoherent = architecture.CacheCoherentUMA; + driver->IsUMAAvailable = architecture.UMA; + driver->IsUMACacheCoherent = architecture.CacheCoherentUMA; #ifndef E_INVALIDARG #define E_INVALIDARG (HRESULT)0x80070057L @@ -419,28 +388,31 @@ namespace Juliet // Check "GPU Upload Heap" support (for fast uniform buffers. Not supported on my 5700xt D3D12_FEATURE_DATA_D3D12_OPTIONS16 options16; - renderer->GPUUploadHeapSupported = false; - result = ID3D12Device_CheckFeatureSupport(renderer->D3D12Device, D3D12_FEATURE_D3D12_OPTIONS16, &options16, - sizeof(options16)); + driver->GPUUploadHeapSupported = false; + result = ID3D12Device5_CheckFeatureSupport(driver->D3D12Device, D3D12_FEATURE_D3D12_OPTIONS16, &options16, + sizeof(options16)); if (SUCCEEDED(result)) { - renderer->GPUUploadHeapSupported = options16.GPUUploadHeapSupported; + driver->GPUUploadHeapSupported = options16.GPUUploadHeapSupported; } - // Command Queue + // Command Queues + // Graphics Queue only for now D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queueDesc.NodeMask = 0; - queueDesc.Priority = 0; - result = ID3D12Device_CreateCommandQueue(renderer->D3D12Device, &queueDesc, IID_ID3D12CommandQueue, - reinterpret_cast(&renderer->CommandQueue)); + queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + result = ID3D12Device5_CreateCommandQueue(driver->D3D12Device, &queueDesc, IID_ID3D12CommandQueue, + reinterpret_cast(&driver->GraphicsQueue)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); - Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12CommandQueue"); + DestroyDriver_Internal(driver); + Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12CommandQueue: Graphics"); return nullptr; } + ID3D12CommandQueue_SetName(driver->GraphicsQueue, L"GRAPHICS_QUEUE"); + driver->QueueDesc[ToUnderlying(QueueType::Graphics)] = queueDesc; // Indirect Commands D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc; @@ -452,11 +424,11 @@ namespace Juliet commandSignatureDesc.ByteStride = sizeof(IndirectDrawCommand); commandSignatureDesc.NumArgumentDescs = 1; commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc; - result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, - reinterpret_cast(&renderer->IndirectDrawCommandSignature)); + result = ID3D12Device5_CreateCommandSignature(driver->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, + reinterpret_cast(&driver->IndirectDrawCommandSignature)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create indirect draw command signature"); return nullptr; } @@ -465,12 +437,12 @@ namespace Juliet commandSignatureDesc.ByteStride = sizeof(IndexedIndirectDrawCommand); commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc; - result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, - reinterpret_cast(&renderer->IndirectIndexedDrawCommandSignature)); + result = ID3D12Device5_CreateCommandSignature(driver->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, + reinterpret_cast(&driver->IndirectIndexedDrawCommandSignature)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create INDEXED Indirect draw command signature"); return nullptr; } @@ -479,11 +451,11 @@ namespace Juliet commandSignatureDesc.ByteStride = sizeof(IndirectDispatchCommand); commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc; - result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, - reinterpret_cast(&renderer->IndirectDispatchCommandSignature)); + result = ID3D12Device5_CreateCommandSignature(driver->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature, + reinterpret_cast(&driver->IndirectDispatchCommandSignature)); if (FAILED(result)) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create Indirect dispatch command signature"); return nullptr; } @@ -491,22 +463,30 @@ namespace Juliet auto device = static_cast(Calloc(1, sizeof(GraphicsDevice))); if (!device) { - DestroyRenderer_Internal(renderer); + DestroyDriver_Internal(driver); return nullptr; } - device->DestroyDevice = DestroyGraphicsDevice; - device->Renderer = renderer; - renderer->GraphicsDevice = device; + + // Assign Functions to the device + device->DestroyDevice = DestroyGraphicsDevice; + device->AcquireCommandList = AcquireCommandList; + device->SubmitCommandLists = SubmitCommandLists; + + device->Driver = driver; + driver->GraphicsDevice = device; return device; } } // namespace +} // namespace Juliet::D3D12 +namespace Juliet +{ // clang-format off GraphicsDeviceFactory DX12DeviceFactory = { .Name="DirectX12", - .Type=RendererType::DX12, - .CheckDriver = CheckDriver, - .CreateGraphicsDevice = CreateGraphicsDevice }; + .Type=DriverType::DX12, + .CheckDriver = D3D12::CheckDriver, + .CreateGraphicsDevice = D3D12::CreateGraphicsDevice }; // clang-format on } // namespace Juliet diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h new file mode 100644 index 0000000..3644c65 --- /dev/null +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +namespace Juliet +{ + struct DynamicLibrary; +} + +namespace Juliet::D3D12 +{ + struct D3D12Driver : GPUDriver + { + GraphicsDevice* GraphicsDevice; + + // D3D12 + DynamicLibrary* D3D12DLL; + ID3D12Device5* D3D12Device; + PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFct; + ID3D12CommandQueue* GraphicsQueue; + D3D12_COMMAND_QUEUE_DESC QueueDesc[ToUnderlying(QueueType::Copy)]; + + // Indirect commands signature + ID3D12CommandSignature* IndirectDrawCommandSignature; + ID3D12CommandSignature* IndirectIndexedDrawCommandSignature; + ID3D12CommandSignature* IndirectDispatchCommandSignature; + + // DXGI + IDXGIFactory4* DXGIFactory; + IDXGIAdapter1* DXGIAdapter; + DynamicLibrary* DXGIDebugDLL; + IDXGIDebug* DXGIDebug; +#ifdef IDXGIINFOQUEUE_SUPPORTED + IDXGIInfoQueue* DXGIInfoQueue; +#endif + + // Resources + D3D12CommandList** AvailableCommandLists; + uint8 AvailableCommandListCapacity; + + bool IsTearingSupported : 1; + + // UMA + bool IsUMAAvailable : 1; + bool IsUMACacheCoherent : 1; + + bool GPUUploadHeapSupported : 1; + }; +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index 3b53913..c02df20 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -16,12 +16,12 @@ namespace Juliet GraphicsDeviceFactory* ChooseFactory(GraphicsConfig config) { // First try to check the preferred renderer from the config if any. - if (config.PreferredRenderer != RendererType::Any) + if (config.PreferredDriver != DriverType::Any) { for (GraphicsDeviceFactory* factory : Factories) { // If the config has a preferred renderer, immediately pick it up. - if (factory->Type == config.PreferredRenderer) + if (factory->Type == config.PreferredDriver) { if (factory->CheckDriver()) { @@ -63,4 +63,22 @@ namespace Juliet device->DestroyDevice(device); } + CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType /* = QueueType::Graphics */) + { + GPUDriver* driver = device->Driver; + CommandList* cmdList = device->AcquireCommandList(driver, queueType); + + driver->CommandListCount += 1; + + return cmdList; + } + + void SubmitCommandLists(NonNullPtr device) + { + GPUDriver* driver = device->Driver; + device->SubmitCommandLists(driver); + + driver->CommandListCount = 0; + } + } // namespace Juliet diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index 5b1c276..5d55146 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -1,26 +1,32 @@ #pragma once -#include #include +#include +#include namespace Juliet { - struct GPURenderer + struct GPUDriver { + uint8 CommandListCount; + + static constexpr uint8 kResourceBufferCount = 2; }; struct GraphicsDevice { void (*DestroyDevice)(NonNullPtr self); + CommandList* (*AcquireCommandList)(NonNullPtr driver, QueueType queueType); + void (*SubmitCommandLists)(NonNullPtr driver); - const char* Name = "Unknown"; - GPURenderer* Renderer = nullptr; + const char* Name = "Unknown"; + GPUDriver* Driver = nullptr; }; struct GraphicsDeviceFactory { - const char* Name = "Unknown"; - RendererType Type = RendererType::Any; + const char* Name = "Unknown"; + DriverType Type = DriverType::Any; bool (*CheckDriver)(void); GraphicsDevice* (*CreateGraphicsDevice)(void); }; diff --git a/JulietApp/Editor/EditorMain_win32.cpp b/JulietApp/Editor/EditorMain_win32.cpp index 98558a0..5181b25 100644 --- a/JulietApp/Editor/EditorMain_win32.cpp +++ b/JulietApp/Editor/EditorMain_win32.cpp @@ -71,6 +71,10 @@ void Win32EditorApplication::Update() // Draw here for now // 1) Acquire a Command Buffer + CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics); + + // Submit Commands + SubmitCommandLists(GraphicsDevice); } bool Win32EditorApplication::IsRunning()