From 9b0183055951b40b73b52ba3f1024394f1f439f9 Mon Sep 17 00:00:00 2001 From: Artem Kharytoniuk Date: Sat, 21 Oct 2017 22:50:40 +0200 Subject: [PATCH] DX12: initial rendering code, just clear the render target. --- src/engine/renderer/tr_backend.c | 2 + src/engine/renderer/vk.cpp | 108 ++++++++++++++++++++++++++++++- src/engine/renderer/vk.h | 12 +++- 3 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/engine/renderer/tr_backend.c b/src/engine/renderer/tr_backend.c index 4ae7b85..6bb12e2 100644 --- a/src/engine/renderer/tr_backend.c +++ b/src/engine/renderer/tr_backend.c @@ -870,6 +870,7 @@ const void *RB_DrawBuffer( const void *data ) { // VULKAN vk_begin_frame(); + dx_begin_frame(); // clear screen for debugging if ( r_clear->integer ) { @@ -1047,6 +1048,7 @@ const void *RB_SwapBuffers( const void *data ) { // VULKAN vk_end_frame(); + dx_end_frame(); return (const void *)(cmd + 1); } diff --git a/src/engine/renderer/vk.cpp b/src/engine/renderer/vk.cpp index 2fea5b3..1d5d038 100644 --- a/src/engine/renderer/vk.cpp +++ b/src/engine/renderer/vk.cpp @@ -870,6 +870,7 @@ void dx_initialize() { // This sample does not support fullscreen transitions. DX_CHECK(factory->MakeWindowAssociation(g_wv.hWnd_dx, DXGI_MWA_NO_ALT_ENTER)); DX_CHECK(swapchain.As(&vk.dx_swapchain)); + vk.dx_frame_index = vk.dx_swapchain->GetCurrentBackBufferIndex(); // Create descriptor heaps. { @@ -890,13 +891,44 @@ void dx_initialize() { // Create a RTV for each frame. for (UINT n = 0; n < D3D_FRAME_COUNT; n++) { - DX_CHECK(vk.dx_swapchain->GetBuffer(n, IID_PPV_ARGS(&vk.render_targets[n]))); - vk.dx_device->CreateRenderTargetView(vk.render_targets[n].Get(), nullptr, rtv_handle); + DX_CHECK(vk.dx_swapchain->GetBuffer(n, IID_PPV_ARGS(&vk.dx_render_targets[n]))); + vk.dx_device->CreateRenderTargetView(vk.dx_render_targets[n], nullptr, rtv_handle); rtv_handle.Offset(1, vk.dx_rtv_descriptor_size); } } DX_CHECK(vk.dx_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vk.dx_command_allocator))); + + + DX_CHECK(vk.dx_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vk.dx_command_allocator, nullptr, IID_PPV_ARGS(&vk.dx_command_list))); + + // Command lists are created in the recording state, but there is nothing + // to record yet. The main loop expects it to be closed, so close it now. + DX_CHECK(vk.dx_command_list->Close()); + + // Create synchronization objects. + { + DX_CHECK(vk.dx_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vk.dx_fence))); + vk.dx_fence_value = 1; + + // Create an event handle to use for frame synchronization. + vk.dx_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (vk.dx_fence_event == NULL) + { + DX_CHECK(HRESULT_FROM_WIN32(GetLastError())); + } + } + + // Create an empty root signature. + /* { + CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ComPtr signature; + ComPtr error; + DX_CHECK(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); + DX_CHECK(vk.dx_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature))); + }*/ } void vk_initialize() { @@ -1386,7 +1418,7 @@ void dx_shutdown() { vk.dx_command_allocator = nullptr; for (int i = 0; i < D3D_FRAME_COUNT; i++) { - vk.render_targets[i].Reset(); + vk.dx_render_targets[i]->Release(); } vk.dx_rtv_heap->Release(); @@ -1395,6 +1427,15 @@ void dx_shutdown() { vk.dx_command_queue->Release(); vk.dx_command_queue = nullptr; + vk.dx_command_list->Release(); + vk.dx_command_list = nullptr; + + vk.dx_fence->Release(); + vk.dx_fence = nullptr; + + ::CloseHandle(vk.dx_fence_event); + vk.dx_fence_event = NULL; + vk.dx_device->Release(); vk.dx_device = nullptr; } @@ -2411,6 +2452,67 @@ void vk_shade_geometry(VkPipeline pipeline, bool multitexture, Vk_Depth_Range de vk_world.dirty_depth_attachment = true; } +void dx_begin_frame() { + // Command list allocators can only be reset when the associated + // command lists have finished execution on the GPU; apps should use + // fences to determine GPU execution progress. + DX_CHECK(vk.dx_command_allocator->Reset()); + + // However, when ExecuteCommandList() is called on a particular command + // list, that command list can then be reset at any time and must be before + // re-recording. + DX_CHECK(vk.dx_command_list->Reset(vk.dx_command_allocator, vk.dx_pipeline_state)); + + // Indicate that the back buffer will be used as a render target. + vk.dx_command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(vk.dx_render_targets[vk.dx_frame_index], + D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); + + CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(vk.dx_rtv_heap->GetCPUDescriptorHandleForHeapStart(), vk.dx_frame_index, vk.dx_rtv_descriptor_size); + + // Record commands. + const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; + vk.dx_command_list->ClearRenderTargetView(rtv_handle, clearColor, 0, nullptr); + + // Indicate that the back buffer will now be used to present. + vk.dx_command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(vk.dx_render_targets[vk.dx_frame_index], + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); + + DX_CHECK(vk.dx_command_list->Close()); +} + +void wait_for_previous_frame() +{ + // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE. + // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering + // sample illustrates how to use fences for efficient resource usage and to + // maximize GPU utilization. + + // Signal and increment the fence value. + const UINT64 fence = vk.dx_fence_value; + DX_CHECK(vk.dx_command_queue->Signal(vk.dx_fence, fence)); + vk.dx_fence_value++; + + // Wait until the previous frame is finished. + if (vk.dx_fence->GetCompletedValue() < fence) + { + DX_CHECK(vk.dx_fence->SetEventOnCompletion(fence, vk.dx_fence_event)); + WaitForSingleObject(vk.dx_fence_event, INFINITE); + } + + vk.dx_frame_index = vk.dx_swapchain->GetCurrentBackBufferIndex(); +} + +void dx_end_frame() { + // Execute the command list. + ID3D12CommandList* ppCommandLists[] = { vk.dx_command_list }; + vk.dx_command_queue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); + + // Present the frame. + DX_CHECK(vk.dx_swapchain->Present(1, 0)); + + wait_for_previous_frame(); +} + void vk_begin_frame() { if (!vk.active) return; diff --git a/src/engine/renderer/vk.h b/src/engine/renderer/vk.h index 77e29fd..8d867b1 100644 --- a/src/engine/renderer/vk.h +++ b/src/engine/renderer/vk.h @@ -122,6 +122,9 @@ void vk_shade_geometry(VkPipeline pipeline, bool multitexture, Vk_Depth_Range de void vk_begin_frame(); void vk_end_frame(); +void dx_begin_frame(); +void dx_end_frame(); + void vk_read_pixels(byte* buffer); // screenshots // Vk_Instance contains engine-specific vulkan resources that persist entire renderer lifetime. @@ -134,8 +137,15 @@ struct Vk_Instance { ComPtr dx_swapchain; ID3D12DescriptorHeap* dx_rtv_heap = nullptr; UINT dx_rtv_descriptor_size = 0; - ComPtr render_targets[D3D_FRAME_COUNT]; + ID3D12Resource* dx_render_targets[D3D_FRAME_COUNT]; ID3D12CommandAllocator* dx_command_allocator = nullptr; + ID3D12GraphicsCommandList* dx_command_list = nullptr; + ID3D12PipelineState* dx_pipeline_state = nullptr; + + UINT dx_frame_index = 0; + ID3D12Fence* dx_fence = nullptr; + UINT64 dx_fence_value = 0; + HANDLE dx_fence_event = NULL; VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE;