From 4c43cce133413fb0b0740111ce859da203596d37 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 12 Jan 2025 22:11:47 -0500 Subject: [PATCH] dx12 dynamic lib loading + creation of device first pass. --- Juliet/include/Core/Common/CoreTypes.h | 2 + Juliet/include/Core/Common/CoreUtils.h | 19 +- Juliet/include/Graphics/Graphics.h | 29 ++ Juliet/src/Core/Common/CoreUtils.cpp | 11 +- Juliet/src/Core/HAL/Display/Display.cpp | 4 +- .../src/Graphics/D3D12/DX12GraphicsDevice.cpp | 248 ++++++++++++++++-- Juliet/src/Graphics/Graphics.cpp | 6 + JulietApp/Editor/EditorMain_win32.cpp | 94 ++++--- JulietApp/Editor/EditorMain_win32.h | 29 +- 9 files changed, 361 insertions(+), 81 deletions(-) diff --git a/Juliet/include/Core/Common/CoreTypes.h b/Juliet/include/Core/Common/CoreTypes.h index a719e94..a553826 100644 --- a/Juliet/include/Core/Common/CoreTypes.h +++ b/Juliet/include/Core/Common/CoreTypes.h @@ -24,4 +24,6 @@ namespace Juliet const byte* Data; size_t Size; }; + + using FunctionPtr = auto (*)(void) -> void; } // namespace Juliet diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h index 0626426..825d249 100644 --- a/Juliet/include/Core/Common/CoreUtils.h +++ b/Juliet/include/Core/Common/CoreUtils.h @@ -2,13 +2,16 @@ #include +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 diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index ff54ea4..0bdf79f 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -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 device); } // namespace Juliet diff --git a/Juliet/src/Core/Common/CoreUtils.cpp b/Juliet/src/Core/Common/CoreUtils.cpp index cac387a..d07a605 100644 --- a/Juliet/src/Core/Common/CoreUtils.cpp +++ b/Juliet/src/Core/Common/CoreUtils.cpp @@ -1,7 +1,10 @@ #include -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(); + } +} \ No newline at end of file diff --git a/Juliet/src/Core/HAL/Display/Display.cpp b/Juliet/src/Core/HAL/Display/Display.cpp index eb982b8..32542fb 100644 --- a/Juliet/src/Core/HAL/Display/Display.cpp +++ b/Juliet/src/Core/HAL/Display/Display.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -20,7 +21,8 @@ namespace Juliet Assert(!CurrentDisplayDevice); DisplayDevice* candidateDevice = nullptr; - DisplayDeviceFactory* candidateFactory = nullptr; + DisplayDeviceFactory* + candidateFactory = nullptr; for (DisplayDeviceFactory* factory : Factories) { if (factory) diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp index 8611a2f..14a4539 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp @@ -1,9 +1,11 @@ #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 @@ -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(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(&factory1)); @@ -79,8 +123,8 @@ namespace Juliet } ID3D12Device* device = nullptr; - result = D3D12CreateDevice(reinterpret_cast(adapter), kD3DFeatureLevel, IID_ID3D12Device, - reinterpret_cast(&device)); + result = D3D12CreateDeviceFuncPtr(reinterpret_cast(adapter), kD3DFeatureLevel, IID_ID3D12Device, + reinterpret_cast(&device)); if (SUCCEEDED(result)) { @@ -100,19 +144,18 @@ namespace Juliet } #ifdef IDXGIINFOQUEUE_SUPPORTED - void InitializeDebugLayer(NonNullPtr renderer) + void InitializeDXGIDebug(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) + renderer->DXGIDebugDLL = LoadDynamicLibrary(DXGIDEBUG_DLL); + if (renderer->DXGIDebugDLL) { auto dxgiGetDebugInterface = reinterpret_cast( - reinterpret_cast(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 renderer) + void ShutdownDXGIDebug(NonNullPtr 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 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(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(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( + 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(renderer->DXGIAdapter), kD3DFeatureLevel, + IID_ID3D12Device, reinterpret_cast(&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(&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(&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(&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(&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(Calloc(1, sizeof(GraphicsDevice))); + if (!device) + { + DestroyRenderer_Internal(renderer); + return nullptr; + } + device->DestroyDevice = DestroyGraphicsDevice; + device->Renderer = renderer; + renderer->GraphicsDevice = device; return device; } diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index c43defb..3b53913 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -57,4 +57,10 @@ namespace Juliet } return nullptr; } + + void DestroyGraphicsDevice(NonNullPtr device) + { + device->DestroyDevice(device); + } + } // namespace Juliet diff --git a/JulietApp/Editor/EditorMain_win32.cpp b/JulietApp/Editor/EditorMain_win32.cpp index 2f814ca..08c3d0f 100644 --- a/JulietApp/Editor/EditorMain_win32.cpp +++ b/JulietApp/Editor/EditorMain_win32.cpp @@ -14,62 +14,76 @@ #include #include -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"); diff --git a/JulietApp/Editor/EditorMain_win32.h b/JulietApp/Editor/EditorMain_win32.h index 0d68df5..739647e 100644 --- a/JulietApp/Editor/EditorMain_win32.h +++ b/JulietApp/Editor/EditorMain_win32.h @@ -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();