From 5fa75c116e15a4b93cff8c317bc9d86e179eb097 Mon Sep 17 00:00:00 2001 From: Patedam Date: Tue, 27 Oct 2026 23:06:45 -0400 Subject: [PATCH] blue window --- Juliet/core/core_math.h | 11 ++ Juliet/julietapp/julietapp_core.c | 9 + Juliet/render/d3d12/render_d3d12.c | 308 +++++++++++++++++++++++------ Juliet/render/d3d12/render_d3d12.h | 27 ++- Juliet/render/render.h | 7 + 5 files changed, 300 insertions(+), 62 deletions(-) diff --git a/Juliet/core/core_math.h b/Juliet/core/core_math.h index 7c5d9dc..4c6450a 100644 --- a/Juliet/core/core_math.h +++ b/Juliet/core/core_math.h @@ -14,6 +14,17 @@ union Vector2f float32 v[2]; }; +typedef union Vector2i Vector2i; +union Vector2i +{ + struct + { + int32 x; + int32 y; + }; + int32 v[2]; +}; + // ============================================================================= // Rect Types diff --git a/Juliet/julietapp/julietapp_core.c b/Juliet/julietapp/julietapp_core.c index f2ab15d..4691d9c 100644 --- a/Juliet/julietapp/julietapp_core.c +++ b/Juliet/julietapp/julietapp_core.c @@ -104,6 +104,15 @@ internal void ja_frame() } } + // render frame + r_begin_frame(); + for (JA_WindowState* w = ja_state->first_window_state; w != &ja_nil_window_state; w = w->order_next) + { + r_window_begin_frame(w->plat_handle, w->render_handle); + r_window_end_frame(w->plat_handle, w->render_handle); + } + r_end_frame(); + // show window after first frame if (ja_state->frame_depth == 1) { diff --git a/Juliet/render/d3d12/render_d3d12.c b/Juliet/render/d3d12/render_d3d12.c index 471cad5..65bbf99 100644 --- a/Juliet/render/d3d12/render_d3d12.c +++ b/Juliet/render/d3d12/render_d3d12.c @@ -21,6 +21,20 @@ internal R_Handle r_d3d12_handle_from_window(R_D3D12_Window* window) return handle; } +// ============================================================================= +// Wait Function + +internal void r_d3d12_wait() +{ + r_d3d12_state->fence_value += 1; + ID3D12CommandQueue_Signal(r_d3d12_state->direct_command_queue, r_d3d12_state->fence, r_d3d12_state->fence_value); + if (ID3D12Fence_GetCompletedValue(r_d3d12_state->fence) < r_d3d12_state->fence_value) + { + ID3D12Fence_SetEventOnCompletion(r_d3d12_state->fence, r_d3d12_state->fence_value, r_d3d12_state->fence_event); + WaitForSingleObject(r_d3d12_state->fence_event, INFINITE); + } +} + // ============================================================================= // top level layer initialization @@ -37,6 +51,13 @@ void r_init() UINT dxgi_flags = 0; #if BUILD_DEBUG + ID3D12Debug1* debug_controller = 0; + if (SUCCEEDED(D3D12GetDebugInterface(&IID_ID3D12Debug1, (void**)&debug_controller))) + { + ID3D12Debug1_EnableDebugLayer(debug_controller); + ID3D12Debug1_SetEnableGPUBasedValidation(debug_controller, TRUE); + ID3D12Debug1_Release(debug_controller); + } dxgi_flags |= DXGI_CREATE_FACTORY_DEBUG; #endif @@ -49,38 +70,6 @@ void r_init() 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); @@ -97,6 +86,41 @@ void r_init() error = IDXGIFactory4_EnumAdapters1(r_d3d12_state->dxgi_factory, 0, &r_d3d12_state->dxgi_adapter); } + error = D3D12CreateDevice((IUnknown*)r_d3d12_state->dxgi_adapter, feature_level, &IID_ID3D12Device4, + (void**)&r_d3d12_state->device); + + // Try warp as hardware is no available + if (FAILED(error)) + { + IDXGIAdapter1_Release(r_d3d12_state->dxgi_adapter); + error = IDXGIFactory4_EnumWarpAdapter(r_d3d12_state->dxgi_factory, &IID_IDXGIAdapter, (void**)&r_d3d12_state->dxgi_adapter); + if (SUCCEEDED(error)) + { + error = D3D12CreateDevice((IUnknown*)r_d3d12_state->dxgi_adapter, feature_level, &IID_ID3D12Device4, + (void**)&r_d3d12_state->device); + } + } + + 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 + // Create basic graphics queue D3D12_COMMAND_QUEUE_DESC queue_desc = { 0 }; queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; @@ -110,10 +134,39 @@ void r_init() sh_message(1, str8_literal("Fatal Error"), str8_cstring(buffer)); abort_self(1); } + + // Create command allocator and command list + for EachIndex(i, 2) + { + ID3D12Device4_CreateCommandAllocator(r_d3d12_state->device, D3D12_COMMAND_LIST_TYPE_DIRECT, + &IID_ID3D12CommandAllocator, (void**)&r_d3d12_state->command_allocators[i]); + } + + ID3D12Device4_CreateCommandList1(r_d3d12_state->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_FLAG_NONE, + &IID_ID3D12GraphicsCommandList, (void**)&r_d3d12_state->command_list); + + ID3D12Device4_CreateFence(r_d3d12_state->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void**)&r_d3d12_state->fence); + r_d3d12_state->fence_event = CreateEventW(0, FALSE, FALSE, 0); } void r_shutdown() { + MutexScopeW(r_d3d12_state->device_rw_mutex) + { + r_d3d12_wait(); + + CloseHandle(r_d3d12_state->fence_event); + ID3D12Fence_Release(r_d3d12_state->fence); + for EachIndex(i,2) + { + ID3D12CommandAllocator_Release(r_d3d12_state->command_allocators[i]); + } + ID3D12CommandList_Release(r_d3d12_state->command_list); + ID3D12CommandQueue_Release(r_d3d12_state->direct_command_queue); + ID3D12Device4_Release(r_d3d12_state->device); + IDXGIAdapter1_Release(r_d3d12_state->dxgi_adapter); + IDXGIFactory4_Release(r_d3d12_state->dxgi_factory); + } rw_mutex_release(r_d3d12_state->device_rw_mutex); arena_release(r_d3d12_state->arena); } @@ -145,31 +198,31 @@ R_Handle r_window_equip(WM_Window handle) } // 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_DESC1 swapchain_desc = { 0 }; + swapchain_desc.Width = 0; // Use all window width + swapchain_desc.Height = 0; // Use all window height + swapchain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapchain_desc.Stereo = 0; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = 2; // Double buffering + swapchain_desc.Scaling = DXGI_SCALING_NONE; + swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapchain_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; + DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapchain_fullscreen_desc = { 0 }; + swapchain_fullscreen_desc.RefreshRate.Numerator = 0; + swapchain_fullscreen_desc.RefreshRate.Denominator = 0; + swapchain_fullscreen_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchain_fullscreen_desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapchain_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); + IDXGISwapChain1* swapchain1 = 0; + HRESULT error = IDXGIFactory4_CreateSwapChainForHwnd(r_d3d12_state->dxgi_factory, + (IUnknown*)r_d3d12_state->direct_command_queue, hwnd, + &swapchain_desc, &swapchain_fullscreen_desc, 0, &swapchain1); if (FAILED(error)) { char buffer[256]; @@ -178,11 +231,38 @@ R_Handle r_window_equip(WM_Window handle) abort_self(1); } - IDXGISwapChain1_QueryInterface(swap_chain1, &IID_IDXGISwapChain3, (void**)&window->swap_chain); - IDXGISwapChain1_Release(swap_chain1); + IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void**)&window->swapchain); + IDXGISwapChain1_Release(swapchain1); IDXGIFactory4_MakeWindowAssociation(r_d3d12_state->dxgi_factory, hwnd, DXGI_MWA_NO_ALT_ENTER); + // Create framebuffer and rtv for swapchain + D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = { 0 }; + rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtv_heap_desc.NumDescriptors = 2; + rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + + ID3D12Device4_CreateDescriptorHeap(r_d3d12_state->device, &rtv_heap_desc, &IID_ID3D12DescriptorHeap, + (void**)&window->rtv_heap); + + UINT rtv_stride = ID3D12Device4_GetDescriptorHandleIncrementSize(r_d3d12_state->device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = { 0 }; + ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(window->rtv_heap, &rtv_handle); + + for (UINT i = 0; i < 2; ++i) + { + window->rtv_handles[i] = rtv_handle; + IDXGISwapChain3_GetBuffer(window->swapchain, i, &IID_ID3D12Resource, (void**)&window->framebuffer[i]); + ID3D12Device4_CreateRenderTargetView(r_d3d12_state->device, window->framebuffer[i], 0, window->rtv_handles[i]); + rtv_handle.ptr += rtv_stride; + } + + // Save resolution in case of resize + Rect2f client_rect = wm_client_rect_from_window(handle); + Vector2i resolution = { (int32)(client_rect.x1 - client_rect.x0), (int32)(client_rect.y1 - client_rect.y0) }; + window->last_resolution = resolution; + result = r_d3d12_handle_from_window(window); } return result; @@ -192,11 +272,121 @@ void r_window_unequip(WM_Window handle, R_Handle handle_equip) { MutexScopeW(r_d3d12_state->device_rw_mutex) { + r_d3d12_wait(); + R_D3D12_Window* window = r_d3d12_window_from_handle(handle_equip); - if (window != 0) + if (window != 0 && window != &r_d3d12_nil_window) { - IDXGISwapChain1_Release(window->swap_chain); + for EachIndex(i, 2) + { + ID3D12Resource_Release(window->framebuffer[i]); + } + ID3D12DescriptorHeap_Release(window->rtv_heap); + IDXGISwapChain1_Release(window->swapchain); SLL_stack_push(r_d3d12_state->first_free_window, window); } } } + +// frame markers + +void r_begin_frame() +{ + // No-op +} + +void r_end_frame() +{ + // pouet +} + +void r_window_begin_frame(WM_Window window, R_Handle window_equip) +{ + MutexScopeW(r_d3d12_state->device_rw_mutex) + { + R_D3D12_Window* r_window = r_d3d12_window_from_handle(window_equip); + + // Get resolution + Rect2f client_rect = wm_client_rect_from_window(window); + Vector2i resolution = { (int32)(client_rect.x1 - client_rect.x0), (int32)(client_rect.y1 - client_rect.y0) }; + + // change resolution + if (resolution.x > 0 && resolution.y > 0 && + (r_window->last_resolution.x != resolution.x || r_window->last_resolution.y != resolution.y)) + { + r_window->last_resolution = resolution; + + // resize swap chain + r_d3d12_wait(); + + for EachIndex(i,2) + { + if (r_window->framebuffer[i] != 0) + { + ID3D12Resource_Release(r_window->framebuffer[i]); + r_window->framebuffer[i] = 0; + } + } + + IDXGISwapChain3_ResizeBuffers(r_window->swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); + + for EachIndex(i,2) + { + IDXGISwapChain3_GetBuffer(r_window->swapchain, i, &IID_ID3D12Resource, (void**)&r_window->framebuffer[i]); + ID3D12Device4_CreateRenderTargetView(r_d3d12_state->device, r_window->framebuffer[i], 0, r_window->rtv_handles[i]); + } + } + + // Open the command list for that window that frame + // TODO : multiple command list as we have multiple windows + r_window->framebuffer_idx = IDXGISwapChain3_GetCurrentBackBufferIndex(r_window->swapchain); + ID3D12CommandAllocator* allocator = r_d3d12_state->command_allocators[r_window->framebuffer_idx]; + ID3D12GraphicsCommandList* cmd_list = r_d3d12_state->command_list; + + ID3D12CommandAllocator_Reset(allocator); + ID3D12GraphicsCommandList1_Reset(cmd_list, allocator, 0); + + // Barrier to transition from Present (end of frame) to render target + D3D12_RESOURCE_BARRIER barrier = { 0 }; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = r_window->framebuffer[r_window->framebuffer_idx]; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + ID3D12GraphicsCommandList_ResourceBarrier(cmd_list, 1, &barrier); + + // HARDCODED + // BLUE + float32 background[4] = { 0.1f, 0.2f, 0.8f, 1.0f }; + ID3D12GraphicsCommandList_ClearRenderTargetView(cmd_list, r_window->rtv_handles[r_window->framebuffer_idx], + background, 0, 0); + } +} + +void r_window_end_frame(WM_Window window, R_Handle window_equip) +{ + MutexScopeW(r_d3d12_state->device_rw_mutex) + { + R_D3D12_Window* r_window = r_d3d12_window_from_handle(window_equip); + + ID3D12GraphicsCommandList* cmd_list = r_d3d12_state->command_list; + + // Transition from render target to present + D3D12_RESOURCE_BARRIER barrier = { 0 }; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = r_window->framebuffer[r_window->framebuffer_idx]; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + ID3D12GraphicsCommandList_ResourceBarrier(cmd_list, 1, &barrier); + + // Close command list and push it onto the command queue + ID3D12GraphicsCommandList_Close(cmd_list); + ID3D12CommandQueue_ExecuteCommandLists(r_d3d12_state->direct_command_queue, 1, (ID3D12CommandList**)&cmd_list); + + IDXGISwapChain3_Present(r_window->swapchain, 1, 0); + + // Fence until gpu finishes work + r_d3d12_wait(); + } +} diff --git a/Juliet/render/d3d12/render_d3d12.h b/Juliet/render/d3d12/render_d3d12.h index 48c0fc8..1c58c0f 100644 --- a/Juliet/render/d3d12/render_d3d12.h +++ b/Juliet/render/d3d12/render_d3d12.h @@ -17,8 +17,17 @@ typedef struct R_D3D12_Window R_D3D12_Window; struct R_D3D12_Window { - R_D3D12_Window* next; - IDXGISwapChain3* swap_chain; + R_D3D12_Window* next; + + // SwapChain and framebuffer + IDXGISwapChain3* swapchain; + ID3D12Resource* framebuffer[2]; + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handles[2]; + ID3D12DescriptorHeap* rtv_heap; + uint8 framebuffer_idx; + + // last state + Vector2i last_resolution; }; typedef struct R_D3D12_State R_D3D12_State; @@ -35,7 +44,14 @@ struct R_D3D12_State IDXGIAdapter1* dxgi_adapter; // Commands - ID3D12CommandQueue* direct_command_queue; + ID3D12CommandQueue* direct_command_queue; + ID3D12CommandAllocator* command_allocators[2]; + ID3D12GraphicsCommandList* command_list; + + // Fence + ID3D12Fence* fence; + uint64 fence_value; + HANDLE fence_event; }; // ============================================================================= @@ -49,3 +65,8 @@ global read_only R_D3D12_Window r_d3d12_nil_window = { &r_d3d12_nil_window }; internal R_D3D12_Window* r_d3d12_window_from_handle(R_Handle handle); internal R_Handle r_d3d12_handle_from_window(R_D3D12_Window* window); + +// ============================================================================= +// Wait Function + +internal void r_d3d12_wait(); diff --git a/Juliet/render/render.h b/Juliet/render/render.h index 04cd74b..2c7b08b 100644 --- a/Juliet/render/render.h +++ b/Juliet/render/render.h @@ -21,3 +21,10 @@ void r_shutdown(); R_Handle r_window_equip(WM_Window window); void r_window_unequip(WM_Window window, R_Handle window_equip); + +// frame markers + +void r_begin_frame(); +void r_end_frame(); +void r_window_begin_frame(WM_Window window, R_Handle window_equip); +void r_window_end_frame(WM_Window window, R_Handle window_equip);