anton_render/src/render/d3d12/render_d3d12.c
2025-05-21 21:13:45 +02:00

325 lines
12 KiB
C

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxguid.lib")
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~
#define D3D12_ERROR(msg, ...) LOG("D3D12 ERROR (anton): "); LOG(msg, __VA_ARGS__); Trap();
#define D3D12_CHECK(hr, msg) if(FAILED((hr))) { D3D12_ERROR(msg, __VA_ARGS__) }
#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;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~
internal void
r_init()
{
Arena *arena = arena_alloc();
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;
hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void**)&r_d3d12_state->debug);
if(SUCCEEDED(hr) && r_d3d12_state->debug )
{
r_d3d12_state->debug->lpVtbl->EnableDebugLayer(r_d3d12_state->debug);
}
else
{
D3D12_ERROR("Failed to create D3D12 Debug interface \n");
}
// ---------------------------------------------------------------------------
// Create device
hr = D3D12CreateDevice(0,
D3D_FEATURE_LEVEL_11_0,
&IID_ID3D12Device,
(void **)&r_d3d12_state->device);
D3D12_CHECK(hr, "Failed to create device\n");
// ---------------------------------------------------------------------------
// Create command queue
D3D12_COMMAND_QUEUE_DESC queue_desc = {
.Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
.NodeMask = 0
};
hr = ID3D12Device_CreateCommandQueue(r_d3d12_state->device,
&queue_desc,
&IID_ID3D12CommandQueue,
(void**)&r_d3d12_cmd->queue);
D3D12_CHECK(hr, "Failed to create command queue\n");
// ---------------------------------------------------------------------------
// Create DXGI factory
IDXGIFactory4 *factory;
hr = CreateDXGIFactory1(&IID_IDXGIFactory4, (void**)&factory);
D3D12_CHECK(hr, "Failed to create factory\n");
// ---------------------------------------------------------------------------
// Create Swapchain
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {
.Width = WINDOW_WIDTH_PX,
.Height = WINDOW_HEIGHT_PX,
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
.Stereo = FALSE,
.SampleDesc = {1, 0},
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferCount = 2,
.Scaling = DXGI_SCALING_STRETCH,
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED,
.Flags = 0
};
// We create a temporary SwapChain1 that is then upgraded to a SwapChain3.
IDXGISwapChain1* temp_swap_chain;
hr = IDXGIFactory4_CreateSwapChainForHwnd(factory,
(IUnknown*)r_d3d12_cmd->queue,
r_d3d12_state->window_handle,
&swap_chain_desc,
0,
0,
&temp_swap_chain);
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);
D3D12_CHECK(hr, "Failed to upgrade to swapchain3\n");
temp_swap_chain->lpVtbl->Release(temp_swap_chain);
// ---------------------------------------------------------------------------
// Create Render target view (RTV) descriptor heap
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {
.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
.NumDescriptors = 2,
.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
.NodeMask = 0
};
hr = ID3D12Device_CreateDescriptorHeap(r_d3d12_state->device,
&rtv_heap_desc,
&IID_ID3D12DescriptorHeap,
(void**)&r_d3d12_rt->heap);
D3D12_CHECK(hr, "Failed to create descriptor heap\n");
r_d3d12_rt->descriptor_size = r_d3d12_state->device->lpVtbl->
GetDescriptorHandleIncrementSize(r_d3d12_state->device,
D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// ---------------------------------------------------------------------------
// Create render target views
// TODO(anton): This is probably not correct D3D12 API, but I might have an old SDK.
D3D12_CPU_DESCRIPTOR_HANDLE handle;
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &handle);
for (U32 i = 0; i < 2; ++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,
handle);
handle.ptr += r_d3d12_rt->descriptor_size;
}
// ---------------------------------------------------------------------------
// Command allocator and command list
hr = ID3D12Device_CreateCommandAllocator(r_d3d12_state->device,
D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator,
(void**)&r_d3d12_cmd->allocator);
D3D12_CHECK(hr, "Failed to create command allocator\n");
hr = ID3D12Device_CreateCommandList(r_d3d12_state->device,
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
r_d3d12_cmd->allocator,
0,
&IID_ID3D12GraphicsCommandList,
(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
hr = ID3D12Device_CreateFence(r_d3d12_state->device,
0,
D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence,
(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();
}
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 = 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;
// Clear render target
F32 clearColor[4] = {0.1f, 0.2f, 0.3f, 1.0f};
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;
ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier);
// Close command list
hr = ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1);
if (FAILED(hr)) return;
// Execute command list
ID3D12CommandList* commandLists[] = { (ID3D12CommandList*)r_d3d12_cmd->list1 };
ID3D12CommandQueue_ExecuteCommandLists(r_d3d12_cmd->queue, 1, commandLists);
// Present frame
IDXGISwapChain3_Present(r_d3d12_state->swapchain, 1, 0);
r_d3d12_wait_for_previous_frame();
}
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;
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);
}
}
}
// 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);
}