From 2881b2c9d58aeed736c1fb1cad079e66f7457804 Mon Sep 17 00:00:00 2001 From: Patedam Date: Mon, 26 Oct 2026 22:52:08 -0400 Subject: [PATCH] started d3d12 renderer. device + swap chain + associate to window --- Juliet/core/core.h | 2 + Juliet/core/core_threads.h | 6 + Juliet/julietapp/julietapp_core.c | 2 + Juliet/julietapp/julietapp_core.h | 1 + Juliet/julietapp/julietapp_main.c | 2 + Juliet/render/d3d12/render_d3d12.c | 177 ++++++++++++++++++++++++++++- Juliet/render/d3d12/render_d3d12.h | 40 ++++++- Juliet/shell/shell.c | 0 Juliet/shell/shell.h | 6 + Juliet/shell/shell_inc.c | 7 ++ Juliet/shell/shell_inc.h | 9 ++ Juliet/win32/shell/win32_shell.c | 11 ++ Juliet/win32/shell/win32_shell.h | 1 + 13 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 Juliet/shell/shell.c create mode 100644 Juliet/shell/shell.h create mode 100644 Juliet/shell/shell_inc.c create mode 100644 Juliet/shell/shell_inc.h create mode 100644 Juliet/win32/shell/win32_shell.c create mode 100644 Juliet/win32/shell/win32_shell.h diff --git a/Juliet/core/core.h b/Juliet/core/core.h index d2ae78e..2cc9e22 100644 --- a/Juliet/core/core.h +++ b/Juliet/core/core.h @@ -73,6 +73,8 @@ // ============================================================================= // For loop helpers +#define DeferLoop(begin, end) for (int _i_ = ((begin), 0); !_i_; ++_i_, (end)) + #define EachIndex(it, count) (uint64 it = 0; it < (count); ++it) #define EachNode(it, Type, first) (Type* it = first; it != 0; it = it->next) diff --git a/Juliet/core/core_threads.h b/Juliet/core/core_threads.h index 5ed7aea..30e666c 100644 --- a/Juliet/core/core_threads.h +++ b/Juliet/core/core_threads.h @@ -21,6 +21,8 @@ internal uint32 tid(); internal void set_platform_thread_name(String8 name); // ============================================================================= +// Synchronization primitive functions + // Read Write Mutexes functions internal RWMutex rw_mutex_alloc(); @@ -31,3 +33,7 @@ internal void rw_mutex_drop(RWMutex mutex, bool32 write_mode); #define rw_mutex_take_w(m) rw_mutex_take((m), (1)) #define rw_mutex_drop_r(m) rw_mutex_drop((m), (0)) #define rw_mutex_drop_w(m) rw_mutex_drop((m), (1)) + +// Scope macros +#define MutexScopeR(mutex) DeferLoop(rw_mutex_take_r(mutex), rw_mutex_drop_r(mutex)) +#define MutexScopeW(mutex) DeferLoop(rw_mutex_take_w(mutex), rw_mutex_drop_w(mutex)) diff --git a/Juliet/julietapp/julietapp_core.c b/Juliet/julietapp/julietapp_core.c index 207bfa2..f2ab15d 100644 --- a/Juliet/julietapp/julietapp_core.c +++ b/Juliet/julietapp/julietapp_core.c @@ -91,6 +91,7 @@ internal void ja_frame() JA_WindowRequest* request = ja_window_request_from_id(ws->id); if (request == 0 || ja_state->quit) { + r_window_unequip(ws->plat_handle, ws->render_handle); wm_window_close(ws->plat_handle); arena_release(ws->arena); DLL_remove_next_prev_zero(&ja_nil_window_state, ja_state->first_window_state, @@ -220,6 +221,7 @@ internal JA_WindowState* ja_window_state_from_id(JA_ID id) // TODO : put those in config, hardcoded for now String8 title = str8_literal("Juliet"); ws->plat_handle = wm_window_open(rect2fp(50.f, 240.f, 1280.f, 720.f), WM_WindowFlag_UseDefaultPosition, title); + ws->render_handle = r_window_equip(ws->plat_handle); WM_Monitor zero_monitor = { 0 }; if (!wm_monitor_match(zero_monitor, fav_monitor)) { diff --git a/Juliet/julietapp/julietapp_core.h b/Juliet/julietapp/julietapp_core.h index b7f4499..0b0f83e 100644 --- a/Juliet/julietapp/julietapp_core.h +++ b/Juliet/julietapp/julietapp_core.h @@ -44,6 +44,7 @@ struct JA_WindowState // Info and handle Arena* arena; WM_Window plat_handle; + R_Handle render_handle; }; typedef struct JA_WindowStateSlot JA_WindowStateSlot; diff --git a/Juliet/julietapp/julietapp_main.c b/Juliet/julietapp/julietapp_main.c index 2c6dc44..d5054e7 100644 --- a/Juliet/julietapp/julietapp_main.c +++ b/Juliet/julietapp/julietapp_main.c @@ -4,12 +4,14 @@ // Headers #include "core/core_inc.h" +#include "shell/shell_inc.h" #include "window_manager/window_manager_inc.h" #include "render/render_inc.h" #include "julietapp/julietapp_inc.h" // C #include "core/core_inc.c" +#include "shell/shell_inc.c" #include "window_manager/window_manager_inc.c" #include "render/render_inc.c" #include "julietapp/julietapp_inc.c" diff --git a/Juliet/render/d3d12/render_d3d12.c b/Juliet/render/d3d12/render_d3d12.c index 3b948ce..471cad5 100644 --- a/Juliet/render/d3d12/render_d3d12.c +++ b/Juliet/render/d3d12/render_d3d12.c @@ -1,6 +1,29 @@ +// TODO : Adapt stuff from D3D12GraphicsDevice.c specially all the shenanigans with dxgi factory and stuff +// TODO : Support more kind of format (HDR) + +// ============================================================================= +// Helpers + +internal R_D3D12_Window* r_d3d12_window_from_handle(R_Handle handle) +{ + R_D3D12_Window* window = (R_D3D12_Window*)handle.u64[0]; + if (window == 0) + { + window = &r_d3d12_nil_window; + } + return window; +} + +internal R_Handle r_d3d12_handle_from_window(R_D3D12_Window* window) +{ + R_Handle handle = { 0 }; + handle.u64[0] = (uint64)window; + return handle; +} // ============================================================================= // top level layer initialization + void r_init() { HRESULT error = 0; @@ -8,6 +31,85 @@ void r_init() r_d3d12_state = push_array(arena, R_D3D12_State, 1); r_d3d12_state->arena = arena; r_d3d12_state->device_rw_mutex = rw_mutex_alloc(); + + // Create base device + D3D_FEATURE_LEVEL feature_level = D3D_FEATURE_LEVEL_12_1; + + UINT dxgi_flags = 0; +#if BUILD_DEBUG + dxgi_flags |= DXGI_CREATE_FACTORY_DEBUG; +#endif + + error = CreateDXGIFactory2(dxgi_flags, &IID_IDXGIFactory4, (void**)&r_d3d12_state->dxgi_factory); + if (FAILED(error)) + { + char buffer[256] = { 0 }; + juliet_snprintf(buffer, sizeof(buffer), "D3D12 DXGI factory creation failure (%lx). Process is terminating", error); + sh_message(1, str8_literal("Fatal Error"), str8_cstring(buffer)); + abort_self(1); + } + + error = D3D12CreateDevice(0, feature_level, &IID_ID3D12Device4, (void**)&r_d3d12_state->device); + if (FAILED(error)) + { + IDXGIAdapter1* warp_adapter = 0; + error = IDXGIFactory4_EnumWarpAdapter(r_d3d12_state->dxgi_factory, &IID_IDXGIFactory1, (void**)&warp_adapter); + if (SUCCEEDED(error)) + { + error = D3D12CreateDevice((IUnknown*)warp_adapter, feature_level, &IID_ID3D12Device4, (void**)&r_d3d12_state->device); + } + IDXGIAdapter1_Release(warp_adapter); + } + + if (FAILED(error)) + { + char buffer[256] = { 0 }; + juliet_snprintf(buffer, sizeof(buffer), "D3D12 device factory creation failure (%lx). Process is terminating", error); + sh_message(1, str8_literal("Fatal Error"), str8_cstring(buffer)); + abort_self(1); + } + + // break on error +#if BUILD_DEBUG + ID3D12InfoQueue* info = 0; + error = ID3D12Device4_QueryInterface(r_d3d12_state->device, &IID_ID3D12InfoQueue, (void**)info); + if (SUCCEEDED(error)) + { + error = ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); + error = ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_ERROR, TRUE); + ID3D12InfoQueue_Release(info); + } +#endif + + // dxgi setup (device and adapter) + IDXGIFactory6* factory6 = 0; + error = IDXGIFactory4_QueryInterface(r_d3d12_state->dxgi_factory, &IID_IDXGIFactory6, (void**)&factory6); + if (SUCCEEDED(error)) + { + // Prioritize discrete GPU + error = IDXGIFactory6_EnumAdapterByGpuPreference(factory6, 0, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + &IID_IDXGIAdapter1, (void**)&r_d3d12_state->dxgi_adapter); + IDXGIFactory6_Release(factory6); + } + else + { + // Pray device 0 is the discrete gpu + error = IDXGIFactory4_EnumAdapters1(r_d3d12_state->dxgi_factory, 0, &r_d3d12_state->dxgi_adapter); + } + + // Create basic graphics queue + D3D12_COMMAND_QUEUE_DESC queue_desc = { 0 }; + queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + error = ID3D12Device4_CreateCommandQueue(r_d3d12_state->device, &queue_desc, &IID_ID3D12CommandQueue, + (void**)&r_d3d12_state->direct_command_queue); + if (FAILED(error)) + { + char buffer[256] = { 0 }; + juliet_snprintf(buffer, sizeof(buffer), "D3D12 direct command queue creation failure (%lx). Process is terminating", error); + sh_message(1, str8_literal("Fatal Error"), str8_cstring(buffer)); + abort_self(1); + } } void r_shutdown() @@ -19,11 +121,82 @@ void r_shutdown() // ============================================================================= // window setup/teardown -R_Handle r_window_equip(WM_Window window) +R_Handle r_window_equip(WM_Window handle) { R_Handle result = { 0 }; + MutexScopeW(r_d3d12_state->device_rw_mutex) + { + R_D3D12_Window* window = r_d3d12_state->first_free_window; + if (window != 0) + { + SLL_stack_pop(r_d3d12_state->first_free_window); + MemoryZeroStruct(window); + } + else + { + window = push_array(r_d3d12_state->arena, R_D3D12_Window, 1); + } + + HWND hwnd = { 0 }; + { + W32_WM_Window* w32_layer_window = w32_wm_window_from_handle(handle); + hwnd = w32_wm_hwnd_from_window(w32_layer_window); + } + + // Create swapchain + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { 0 }; + swap_chain_desc.Width = 0; // Use all window width + swap_chain_desc.Height = 0; // Use all window height + swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swap_chain_desc.Stereo = 0; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = 2; // Double buffering + swap_chain_desc.Scaling = DXGI_SCALING_NONE; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swap_chain_desc.Flags = 0; + + DXGI_SWAP_CHAIN_FULLSCREEN_DESC swap_chain_fullscreen_desc = { 0 }; + swap_chain_fullscreen_desc.RefreshRate.Numerator = 0; + swap_chain_fullscreen_desc.RefreshRate.Denominator = 0; + swap_chain_fullscreen_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swap_chain_fullscreen_desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swap_chain_fullscreen_desc.Windowed = TRUE; + + IDXGISwapChain1* swap_chain1 = 0; + HRESULT error = + IDXGIFactory4_CreateSwapChainForHwnd(r_d3d12_state->dxgi_factory, (IUnknown*)r_d3d12_state->direct_command_queue, + hwnd, &swap_chain_desc, &swap_chain_fullscreen_desc, 0, &swap_chain1); + if (FAILED(error)) + { + char buffer[256]; + juliet_snprintf(buffer, sizeof(buffer), "DXGI swap chain creation failure (%lx). Process is terminating.", error); + sh_message(1, str8_literal("Fatal Error"), str8_cstring(buffer)); + abort_self(1); + } + + IDXGISwapChain1_QueryInterface(swap_chain1, &IID_IDXGISwapChain3, (void**)&window->swap_chain); + IDXGISwapChain1_Release(swap_chain1); + + IDXGIFactory4_MakeWindowAssociation(r_d3d12_state->dxgi_factory, hwnd, DXGI_MWA_NO_ALT_ENTER); + + result = r_d3d12_handle_from_window(window); + } return result; } -void r_window_unequip(WM_Window window, R_Handle window_equip) {} +void r_window_unequip(WM_Window handle, R_Handle handle_equip) +{ + MutexScopeW(r_d3d12_state->device_rw_mutex) + { + R_D3D12_Window* window = r_d3d12_window_from_handle(handle_equip); + if (window != 0) + { + IDXGISwapChain1_Release(window->swap_chain); + SLL_stack_push(r_d3d12_state->first_free_window, window); + } + } +} diff --git a/Juliet/render/d3d12/render_d3d12.h b/Juliet/render/d3d12/render_d3d12.h index a793450..48c0fc8 100644 --- a/Juliet/render/d3d12/render_d3d12.h +++ b/Juliet/render/d3d12/render_d3d12.h @@ -1,17 +1,51 @@ #pragma once +#define COBJMACROS +#define CINTERFACE +#include +#include +#undef COBJMACROS +#undef CINTERFACE + +#pragma comment(lib, "d3d12") +#pragma comment(lib, "dxgi") +#pragma comment(lib, "dxguid") + // ============================================================================= // D3D12 States Types +typedef struct R_D3D12_Window R_D3D12_Window; +struct R_D3D12_Window +{ + R_D3D12_Window* next; + IDXGISwapChain3* swap_chain; +}; + typedef struct R_D3D12_State R_D3D12_State; struct R_D3D12_State { // states - Arena* arena; - RWMutex device_rw_mutex; + Arena* arena; + R_D3D12_Window* first_free_window; + RWMutex device_rw_mutex; + + // base d3d12 object + ID3D12Device4* device; + IDXGIFactory4* dxgi_factory; + IDXGIAdapter1* dxgi_adapter; + + // Commands + ID3D12CommandQueue* direct_command_queue; }; // ============================================================================= // Globals -global R_D3D12_State* r_d3d12_state = 0; +global R_D3D12_State* r_d3d12_state = 0; +global read_only R_D3D12_Window r_d3d12_nil_window = { &r_d3d12_nil_window }; + +// ============================================================================= +// Helpers + +internal R_D3D12_Window* r_d3d12_window_from_handle(R_Handle handle); +internal R_Handle r_d3d12_handle_from_window(R_D3D12_Window* window); diff --git a/Juliet/shell/shell.c b/Juliet/shell/shell.c new file mode 100644 index 0000000..e69de29 diff --git a/Juliet/shell/shell.h b/Juliet/shell/shell.h new file mode 100644 index 0000000..80c8292 --- /dev/null +++ b/Juliet/shell/shell.h @@ -0,0 +1,6 @@ +#pragma once + +// ============================================================================= +// Shell operations + +internal void sh_message(bool32 error, String8 title, String8 message); diff --git a/Juliet/shell/shell_inc.c b/Juliet/shell/shell_inc.c new file mode 100644 index 0000000..4802b6b --- /dev/null +++ b/Juliet/shell/shell_inc.c @@ -0,0 +1,7 @@ +#include "shell.c" + +#if OS_WINDOWS +# include "win32/shell/win32_shell.c" +#else +# error Shell functions not implemented for this operating system +#endif diff --git a/Juliet/shell/shell_inc.h b/Juliet/shell/shell_inc.h new file mode 100644 index 0000000..0234ca7 --- /dev/null +++ b/Juliet/shell/shell_inc.h @@ -0,0 +1,9 @@ +#pragma once + +#include "shell.h" + +#if OS_WINDOWS +# include "win32/shell/win32_shell.h" +#else +# error Shell functions not implemented for this operating system +#endif diff --git a/Juliet/win32/shell/win32_shell.c b/Juliet/win32/shell/win32_shell.c new file mode 100644 index 0000000..a6df174 --- /dev/null +++ b/Juliet/win32/shell/win32_shell.c @@ -0,0 +1,11 @@ +// ============================================================================= +// Shell operations + +internal void sh_message(bool32 error, String8 title, String8 message) +{ + TempArena scratch = scratch_begin(0, 0); + String16 title16 = str16_from_8(scratch.arena, title); + String16 message16 = str16_from_8(scratch.arena, message); + MessageBoxW(0, (WCHAR*)message16.str, (WCHAR*)title16.str, MB_OK | (!!error * MB_ICONERROR)); + scratch_end(scratch); +} diff --git a/Juliet/win32/shell/win32_shell.h b/Juliet/win32/shell/win32_shell.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/Juliet/win32/shell/win32_shell.h @@ -0,0 +1 @@ +#pragma once