dx12 dynamic lib loading + creation of device first pass.

This commit is contained in:
2025-01-12 22:11:47 -05:00
parent 6c80168e8c
commit 4c43cce133
9 changed files with 361 additions and 81 deletions

View File

@@ -24,4 +24,6 @@ namespace Juliet
const byte* Data;
size_t Size;
};
using FunctionPtr = auto (*)(void) -> void;
} // namespace Juliet

View File

@@ -2,13 +2,16 @@
#include <Juliet.h>
namespace Juliet
{
#if _DEBUG
#define Assert(expression) \
do \
{ \
if (!(expression)) \
{ \
JulietAssert("Assertion Failed: " #expression); \
Juliet::JulietAssert("Assertion Failed: " #expression); \
} \
} \
while (0)
@@ -17,4 +20,16 @@
#define Assert(Expression)
#endif
extern void JULIET_API JulietAssert(const char* expression);
extern void JULIET_API JulietAssert(const char* expression);
#define ZeroStruct(structInstance) ZeroSize(sizeof(structInstance), &(structInstance))
#define ZeroArray(Count, Pointer) ZeroSize((Count) * sizeof((Pointer)[0]), Pointer)
inline void ZeroSize(size_t size, void* ptr)
{
auto Byte = (uint8*)ptr;
while (size--)
{
*Byte++ = 0;
}
}
} // namespace Juliet

View File

@@ -1,5 +1,6 @@
#pragma once
#include <core/Common/NonNullPtr.h>
#include <Graphics/GraphicsConfig.h>
#include <Juliet.h>
@@ -8,5 +9,33 @@ namespace Juliet
{
struct GraphicsDevice;
// Parameters of an indirect draw command
struct IndirectDrawCommand
{
uint32 VertexCount; // Number of vertices to draw
uint32 InstanceCount; // Number of instanced to draw
uint32 FirstVertex; // Index of the first vertex to draw
uint32 FirstInstance; // ID of the first instance to draw
};
// Parameters of an INDEXED indirect draw command
struct IndexedIndirectDrawCommand
{
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
uint32 FirstInstance; // ID of the first instance to draw
};
// Parameters of an INDEXED Indirect Dispatch Command
struct IndirectDispatchCommand
{
uint32 X_WorkGroupCount; // Number of Workgroup to dispatch on dimension X
uint32 Y_WorkGroupCount; // Number of Workgroup to dispatch on dimension Y
uint32 Z_WorkGroupCount; // Number of Workgroup to dispatch on dimension Z
};
extern JULIET_API GraphicsDevice* CreateGraphicsDevice(GraphicsConfig config);
extern JULIET_API void DestroyGraphicsDevice(NonNullPtr<GraphicsDevice> device);
} // namespace Juliet

View File

@@ -1,7 +1,10 @@
#include <pch.h>
void JulietAssert(const char* expression)
namespace Juliet
{
Juliet::Log(Juliet::LogLevel::Error, Juliet::LogCategory::Core, expression);
__debugbreak();
}
void JulietAssert(const char* expression)
{
Juliet::Log(Juliet::LogLevel::Error, Juliet::LogCategory::Core, expression);
__debugbreak();
}
}

View File

@@ -1,5 +1,6 @@
#include <pch.h>
#include <Core/Common/CoreTypes.h>
#include <Core/HAL/Display/Display_Private.h>
#include <Core/HAL/Display/DisplayDevice.h>
#include <Core/Memory/Allocator.h>
@@ -20,7 +21,8 @@ namespace Juliet
Assert(!CurrentDisplayDevice);
DisplayDevice* candidateDevice = nullptr;
DisplayDeviceFactory* candidateFactory = nullptr;
DisplayDeviceFactory*
candidateFactory = nullptr;
for (DisplayDeviceFactory* factory : Factories)
{
if (factory)

View File

@@ -1,9 +1,11 @@
#include <pch.h>
#include <core/Common/NonNullPtr.h>
#include <Core/DynLib/DynamicLibrary.h>
#include <Core/Memory/Allocator.h>
#include <DX12Utils.h>
#include <Graphics/D3D12/DX12Includes.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsDevice.h>
// TODO : Use LoadLibrary and not link to the lib. Allows failing earlier if Dx12 is not installed for some reason
@@ -11,18 +13,43 @@
// 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
{
IDXGIFactory4* DXGIFactory;
IDXGIAdapter1* DXGIAdapter;
GraphicsDevice* GraphicsDevice;
HMODULE DXGIDebug_DLL;
IDXGIDebug* DXGIDebug;
// 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;
};
namespace
@@ -35,6 +62,23 @@ namespace Juliet
bool CheckDriver()
{
// Can we Load D3D12.dll and the create device function
DynamicLibrary* d3d12_dll = LoadDynamicLibrary(D3D12_DLL);
if (d3d12_dll == nullptr)
{
Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Couldn't find " D3D12_DLL);
return false;
}
auto* D3D12CreateDeviceFuncPtr =
reinterpret_cast<PFN_D3D12_CREATE_DEVICE>(LoadFunction(d3d12_dll, D3D12_CREATEDEVICE_FUNC));
if (D3D12CreateDeviceFuncPtr == nullptr)
{
Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Couldn't find function " D3D12_CREATEDEVICE_FUNC " in " D3D12_DLL);
UnloadDynamicLibrary(d3d12_dll);
return false;
}
// Can create DXGI factory ?
IDXGIFactory1* factory1 = nullptr;
HRESULT result = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void**>(&factory1));
@@ -79,8 +123,8 @@ namespace Juliet
}
ID3D12Device* device = nullptr;
result = D3D12CreateDevice(reinterpret_cast<IUnknown*>(adapter), kD3DFeatureLevel, IID_ID3D12Device,
reinterpret_cast<void**>(&device));
result = D3D12CreateDeviceFuncPtr(reinterpret_cast<IUnknown*>(adapter), kD3DFeatureLevel, IID_ID3D12Device,
reinterpret_cast<void**>(&device));
if (SUCCEEDED(result))
{
@@ -100,19 +144,18 @@ namespace Juliet
}
#ifdef IDXGIINFOQUEUE_SUPPORTED
void InitializeDebugLayer(NonNullPtr<D3D12Renderer> renderer)
void InitializeDXGIDebug(NonNullPtr<D3D12Renderer> 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)
renderer->DXGIDebugDLL = LoadDynamicLibrary(DXGIDEBUG_DLL);
if (renderer->DXGIDebugDLL)
{
auto dxgiGetDebugInterface = reinterpret_cast<LPDXGIGETDEBUGINTERFACE>(
reinterpret_cast<void*>(GetProcAddress(renderer->DXGIDebug_DLL, "DXGIGetDebugInterface")));
HRESULT result = DXGIGetDebugInterface1(0, IID_IDXGIDebug, (void**)&renderer->DXGIDebug);
LoadFunction(renderer->DXGIDebugDLL, DXGI_GET_DEBUG_INTERFACE_FUNC));
HRESULT result = dxgiGetDebugInterface(IID_IDXGIDebug, (void**)&renderer->DXGIDebug);
if (FAILED(result))
{
Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIDebug interface");
@@ -135,7 +178,7 @@ namespace Juliet
}
}
void ShutdownDebugLayer(NonNullPtr<D3D12Renderer> renderer)
void ShutdownDXGIDebug(NonNullPtr<D3D12Renderer> renderer)
{
if (renderer->DXGIDebug)
{
@@ -145,17 +188,44 @@ namespace Juliet
renderer->DXGIDebug = nullptr;
}
if (renderer->DXGIDebug_DLL)
if (renderer->DXGIDebugDLL)
{
FreeLibrary(renderer->DXGIDebug_DLL);
renderer->DXGIDebug_DLL = nullptr;
UnloadDynamicLibrary(renderer->DXGIDebugDLL);
renderer->DXGIDebugDLL = nullptr;
}
}
#endif
void DestroyRenderer_Internal(NonNullPtr<D3D12Renderer> renderer)
{
ShutdownDebugLayer(renderer);
if (renderer->D3D12Device)
{
ID3D12Device_Release(renderer->D3D12Device);
renderer->D3D12Device = nullptr;
}
if (renderer->DXGIAdapter)
{
IDXGIAdapter1_Release(renderer->DXGIAdapter);
renderer->DXGIAdapter = nullptr;
}
if (renderer->DXGIFactory)
{
IDXGIFactory4_Release(renderer->DXGIFactory);
renderer->DXGIFactory = nullptr;
}
ShutdownDXGIDebug(renderer);
if (renderer->D3D12DLL)
{
UnloadDynamicLibrary(renderer->D3D12DLL);
renderer->D3D12DLL = nullptr;
}
renderer->D3D12SerializeRootSignatureFct = nullptr;
Free(renderer.Get());
}
@@ -174,7 +244,7 @@ namespace Juliet
static const bool kDebug = true;
if (kDebug)
{
InitializeDebugLayer(renderer);
InitializeDXGIDebug(renderer);
}
#endif
@@ -266,9 +336,145 @@ namespace Juliet
Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Driver Version: %d.%d.%d.%d", HIWORD(umdVersion.HighPart),
LOWORD(umdVersion.HighPart), HIWORD(umdVersion.LowPart), LOWORD(umdVersion.LowPart));
auto device = static_cast<GraphicsDevice*>(Calloc(1, sizeof(GraphicsDevice)));
device->DestroyDevice = DestroyGraphicsDevice;
device->Renderer = renderer;
renderer->D3D12DLL = LoadDynamicLibrary(D3D12_DLL);
if (renderer->D3D12DLL == nullptr)
{
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't find " D3D12_DLL);
return nullptr;
}
auto* D3D12CreateDeviceFuncPtr =
reinterpret_cast<PFN_D3D12_CREATE_DEVICE>(LoadFunction(renderer->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);
return nullptr;
}
renderer->D3D12SerializeRootSignatureFct = reinterpret_cast<PFN_D3D12_SERIALIZE_ROOT_SIGNATURE>(
LoadFunction(renderer->D3D12DLL, D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC));
if (renderer->D3D12SerializeRootSignatureFct == nullptr)
{
Log(LogLevel::Error, LogCategory::Graphics,
"DX12: Couldn't Load function " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC " in " D3D12_DLL);
DestroyRenderer_Internal(renderer);
return nullptr;
}
// TODO : D3D12 Debug Layer here
// InitializeD3D12DebugLayer()
result = D3D12CreateDeviceFuncPtr(reinterpret_cast<IUnknown*>(renderer->DXGIAdapter), kD3DFeatureLevel,
IID_ID3D12Device, reinterpret_cast<void**>(&renderer->D3D12Device));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12Device");
return nullptr;
}
// TODO : D3D12 Debug Info Queue
// InitializeD3D12DebugInfoQueue
// 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));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not get the device architecture");
return nullptr;
}
renderer->IsUMAAvailable = architecture.UMA;
renderer->IsUMACacheCoherent = architecture.CacheCoherentUMA;
#ifndef E_INVALIDARG
#define E_INVALIDARG (HRESULT)0x80070057L
#endif
// 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));
if (SUCCEEDED(result))
{
renderer->GPUUploadHeapSupported = options16.GPUUploadHeapSupported;
}
// Command Queue
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<void**>(&renderer->CommandQueue));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12CommandQueue");
return nullptr;
}
// Indirect Commands
D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc;
D3D12_INDIRECT_ARGUMENT_DESC indirectArgumentDesc;
ZeroStruct(indirectArgumentDesc);
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
commandSignatureDesc.NodeMask = 0;
commandSignatureDesc.ByteStride = sizeof(IndirectDrawCommand);
commandSignatureDesc.NumArgumentDescs = 1;
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature,
reinterpret_cast<void**>(&renderer->IndirectDrawCommandSignature));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create indirect draw command signature");
return nullptr;
}
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
commandSignatureDesc.ByteStride = sizeof(IndexedIndirectDrawCommand);
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature,
reinterpret_cast<void**>(&renderer->IndirectIndexedDrawCommandSignature));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create INDEXED Indirect draw command signature");
return nullptr;
}
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
commandSignatureDesc.ByteStride = sizeof(IndirectDispatchCommand);
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
result = ID3D12Device_CreateCommandSignature(renderer->D3D12Device, &commandSignatureDesc, NULL, IID_ID3D12CommandSignature,
reinterpret_cast<void**>(&renderer->IndirectDispatchCommandSignature));
if (FAILED(result))
{
DestroyRenderer_Internal(renderer);
Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create Indirect dispatch command signature");
return nullptr;
}
auto device = static_cast<GraphicsDevice*>(Calloc(1, sizeof(GraphicsDevice)));
if (!device)
{
DestroyRenderer_Internal(renderer);
return nullptr;
}
device->DestroyDevice = DestroyGraphicsDevice;
device->Renderer = renderer;
renderer->GraphicsDevice = device;
return device;
}

View File

@@ -57,4 +57,10 @@ namespace Juliet
}
return nullptr;
}
void DestroyGraphicsDevice(NonNullPtr<GraphicsDevice> device)
{
device->DestroyDevice(device);
}
} // namespace Juliet

View File

@@ -14,62 +14,76 @@
#include <Graphics/Graphics.h>
#include <Windows.h>
namespace Juliet
// TODO : Think how to do the draw pipeline.
// Ex: Expose a Draw method ?
// Store a graphics context ?
// For now. Put everything in Update down below.
// Should split update from draw, update should have a != timestep than graphics (60fps or more)
using namespace Juliet;
void Win32EditorApplication::Init()
{
void Win32EditorApplication::Init()
Log(LogLevel::Message, LogCategory::Editor, "Initializing Editor Application...");
GraphicsConfig config;
GraphicsDevice = CreateGraphicsDevice(config);
MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720);
// TODO : Assign the graphics device to the main window (and detach)
Running = MainWindow != nullptr && GraphicsDevice != nullptr;
}
void Win32EditorApplication::Shutdown()
{
Log(LogLevel::Message, LogCategory::Editor, "Shutdown Editor Application...");
if (MainWindow)
{
Log(LogLevel::Message, LogCategory::Editor, "Initializing Editor Application...");
MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720);
GraphicsConfig config;
GraphicsDevice* device = CreateGraphicsDevice(config);
Assert(!device && "Currently not implemented so device should be null");
Running = MainWindow != nullptr;
DestroyPlatformWindow(MainWindow);
}
void Win32EditorApplication::Shutdown()
if (GraphicsDevice)
{
Log(LogLevel::Message, LogCategory::Editor, "Shutdown Editor Application...");
if (MainWindow)
{
DestroyPlatformWindow(MainWindow);
}
DestroyGraphicsDevice(GraphicsDevice);
}
}
void Win32EditorApplication::Update()
void Win32EditorApplication::Update()
{
SystemEvent evt;
while (GetEvent(evt))
{
SystemEvent evt;
while (GetEvent(evt))
if (evt.Type == EventType::Window_Close_Request)
{
if (evt.Type == EventType::Window_Close_Request)
if (evt.Data.Window.AssociatedWindowID == GetWindowID(MainWindow))
{
if (evt.Data.Window.AssociatedWindowID == GetWindowID(MainWindow))
{
Running = false;
}
Running = false;
}
}
}
bool Win32EditorApplication::IsRunning()
{
return Running;
}
// Draw here for now
// 1) Acquire a Command Buffer
namespace
{
Win32EditorApplication EditorApplication;
}
Win32EditorApplication& GetEditorApplication()
{
return EditorApplication;
}
}
} // namespace Juliet
bool Win32EditorApplication::IsRunning()
{
return Running;
}
namespace
{
Win32EditorApplication EditorApplication;
}
Win32EditorApplication& GetEditorApplication()
{
return EditorApplication;
}
int main(int argc, char** argv)
{
@@ -80,7 +94,7 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
StartApplication(Juliet::EditorApplication, Juliet::JulietInit_Flags::Display);
StartApplication(EditorApplication, JulietInit_Flags::Display);
// Pause here to not close the console window immediatly on stop
system("PAUSE");

View File

@@ -5,20 +5,23 @@
namespace Juliet
{
struct GraphicsDevice;
struct Window;
}
class Win32EditorApplication : public IApplication
{
protected:
void Init() override;
void Shutdown() override;
void Update() override;
bool IsRunning() override;
class Win32EditorApplication : public Juliet::IApplication
{
protected:
void Init() override;
void Shutdown() override;
void Update() override;
bool IsRunning() override;
private:
Window* MainWindow = {};
bool Running = false;
};
private:
Juliet::Window* MainWindow = {};
Juliet::GraphicsDevice* GraphicsDevice = {};
Win32EditorApplication& GetEditorApplication();
} // namespace Juliet
bool Running = false;
};
Win32EditorApplication& GetEditorApplication();