From 82117af3b8796d9e9bb3de2cdc0f3a7966d493ab Mon Sep 17 00:00:00 2001 From: Anton Ljungdahl Date: Wed, 21 May 2025 21:13:45 +0200 Subject: [PATCH] finally clear color with sync in d3d12 --- build.bat | 12 +- src/main.c | 2 + src/os/win32/os_core_win32.h | 1 + src/render/d3d12/render_d3d12.c | 232 +++++++++++++++++++------------- src/render/d3d12/render_d3d12.h | 29 +++- src/render/render_core.h | 1 + src/render/render_inc.h | 1 - 7 files changed, 180 insertions(+), 98 deletions(-) diff --git a/build.bat b/build.bat index e1e4a7a..05127ef 100644 --- a/build.bat +++ b/build.bat @@ -9,7 +9,7 @@ pushd .\build ctime -begin timeBuild.ctm -cl /Zi /nologo ../src/metagen/codegen.c +cl /Zi /nologo /wd5287 ../src/metagen/codegen.c set LastError=%ERRORLEVEL% ctime -end timeBuild.ctm %LastError% @@ -40,6 +40,8 @@ echo --- Main @rem Build main program set CommonCompilerFlags=/nologo /Zi /FC /Od /wd4042 +@rem set IncludeD3D12= +@rem set INCLUDE=%INCLUDE%;%IncludeD3D12% @rem /WX /W4 /wd4201 /wd4100 /wd4189 /wd4244 /wd4127 /wd4456 @rem @rem @@ -48,9 +50,13 @@ IF NOT EXIST .\build mkdir .\build pushd .\build ctime -begin timeBuild.ctm - +@rem echo INCLUDE: +@rem echo %INCLUDE% +@rem echo LIB: +@rem echo %LIB% +@rem @echo on cl %CommonCompilerFlags% ../src/main.c /Fe:program.exe - +@rem @echo off set LastError=%ERRORLEVEL% ctime -end timeBuild.ctm %LastError% popd diff --git a/src/main.c b/src/main.c index e5e987c..00382cb 100644 --- a/src/main.c +++ b/src/main.c @@ -53,6 +53,8 @@ entry_point() DispatchMessageW(&Message); } + r_render(); + //r_log_debug_messages(); if(g_do_exit) diff --git a/src/os/win32/os_core_win32.h b/src/os/win32/os_core_win32.h index 24fc79c..59fa991 100644 --- a/src/os/win32/os_core_win32.h +++ b/src/os/win32/os_core_win32.h @@ -4,6 +4,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //~ #define WIN32_LEAN_AND_MEAN +#define NOMINMAX #include internal void OS_W32_log_debug(const char *fmt_string, ...); diff --git a/src/render/d3d12/render_d3d12.c b/src/render/d3d12/render_d3d12.c index 6bff732..30d6b16 100644 --- a/src/render/d3d12/render_d3d12.c +++ b/src/render/d3d12/render_d3d12.c @@ -14,6 +14,8 @@ #define D3D12_RELEASE(res) if( (res) && (res)->lpVtbl) { (res)->lpVtbl->Release((res)); (res) = 0; } global R_D3D12_State *r_d3d12_state = 0; +global R_D3D12_Command *r_d3d12_cmd = 0; +global R_D3D12_RenderTarget *r_d3d12_rt = 0; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //~ @@ -25,11 +27,14 @@ r_init() r_d3d12_state = push_array(arena, R_D3D12_State, 1); r_d3d12_state->arena = arena; r_d3d12_state->window_handle = g_win32_window_handle; + + r_d3d12_cmd = push_array(arena, R_D3D12_Command, 1); + r_d3d12_cmd->arena = arena; + + r_d3d12_rt = push_array(arena, R_D3D12_RenderTarget, 1); + r_d3d12_rt->arena = arena; HRESULT hr; - - // --------------------------------------------------------------------------- - // Debug interface hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void**)&r_d3d12_state->debug); if(SUCCEEDED(hr) && r_d3d12_state->debug ) { @@ -58,11 +63,10 @@ r_init() .NodeMask = 0 }; - ID3D12CommandQueue *command_queue; - hr = r_d3d12_state->device->lpVtbl->CreateCommandQueue(r_d3d12_state->device, + hr = ID3D12Device_CreateCommandQueue(r_d3d12_state->device, &queue_desc, &IID_ID3D12CommandQueue, - (void**)&command_queue); + (void**)&r_d3d12_cmd->queue); D3D12_CHECK(hr, "Failed to create command queue\n"); @@ -93,8 +97,8 @@ r_init() // We create a temporary SwapChain1 that is then upgraded to a SwapChain3. IDXGISwapChain1* temp_swap_chain; - hr = factory->lpVtbl->CreateSwapChainForHwnd(factory, - (IUnknown*)command_queue, + hr = IDXGIFactory4_CreateSwapChainForHwnd(factory, + (IUnknown*)r_d3d12_cmd->queue, r_d3d12_state->window_handle, &swap_chain_desc, 0, @@ -103,8 +107,8 @@ r_init() D3D12_CHECK(hr, "Failed to create temp swap chain \n"); hr = temp_swap_chain->lpVtbl->QueryInterface(temp_swap_chain, - &IID_IDXGISwapChain3, - (void**)&r_d3d12_state->swapchain); + &IID_IDXGISwapChain3, + (void**)&r_d3d12_state->swapchain); D3D12_CHECK(hr, "Failed to upgrade to swapchain3\n"); temp_swap_chain->lpVtbl->Release(temp_swap_chain); @@ -118,141 +122,153 @@ r_init() }; - ID3D12DescriptorHeap *rtv_heap; - hr = r_d3d12_state->device->lpVtbl-> - CreateDescriptorHeap(r_d3d12_state->device, + hr = ID3D12Device_CreateDescriptorHeap(r_d3d12_state->device, &rtv_heap_desc, &IID_ID3D12DescriptorHeap, - (void**)&rtv_heap); + (void**)&r_d3d12_rt->heap); D3D12_CHECK(hr, "Failed to create descriptor heap\n"); - U32 rtv_descriptor_size = r_d3d12_state->device->lpVtbl-> - GetDescriptorHandleIncrementSize(r_d3d12_state->device, - D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + r_d3d12_rt->descriptor_size = r_d3d12_state->device->lpVtbl-> + GetDescriptorHandleIncrementSize(r_d3d12_state->device, + D3D12_DESCRIPTOR_HEAP_TYPE_RTV); // --------------------------------------------------------------------------- - // Create render target views - ID3D12Resource* render_targets[2]; - D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle; + // Create render target views // TODO(anton): This is probably not correct D3D12 API, but I might have an old SDK. - rtv_heap->lpVtbl->GetCPUDescriptorHandleForHeapStart(rtv_heap, &rtv_handle); + D3D12_CPU_DESCRIPTOR_HANDLE handle; + ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &handle); for (U32 i = 0; i < 2; ++i) { - r_d3d12_state->swapchain->lpVtbl->GetBuffer(r_d3d12_state->swapchain, - i, - &IID_ID3D12Resource, - (void**)&render_targets[i]); - r_d3d12_state->device->lpVtbl-> - CreateRenderTargetView(r_d3d12_state->device, - render_targets[i], + IDXGISwapChain3_GetBuffer(r_d3d12_state->swapchain, + i, + &IID_ID3D12Resource, + (void**)&r_d3d12_rt->targets[i]); + + ID3D12Device_CreateRenderTargetView(r_d3d12_state->device, + r_d3d12_rt->targets[i], 0, - rtv_handle); + handle); - rtv_handle.ptr += rtv_descriptor_size; + handle.ptr += r_d3d12_rt->descriptor_size; } // --------------------------------------------------------------------------- // Command allocator and command list - ID3D12CommandAllocator* cmd_allocator; - r_d3d12_state->device->lpVtbl->CreateCommandAllocator(r_d3d12_state->device, + hr = ID3D12Device_CreateCommandAllocator(r_d3d12_state->device, D3D12_COMMAND_LIST_TYPE_DIRECT, &IID_ID3D12CommandAllocator, - (void**)&cmd_allocator); + (void**)&r_d3d12_cmd->allocator); + D3D12_CHECK(hr, "Failed to create command allocator\n"); - ID3D12GraphicsCommandList* cmd_list; - r_d3d12_state->device->lpVtbl->CreateCommandList(r_d3d12_state->device, + hr = ID3D12Device_CreateCommandList(r_d3d12_state->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, - cmd_allocator, + r_d3d12_cmd->allocator, 0, &IID_ID3D12GraphicsCommandList, - (void**)&cmd_list); + (void**)&r_d3d12_cmd->list1); + D3D12_CHECK(hr, "Failed to create command list\n"); + // Put it in a closed state during initialisation. It will be opened at the start of the render function. + ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1); // --------------------------------------------------------------------------- - // Fence and event - - ID3D12Fence *fence; - U64 fence_value = 0; - hr = r_d3d12_state->device->lpVtbl->CreateFence(r_d3d12_state->device, + // Fence + hr = ID3D12Device_CreateFence(r_d3d12_state->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, - (void**)&fence); - HANDLE fence_event = CreateEvent(0, FALSE, FALSE, 0); + (void**)&r_d3d12_state->fence); + D3D12_CHECK(hr, "Failed to create fence\n"); + r_d3d12_state->fence_value = 1; + + r_d3d12_state->fence_event = CreateEvent(0, FALSE, FALSE, 0); + if(!r_d3d12_state->fence_event) + { + HRESULT fence_hr = HRESULT_FROM_WIN32(GetLastError()); + D3D12_CHECK(fence_hr, "Failed to create fence event\n"); + } + + r_d3d12_wait_for_previous_frame(); - // Record commands to clear screen - U32 frameIndex = r_d3d12_state->swapchain->lpVtbl->GetCurrentBackBufferIndex(r_d3d12_state->swapchain); +} - cmd_allocator->lpVtbl->Reset(cmd_allocator); - cmd_list->lpVtbl->Reset(cmd_list, cmd_allocator, 0); +internal void +r_d3d12_wait_for_previous_frame() +{ + U64 fence_value = r_d3d12_state->fence_value; + ID3D12CommandQueue_Signal(r_d3d12_cmd->queue, r_d3d12_state->fence, fence_value); + r_d3d12_state->fence_value += 1; + if(ID3D12Fence_GetCompletedValue(r_d3d12_state->fence) < fence_value) + { + HRESULT hr = ID3D12Fence_SetEventOnCompletion(r_d3d12_state->fence, fence_value, r_d3d12_state->fence_event); + D3D12_CHECK(hr, "Failed to set fence event on completion\n"); + WaitForSingleObject(r_d3d12_state->fence_event, INFINITE); + } +} + +internal void +r_render() +{ + + + HRESULT hr = ID3D12CommandAllocator_Reset(r_d3d12_cmd->allocator); + D3D12_CHECK(hr, "Failed to reset command allocator\n"); + hr = ID3D12GraphicsCommandList_Reset(r_d3d12_cmd->list1, r_d3d12_cmd->allocator, 0); + D3D12_CHECK(hr, "Failed to reset command list\n"); + + U32 frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(r_d3d12_state->swapchain); D3D12_RESOURCE_BARRIER barrier = { .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, .Transition = { - .pResource = render_targets[frameIndex], + .pResource = r_d3d12_rt->targets[frame_index], .StateBefore = D3D12_RESOURCE_STATE_PRESENT, .StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET, .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES } }; + // Transition from present to render target + ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle; + ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &rtv_handle); + rtv_handle.ptr += frame_index * r_d3d12_rt->descriptor_size; - cmd_list->lpVtbl->ResourceBarrier(cmd_list, 1, &barrier); - - D3D12_CPU_DESCRIPTOR_HANDLE currentRTV; - // TODO(anton): Probably wrong API due to old SDK... - rtv_heap->lpVtbl->GetCPUDescriptorHandleForHeapStart(rtv_heap, ¤tRTV); - currentRTV.ptr += frameIndex * rtv_descriptor_size; - + // Clear render target F32 clearColor[4] = {0.1f, 0.2f, 0.3f, 1.0f}; - cmd_list->lpVtbl->ClearRenderTargetView(cmd_list, currentRTV, clearColor, 0, 0); + ID3D12GraphicsCommandList_ClearRenderTargetView(r_d3d12_cmd->list1, rtv_handle, clearColor, 0, NULL); + // Transition render target back to present state barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - cmd_list->lpVtbl->ResourceBarrier(cmd_list, 1, &barrier); + ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier); - cmd_list->lpVtbl->Close(cmd_list); + // Close command list + hr = ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1); + if (FAILED(hr)) return; - // Execute and present - ID3D12CommandList* lists[] = { (ID3D12CommandList*)cmd_list }; - command_queue->lpVtbl->ExecuteCommandLists(command_queue, 1, lists); + // Execute command list + ID3D12CommandList* commandLists[] = { (ID3D12CommandList*)r_d3d12_cmd->list1 }; + ID3D12CommandQueue_ExecuteCommandLists(r_d3d12_cmd->queue, 1, commandLists); - r_d3d12_state->swapchain->lpVtbl->Present(r_d3d12_state->swapchain, 1, 0); + // Present frame + IDXGISwapChain3_Present(r_d3d12_state->swapchain, 1, 0); - // Wait for GPU - fence_value++; - command_queue->lpVtbl->Signal(command_queue, fence, fence_value); - if (fence->lpVtbl->GetCompletedValue(fence) < fence_value) { - fence->lpVtbl->SetEventOnCompletion(fence, fence_value, fence_event); - WaitForSingleObject(fence_event, INFINITE); - } -} + r_d3d12_wait_for_previous_frame(); -internal void -r_trigger_debug_message() -{ - if(r_d3d12_state->device) - { - D3D12_COMMAND_QUEUE_DESC queue_desc = {0}; - queue_desc.Type = -1; // Error value, should be 0,1,2 or 3 - - ID3D12CommandQueue *queue = 0; - HRESULT hr = r_d3d12_state->device->lpVtbl->CreateCommandQueue(r_d3d12_state->device, - &queue_desc, - &IID_ID3D12CommandQueue, - (void**)&queue); - if(queue) { - queue->lpVtbl->Release(queue); - } - } } internal void r_cleanup() { - + r_d3d12_wait_for_previous_frame(); + + U32 do_report_live_objects = 2; // 1 for all, 2 for only after release to check any dangling. // Report live objects - { + if(do_report_live_objects == 1) + { + LOG("D3D12 Reporting live objects before cleanup:\n"); if(r_d3d12_state->device) { ID3D12DebugDevice *debug_device = 0; @@ -268,10 +284,42 @@ r_cleanup() } } - D3D12_RELEASE(r_d3d12_state->device); + // State + D3D12_RELEASE(r_d3d12_state->debug); D3D12_RELEASE(r_d3d12_state->swapchain); + D3D12_RELEASE(r_d3d12_state->fence); + // RT + D3D12_RELEASE(r_d3d12_rt->heap); + for(U32 i = 0; i < R_NUM_FRAMES_IN_FLIGHT; i+=1) + { + D3D12_RELEASE(r_d3d12_rt->targets[i]); + } + // Cmd + D3D12_RELEASE(r_d3d12_cmd->queue); + D3D12_RELEASE(r_d3d12_cmd->allocator); + D3D12_RELEASE(r_d3d12_cmd->list1); + // Report live objects + if(do_report_live_objects >= 1) + { + LOG("D3D12 Reporting live objects after cleanup (should only be device):\n"); + if(r_d3d12_state->device) + { + ID3D12DebugDevice *debug_device = 0; + HRESULT hr = r_d3d12_state->device->lpVtbl->QueryInterface(r_d3d12_state->device, + &IID_ID3D12DebugDevice, + (void **)&debug_device); + if(SUCCEEDED(hr) && debug_device) + { + debug_device->lpVtbl->ReportLiveDeviceObjects(debug_device, + D3D12_RLDO_DETAIL | D3D12_RLDO_IGNORE_INTERNAL); + debug_device->lpVtbl->Release(debug_device); + } + } + } + D3D12_RELEASE(r_d3d12_state->device); arena_release(r_d3d12_state->arena); -} + +} \ No newline at end of file diff --git a/src/render/d3d12/render_d3d12.h b/src/render/d3d12/render_d3d12.h index bd9748a..098a7d1 100644 --- a/src/render/d3d12/render_d3d12.h +++ b/src/render/d3d12/render_d3d12.h @@ -1,6 +1,7 @@ #ifndef RENDER_D3D12_H #define RENDER_D3D12_H +#define COBJMACROS 1 #include #include @@ -12,6 +13,27 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //~ +#define R_NUM_FRAMES_IN_FLIGHT 2 + +typedef struct R_D3D12_Command R_D3D12_Command; +struct R_D3D12_Command +{ + Arena *arena; + ID3D12CommandQueue *queue; + ID3D12CommandAllocator *allocator; + ID3D12GraphicsCommandList *list1; +}; + +typedef struct R_D3D12_RenderTarget R_D3D12_RenderTarget; +struct R_D3D12_RenderTarget +{ + Arena *arena; + ID3D12DescriptorHeap *heap; + D3D12_CPU_DESCRIPTOR_HANDLE handle; + U32 descriptor_size; + ID3D12Resource *targets[R_NUM_FRAMES_IN_FLIGHT]; +}; + typedef struct R_D3D12_State R_D3D12_State; struct R_D3D12_State { @@ -19,12 +41,15 @@ struct R_D3D12_State HWND window_handle; ID3D12Device *device; ID3D12Debug *debug; - ID3D12InfoQueue *info_queue; IDXGISwapChain3 *swapchain; + ID3D12Fence *fence; + U64 fence_value; + U64 frame_fence_values[R_NUM_FRAMES_IN_FLIGHT]; + HANDLE fence_event; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //~ internal LRESULT r_d3d12_create_swapchain(IDXGIFactory4 *factory); - +internal void r_d3d12_wait_for_previous_frame(); #endif /* RENDER_D3D12_H */ \ No newline at end of file diff --git a/src/render/render_core.h b/src/render/render_core.h index 72470cf..0a206d8 100644 --- a/src/render/render_core.h +++ b/src/render/render_core.h @@ -6,6 +6,7 @@ internal void r_init(); +internal void r_render(); internal void r_log_debug_messages(); diff --git a/src/render/render_inc.h b/src/render/render_inc.h index 132ed50..5709a69 100644 --- a/src/render/render_inc.h +++ b/src/render/render_inc.h @@ -4,7 +4,6 @@ ////////////////////////////////////////////////////////////////////////////////////////////// //~ - #include "render/render_core.h" #include "render/d3d12/render_d3d12.h"