/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //~ #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); }