From 434f15a9d470b47811864f7cb31e93383a4f566b Mon Sep 17 00:00:00 2001 From: Patedam Date: Sat, 11 Jan 2025 23:33:09 -0500 Subject: [PATCH] Progress on creating the d3d12 renderer --- Juliet/Juliet.vcxproj | 4 +- Juliet/src/Core/HAL/Win32.h | 6 + .../src/Graphics/D3D12/DX12GraphicsDevice.cpp | 221 ++++++++++++++++-- Juliet/src/Graphics/D3D12/DX12Includes.h | 5 + Juliet/src/Graphics/D3D12/DX12Utils.h | 8 + Juliet/src/Graphics/GraphicsDevice.h | 15 +- 6 files changed, 241 insertions(+), 18 deletions(-) diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj index b977a4e..2cc8c2e 100644 --- a/Juliet/Juliet.vcxproj +++ b/Juliet/Juliet.vcxproj @@ -70,7 +70,7 @@ true - ws2_32.lib;dxguid.lib;d3d12.lib;dxgi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + ws2_32.lib;d3d12.lib;dxgi.lib;dxguid.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) @@ -98,7 +98,7 @@ true true true - ws2_32.lib;dxguid.lib;d3d12.lib;dxgi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + ws2_32.lib;d3d12.lib;dxgi.lib;dxguid.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) diff --git a/Juliet/src/Core/HAL/Win32.h b/Juliet/src/Core/HAL/Win32.h index 6b417cc..28b3cc4 100644 --- a/Juliet/src/Core/HAL/Win32.h +++ b/Juliet/src/Core/HAL/Win32.h @@ -14,6 +14,12 @@ #define UNICODE #endif +// Only Supports Win10 and greater +#undef WINVER +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#define WINVER _WIN32_WINNT + #define NOIME #define NOWINRES #define NOGDICAPMASKS diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp index 6acaa04..b46048a 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp @@ -1,27 +1,43 @@ #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 { - // 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 + struct D3D12Renderer : GPURenderer + { + IDXGIFactory4* DXGIFactory; + IDXGIAdapter1* DXGIAdapter; + + HMODULE DXGIDebug_DLL; + IDXGIDebug* DXGIDebug; +#ifdef IDXGIINFOQUEUE_SUPPORTED + IDXGIInfoQueue* DXGIInfoQueue; +#endif + + bool IsTearingSupported : 1; + }; + namespace { + // Note: This is the highest my Gfx Card supports (5700XT) + // https://en.wikipedia.org/wiki/Feature_levels_in_Direct3D#Direct3D_12 + // 12_2 Adds RayTracing and others feature supported by RDNA2 and greater and Gefore 20xx and greater + constexpr D3D_FEATURE_LEVEL kD3DFeatureLevel = D3D_FEATURE_LEVEL_12_1; + constexpr auto kD3DFeatureLevelStr = "12_1"; + bool CheckDriver() { - HRESULT result; - // ID3D12Device *device; - IDXGIFactory4* factory4; - IDXGIFactory6* factory6; - IDXGIAdapter1* adapter; - - ID3D12Object* test; // Can create DXGI factory ? IDXGIFactory1* factory1 = nullptr; - result = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&factory1)); + HRESULT result = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&factory1)); if (FAILED(result)) { Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Cannot create DXGIFactory1"); @@ -29,6 +45,7 @@ namespace Juliet } // Can query the 1.4 factory ? + IDXGIFactory4* factory4 = nullptr; result = IDXGIFactory1_QueryInterface(factory1, IID_IDXGIFactory4, reinterpret_cast(&factory4)); if (FAILED(result)) { @@ -37,14 +54,192 @@ namespace Juliet return false; } IDXGIFactory4_Release(factory4); + + // Check for 1.6. (It's not mandatory). + IDXGIAdapter1* adapter = nullptr; + IDXGIFactory6* factory6 = nullptr; + result = IDXGIFactory1_QueryInterface(factory1, IID_IDXGIFactory6, reinterpret_cast(&factory6)); + if (SUCCEEDED(result)) + { + result = IDXGIFactory6_EnumAdapterByGpuPreference(factory6, 0, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + IID_IDXGIAdapter1, reinterpret_cast(&adapter)); + IDXGIFactory6_Release(factory6); + } + else + { + result = IDXGIFactory1_EnumAdapters1(factory1, 0, &adapter); + } + + if (FAILED(result)) + { + Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Failed to find an adapter for D3D12."); + + IDXGIFactory1_Release(factory1); + return false; + } + + ID3D12Device* device = nullptr; + result = D3D12CreateDevice(reinterpret_cast(adapter), kD3DFeatureLevel, IID_ID3D12Device, + reinterpret_cast(&device)); + + if (SUCCEEDED(result)) + { + ID3D12Device_Release(device); + } + IDXGIAdapter1_Release(adapter); IDXGIFactory1_Release(factory1); - return false; + if (FAILED(result)) + { + Log(LogLevel::Warning, LogCategory::Graphics, + "DX12: Failed to create a D3D12Device with feature level %s.", kD3DFeatureLevelStr); + return false; + } + + return true; + } + + + +#ifdef IDXGIINFOQUEUE_SUPPORTED + void InitializeDebugLayer(NonNullPtr renderer) + { + // 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->DXGIDebug_DLL = LoadLibraryEx(L"dxgidebug.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (renderer->DXGIDebug_DLL) + { + auto dxgiGetDebugInterface = reinterpret_cast( + reinterpret_cast(GetProcAddress(renderer->DXGIDebug_DLL, "DXGIGetDebugInterface"))); + + HRESULT result = DXGIGetDebugInterface1(0, IID_IDXGIDebug, (void**)&renderer->DXGIDebug); + if (FAILED(result)) + { + Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIDebug interface"); + } + + result = dxgiGetDebugInterface(IID_IDXGIInfoQueue, (void**)&renderer->DXGIInfoQueue); + if (FAILED(result)) + { + Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIInfoQueue interface"); + } + else + { + IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); + IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); + IDXGIInfoQueue_SetBreakOnSeverity(renderer->DXGIInfoQueue, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, TRUE); + } + } + } + + void ShutdownDebugLayer(NonNullPtr renderer) + { + if (renderer->DXGIDebug) + { + IDXGIDebug_ReportLiveObjects(renderer->DXGIDebug, DXGI_DEBUG_ALL, + static_cast(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL)); + IDXGIDebug_Release(renderer->DXGIDebug); + renderer->DXGIDebug = nullptr; + } + + if (renderer->DXGIDebug_DLL) { + FreeLibrary(renderer->DXGIDebug_DLL); + renderer->DXGIDebug_DLL = nullptr; + } + } +#endif + + void DestroyRenderer_Internal(NonNullPtr renderer) + { + ShutdownDebugLayer(renderer); + Free(renderer.Get()); + } + + void DestroyGraphicsDevice(NonNullPtr device) + { + auto* renderer = reinterpret_cast(device.Get()); + DestroyRenderer_Internal(renderer); + Free(device.Get()); } GraphicsDevice* CreateGraphicsDevice() { - return nullptr; + auto renderer = static_cast(Calloc(1, sizeof(D3D12Renderer))); + +#ifdef IDXGIINFOQUEUE_SUPPORTED + static const bool kDebug = true; + if (kDebug) + { + InitializeDebugLayer(renderer); + } +#endif + + IDXGIFactory1* factory1 = nullptr; + HRESULT result = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&factory1)); + if (FAILED(result)) + { + DestroyRenderer_Internal(renderer); + Assert(false && "DX12: Cannot create DXGIFactory1"); + return nullptr; + } + + result = IDXGIFactory1_QueryInterface(factory1, IID_IDXGIFactory4, reinterpret_cast(&renderer->DXGIFactory)); + if (FAILED(result)) + { + DestroyRenderer_Internal(renderer); + Assert(false && "DX12: Cannot create DXGIFactory4. Need DXGI1.4 support. Weird because it has been " + "checked in CheckDriver"); + return nullptr; + } + IDXGIFactory1_Release(factory1); + + // Query DXGI1.5 and check for monitor Tearing support + IDXGIFactory5* factory5 = nullptr; + result = IDXGIFactory4_QueryInterface(renderer->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; + if (FAILED(result)) + { + renderer->IsTearingSupported = false; + } + IDXGIFactory1_Release(factory5); + } + + IDXGIFactory6* factory6 = nullptr; + result = IDXGIFactory4_QueryInterface(renderer->DXGIFactory, IID_IDXGIFactory6, (void**)&factory6); + if (SUCCEEDED(result)) + { + // TODO: Put into the config + const static bool useHighPerfFirst = true; + result = IDXGIFactory6_EnumAdapterByGpuPreference(factory6, 0, + useHighPerfFirst ? DXGI_GPU_PREFERENCE_MINIMUM_POWER + : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + IID_IDXGIAdapter1, (void**)&renderer->DXGIAdapter); + IDXGIFactory6_Release(factory6); + } + else + { + result = IDXGIFactory4_EnumAdapters1(renderer->DXGIFactory, 0, &renderer->DXGIAdapter); + } + + if (FAILED(result)) + { + DestroyRenderer_Internal(renderer); + Assert(false && "Could not find adapter for D3D12Device"); + return nullptr; + } + + auto device = static_cast(Calloc(1, sizeof(GraphicsDevice))); + device->DestroyDevice = DestroyGraphicsDevice; + device->Renderer = renderer; + + return device; } } // namespace diff --git a/Juliet/src/Graphics/D3D12/DX12Includes.h b/Juliet/src/Graphics/D3D12/DX12Includes.h index f9292ef..99d2398 100644 --- a/Juliet/src/Graphics/D3D12/DX12Includes.h +++ b/Juliet/src/Graphics/D3D12/DX12Includes.h @@ -14,6 +14,7 @@ #include #include "d3d12.h" #include +#include #include #ifdef _DEBUG @@ -21,3 +22,7 @@ #endif #include + +#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__ +#define IDXGIINFOQUEUE_SUPPORTED +#endif diff --git a/Juliet/src/Graphics/D3D12/DX12Utils.h b/Juliet/src/Graphics/D3D12/DX12Utils.h index b1decea..493f748 100644 --- a/Juliet/src/Graphics/D3D12/DX12Utils.h +++ b/Juliet/src/Graphics/D3D12/DX12Utils.h @@ -8,4 +8,12 @@ namespace Juliet { Assert(!!FAILED(hr)); } + + inline void CheckFailure(HRESULT hr) + { + switch (hr) + { + case E_POINTER: Assert(false && "Invalid Pointer"); + } + } } // namespace Juliet::RHI::DX12 diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index 0302b4b..5b1c276 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -1,20 +1,29 @@ #pragma once + #include +#include namespace Juliet { + struct GPURenderer + { + }; + struct GraphicsDevice { - const char* Name = "Unknown"; + void (*DestroyDevice)(NonNullPtr self); + + const char* Name = "Unknown"; + GPURenderer* Renderer = nullptr; }; struct GraphicsDeviceFactory { - const char* Name = "Unknown"; + const char* Name = "Unknown"; RendererType Type = RendererType::Any; bool (*CheckDriver)(void); GraphicsDevice* (*CreateGraphicsDevice)(void); }; extern GraphicsDeviceFactory DX12DeviceFactory; -} \ No newline at end of file +} // namespace Juliet