From c70309c5398c2a0b8fc457f1bf93906ae46cb396 Mon Sep 17 00:00:00 2001 From: Artem Kharytoniuk Date: Sat, 18 Nov 2017 00:46:16 +0100 Subject: [PATCH] DX12: First texture. --- src/engine/renderer/dx.cpp | 497 +++++++++++++++++++++++ src/engine/renderer/dx.h | 78 ++++ src/engine/renderer/shaders/shaders.hlsl | 22 +- src/engine/renderer/tr_backend.c | 13 + src/engine/renderer/tr_image.c | 58 +++ src/engine/renderer/tr_init.c | 5 + src/engine/renderer/tr_local.h | 7 + src/engine/renderer/vk.cpp | 337 --------------- src/engine/renderer/vk.h | 47 --- visual-studio/renderer.vcxproj | 2 + visual-studio/renderer.vcxproj.filters | 6 + 11 files changed, 673 insertions(+), 399 deletions(-) create mode 100644 src/engine/renderer/dx.cpp create mode 100644 src/engine/renderer/dx.h diff --git a/src/engine/renderer/dx.cpp b/src/engine/renderer/dx.cpp new file mode 100644 index 0000000..ec6660e --- /dev/null +++ b/src/engine/renderer/dx.cpp @@ -0,0 +1,497 @@ +#include "tr_local.h" + +#include + +#include "D3d12.h" +#include "D3d12SDKLayers.h" +#include "DXGI1_4.h" +#include "wrl.h" +#include "d3dx12.h" +#include "D3Dcompiler.h" +#include + +// Helper function for acquiring the first available hardware adapter that supports Direct3D 12. +// If no such adapter can be found, *ppAdapter will be set to nullptr. +static void get_hardware_adapter(IDXGIFactory4* p_factory, IDXGIAdapter1** pp_adapter) { + ComPtr adapter; + *pp_adapter = nullptr; + + for (UINT adapter_index = 0; DXGI_ERROR_NOT_FOUND != p_factory->EnumAdapters1(adapter_index, &adapter); ++adapter_index) { + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { + // Don't select the Basic Render Driver adapter. + // If you want a software adapter, pass in "/warp" on the command line. + continue; + } + + // Check to see if the adapter supports Direct3D 12, but don't create the + // actual device yet. + if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { + break; + } + } + *pp_adapter = adapter.Detach(); +} + +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 = dx.fence_value; + DX_CHECK(dx.command_queue->Signal(dx.fence, fence)); + dx.fence_value++; + + // Wait until the previous frame is finished. + if (dx.fence->GetCompletedValue() < fence) + { + DX_CHECK(dx.fence->SetEventOnCompletion(fence, dx.fence_event)); + WaitForSingleObject(dx.fence_event, INFINITE); + } + + dx.frame_index = dx.swapchain->GetCurrentBackBufferIndex(); +} + +static void wait_for_queue_idle(ID3D12CommandQueue* command_queue) { + const UINT64 fence = dx.fence_value; + DX_CHECK(command_queue->Signal(dx.fence, fence)); + dx.fence_value++; + + if (dx.fence->GetCompletedValue() < fence) { + DX_CHECK(dx.fence->SetEventOnCompletion(fence, dx.fence_event)); + WaitForSingleObject(dx.fence_event, INFINITE); + } +} + +static void record_and_run_commands(ID3D12CommandAllocator* command_allocator, ID3D12CommandQueue* command_queue, + std::function recorder) { + + ID3D12GraphicsCommandList* command_list; + DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, command_allocator, + nullptr, IID_PPV_ARGS(&command_list))); + + recorder(command_list); + DX_CHECK(command_list->Close()); + + ID3D12CommandList* command_lists[] = { command_list }; + command_queue->ExecuteCommandLists(1, command_lists); + wait_for_queue_idle(command_queue); + command_list->Release(); +} + +void dx_initialize() { +#if defined(_DEBUG) + // Enable the D3D12 debug layer + { + ComPtr debug_controller; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) { + debug_controller->EnableDebugLayer(); + } + } +#endif + + ComPtr factory; + DX_CHECK(CreateDXGIFactory1(IID_PPV_ARGS(&factory))); + + ComPtr hardware_adapter; + get_hardware_adapter(factory.Get(), &hardware_adapter); + DX_CHECK(D3D12CreateDevice(hardware_adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&dx.device))); + + // Describe and create the command queue. + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + DX_CHECK(dx.device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&dx.command_queue))); + + // Describe and create the swap chain. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.BufferCount = D3D_FRAME_COUNT; + swapChainDesc.Width = glConfig.vidWidth; + swapChainDesc.Height = glConfig.vidHeight; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.SampleDesc.Count = 1; + + ComPtr swapchain; + DX_CHECK(factory->CreateSwapChainForHwnd( + dx.command_queue, // Swap chain needs the queue so that it can force a flush on it. + g_wv.hWnd_dx, + &swapChainDesc, + nullptr, + nullptr, + &swapchain + )); + + // This sample does not support fullscreen transitions. + DX_CHECK(factory->MakeWindowAssociation(g_wv.hWnd_dx, DXGI_MWA_NO_ALT_ENTER)); + DX_CHECK(swapchain.As(&dx.swapchain)); + dx.frame_index = dx.swapchain->GetCurrentBackBufferIndex(); + + // Create descriptor heaps. + { + // Describe and create a render target view (RTV) descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = D3D_FRAME_COUNT; + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + DX_CHECK(dx.device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&dx.rtv_heap))); + + dx.rtv_descriptor_size = dx.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + D3D12_DESCRIPTOR_HEAP_DESC srv_heap_desc{}; + srv_heap_desc.NumDescriptors = MAX_DRAWIMAGES; + srv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + srv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + DX_CHECK(dx.device->CreateDescriptorHeap(&srv_heap_desc, IID_PPV_ARGS(&dx.srv_heap))); + + dx.srv_descriptor_size = dx.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + // Create frame resources. + { + CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(dx.rtv_heap->GetCPUDescriptorHandleForHeapStart()); + + // Create a RTV for each frame. + for (UINT n = 0; n < D3D_FRAME_COUNT; n++) + { + DX_CHECK(dx.swapchain->GetBuffer(n, IID_PPV_ARGS(&dx.render_targets[n]))); + dx.device->CreateRenderTargetView(dx.render_targets[n], nullptr, rtv_handle); + rtv_handle.Offset(1, dx.rtv_descriptor_size); + } + } + + DX_CHECK(dx.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&dx.command_allocator))); + + // Create the root signature. + { + CD3DX12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0); + + CD3DX12_ROOT_PARAMETER root_parameters[1]; + root_parameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL); + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + CD3DX12_ROOT_SIGNATURE_DESC root_signature_desc; + root_signature_desc.Init(_countof(root_parameters), root_parameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ComPtr signature; + ComPtr error; + DX_CHECK(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); + DX_CHECK(dx.device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&dx.root_signature))); + } + + // Create the pipeline state, which includes compiling and loading shaders. + { + ComPtr vertexShader; + ComPtr pixelShader; + +#if defined(_DEBUG) + // Enable better shader debugging with the graphics debugging tools. + UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#else + UINT compileFlags = 0; +#endif + + DX_CHECK(D3DCompileFromFile(L"d:/Quake-III-Arena-Kenny-Edition/src/engine/renderer/shaders/shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr)); + DX_CHECK(D3DCompileFromFile(L"d:/Quake-III-Arena-Kenny-Edition/src/engine/renderer/shaders/shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr)); + + // Define the vertex input layout. + D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } + }; + + // Describe and create the graphics pipeline state object (PSO). + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; + psoDesc.pRootSignature = dx.root_signature; + psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); + psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); + psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + psoDesc.DepthStencilState.DepthEnable = FALSE; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.SampleMask = UINT_MAX; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + psoDesc.SampleDesc.Count = 1; + DX_CHECK(dx.device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&dx.pipeline_state))); + } + + DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, dx.command_allocator, dx.pipeline_state, IID_PPV_ARGS(&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(dx.command_list->Close()); + + // Create the vertex buffer. + { + struct Vertex + { + XMFLOAT3 position; + XMFLOAT2 color; + }; + + float aspect_ratio = float(glConfig.vidWidth) / float(glConfig.vidHeight); + + // Define the geometry for a triangle. + Vertex triangleVertices[] = + { + { { 0.0f, 0.5f * aspect_ratio, 0.0f }, { 0.5f, 0.0f } }, + { { 0.5f, -0.5f * aspect_ratio, 0.0f }, { 1.0f, 1.0f } }, + { { -0.5f, -0.5f * aspect_ratio, 0.0f }, { 0.0f, 1.0f } } + }; + + const UINT vertexBufferSize = sizeof(triangleVertices); + + // Note: using upload heaps to transfer static data like vert buffers is not + // recommended. Every time the GPU needs it, the upload heap will be marshalled + // over. Please read up on Default Heap usage. An upload heap is used here for + // code simplicity and because there are very few verts to actually transfer. + DX_CHECK(dx.device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&dx.vertex_buffer))); + + // Copy the triangle data to the vertex buffer. + UINT8* pVertexDataBegin; + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + DX_CHECK(dx.vertex_buffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin))); + memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices)); + dx.vertex_buffer->Unmap(0, nullptr); + + // Initialize the vertex buffer view. + dx.vertex_buffer_view.BufferLocation = dx.vertex_buffer->GetGPUVirtualAddress(); + dx.vertex_buffer_view.StrideInBytes = sizeof(Vertex); + dx.vertex_buffer_view.SizeInBytes = vertexBufferSize; + } + + // Create synchronization objects. + { + DX_CHECK(dx.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&dx.fence))); + dx.fence_value = 1; + + // Create an event handle to use for frame synchronization. + dx.fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (dx.fence_event == NULL) + { + DX_CHECK(HRESULT_FROM_WIN32(GetLastError())); + } + + // Wait for the command list to execute; we are reusing the same command + // list in our main loop but for now, we just want to wait for setup to + // complete before continuing. + wait_for_previous_frame(); + } + + dx.active = true; +} + +void dx_shutdown() { + dx.swapchain.Reset(); + + dx.command_allocator->Release(); + dx.command_allocator = nullptr; + + for (int i = 0; i < D3D_FRAME_COUNT; i++) { + dx.render_targets[i]->Release(); + } + + dx.rtv_heap->Release(); + dx.rtv_heap = nullptr; + + dx.srv_heap->Release(); + dx.srv_heap = nullptr; + + dx.root_signature->Release(); + dx.root_signature = nullptr; + + dx.command_queue->Release(); + dx.command_queue = nullptr; + + dx.command_list->Release(); + dx.command_list = nullptr; + + dx.pipeline_state->Release(); + dx.pipeline_state = nullptr; + + dx.fence->Release(); + dx.fence = nullptr; + + ::CloseHandle(dx.fence_event); + dx.fence_event = NULL; + + dx.vertex_buffer->Release(); + dx.vertex_buffer = nullptr; + + dx.device->Release(); + dx.device = nullptr; +} + +void dx_release_resources() { + for (int i = 0; i < MAX_VK_IMAGES; i++) { + if (dx_world.images[i].texture != nullptr) { + dx_world.images[i].texture->Release(); + } + } + Com_Memset(&dx_world, 0, sizeof(dx_world)); +} + +Dx_Image dx_create_image(int width, int height, DXGI_FORMAT format, int mip_levels, bool repeat_texture, int image_index) { + Dx_Image image; + + // create texture + { + D3D12_RESOURCE_DESC desc; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Alignment = 0; + desc.Width = width; + desc.Height = height; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; // /* mip_levels */; !!!!!!!!!!!!!!!!!!!!!! + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + DX_CHECK(dx.device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), + D3D12_HEAP_FLAG_NONE, + &desc, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + nullptr, + IID_PPV_ARGS(&image.texture))); + } + + // create texture descriptor + { + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc{}; + srv_desc.Format = format; + srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srv_desc.Texture2D.MipLevels = 1; // !!!!!!!!!!!!!!!!!!!!!!1 + + D3D12_CPU_DESCRIPTOR_HANDLE handle; + handle.ptr = dx.srv_heap->GetCPUDescriptorHandleForHeapStart().ptr + image_index * dx.srv_descriptor_size; + dx.device->CreateShaderResourceView(image.texture, &srv_desc, handle); + } + + return image; +} + +void dx_upload_image_data(ID3D12Resource* texture, int width, int height, bool mipmap, const uint8_t* pixels, int bytes_per_pixel) { + const UINT64 upload_buffer_size = GetRequiredIntermediateSize(texture, 0, 1); + + ComPtr upload_texture; + DX_CHECK(dx.device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(upload_buffer_size), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&upload_texture))); + + D3D12_SUBRESOURCE_DATA texture_data {}; + texture_data.pData = pixels; + texture_data.RowPitch = width * bytes_per_pixel; + texture_data.SlicePitch = texture_data.RowPitch * height; + + record_and_run_commands(dx.command_allocator, dx.command_queue, + [&texture, &upload_texture, &texture_data](ID3D12GraphicsCommandList* command_list) + { + command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST)); + + UpdateSubresources(command_list, texture, upload_texture.Get(), 0, 0, 1, &texture_data); + + command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture, + D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); + }); +} + +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(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(dx.command_list->Reset(dx.command_allocator, dx.pipeline_state)); + + // Set necessary state. + dx.command_list->SetGraphicsRootSignature(dx.root_signature); + + CD3DX12_VIEWPORT viewport(0.0f, 0.0f, static_cast(glConfig.vidWidth), static_cast(glConfig.vidHeight)); + dx.command_list->RSSetViewports(1, &viewport); + + CD3DX12_RECT scissorRect(0, 0, static_cast(glConfig.vidWidth), static_cast(glConfig.vidHeight)); + dx.command_list->RSSetScissorRects(1, &scissorRect); + + ID3D12DescriptorHeap* heaps[] = { dx.srv_heap }; + dx.command_list->SetDescriptorHeaps(_countof(heaps), heaps); + + int image_index = 35 + (Com_Milliseconds() / 1000) % (tr.numImages - 40); + D3D12_GPU_DESCRIPTOR_HANDLE handle = dx.srv_heap->GetGPUDescriptorHandleForHeapStart(); + handle.ptr += image_index * dx.srv_descriptor_size; + dx.command_list->SetGraphicsRootDescriptorTable(0, handle); + + // Indicate that the back buffer will be used as a render target. + dx.command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(dx.render_targets[dx.frame_index], + D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); + + CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(dx.rtv_heap->GetCPUDescriptorHandleForHeapStart(), dx.frame_index, dx.rtv_descriptor_size); + dx.command_list->OMSetRenderTargets(1, &rtv_handle, FALSE, nullptr); + + // Record commands. + const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; + dx.command_list->ClearRenderTargetView(rtv_handle, clearColor, 0, nullptr); + dx.command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + dx.command_list->IASetVertexBuffers(0, 1, &dx.vertex_buffer_view); + dx.command_list->DrawInstanced(3, 1, 0, 0); + + // Indicate that the back buffer will now be used to present. + dx.command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(dx.render_targets[dx.frame_index], + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); + + DX_CHECK(dx.command_list->Close()); +} + +void dx_end_frame() { + // Execute the command list. + ID3D12CommandList* ppCommandLists[] = { dx.command_list }; + dx.command_queue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); + + // Present the frame. + DX_CHECK(dx.swapchain->Present(1, 0)); + + wait_for_previous_frame(); +} diff --git a/src/engine/renderer/dx.h b/src/engine/renderer/dx.h new file mode 100644 index 0000000..22e4d49 --- /dev/null +++ b/src/engine/renderer/dx.h @@ -0,0 +1,78 @@ +#pragma once + +#include "D3d12.h" +#include "D3d12SDKLayers.h" +#include "DXGI1_4.h" +#include "wrl.h" +#include "d3dx12.h" +#include "D3Dcompiler.h" +#include +#include "../../engine/platform/win_local.h" + +using Microsoft::WRL::ComPtr; +using namespace DirectX; + +const int D3D_FRAME_COUNT = 2; + +#define DX_CHECK(function_call) { \ + HRESULT hr = function_call; \ + if (FAILED(hr)) \ + ri.Error(ERR_FATAL, "Direct3D: error returned by %s", #function_call); \ +} + +struct Dx_Image { + ID3D12Resource* texture = nullptr; +}; + +// +// Initialization. +// +void dx_initialize(); +void dx_shutdown(); + +void dx_release_resources(); + +// +// Resources allocation. +// +Dx_Image dx_create_image(int width, int height, DXGI_FORMAT format, int mip_levels, bool repeat_texture, int image_index); +void dx_upload_image_data(ID3D12Resource* texture, int width, int height, bool mipmap, const uint8_t* pixels, int bytes_per_pixel); + +// +// Rendering setup. +// +void dx_begin_frame(); +void dx_end_frame(); + +struct Dx_Instance { + bool active = false; + + ID3D12Device* device = nullptr; + ID3D12CommandQueue* command_queue = nullptr; + ComPtr swapchain; + ID3D12DescriptorHeap* rtv_heap = nullptr; + UINT rtv_descriptor_size = 0; + ID3D12Resource* render_targets[D3D_FRAME_COUNT]; + ID3D12RootSignature* root_signature = nullptr; + ID3D12CommandAllocator* command_allocator = nullptr; + ID3D12GraphicsCommandList* command_list = nullptr; + ID3D12PipelineState* pipeline_state = nullptr; + + UINT frame_index = 0; + ID3D12Fence* fence = nullptr; + UINT64 fence_value = 0; + HANDLE fence_event = NULL; + + ID3D12Resource* vertex_buffer = nullptr; + D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view; + + ID3D12DescriptorHeap* srv_heap = nullptr; + UINT srv_descriptor_size = 0; +}; + +struct Dx_World { + // + // Resources. + // + Dx_Image images[MAX_VK_IMAGES]; +}; diff --git a/src/engine/renderer/shaders/shaders.hlsl b/src/engine/renderer/shaders/shaders.hlsl index b00be4e..2eb058f 100644 --- a/src/engine/renderer/shaders/shaders.hlsl +++ b/src/engine/renderer/shaders/shaders.hlsl @@ -1,31 +1,23 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - struct PSInput { float4 position : SV_POSITION; - float4 color : COLOR; + float2 uv : TEXCOORD; }; -PSInput VSMain(float4 position : POSITION, float4 color : COLOR) +Texture2D texture0 : register(t0); +SamplerState sampler0 : register(s0); + +PSInput VSMain(float4 position : POSITION, float2 uv : TEXCOORD) { PSInput result; result.position = position; - result.color = color; + result.uv = uv; return result; } float4 PSMain(PSInput input) : SV_TARGET { - return input.color; + return texture0.Sample(sampler0, input.uv); } diff --git a/src/engine/renderer/tr_backend.c b/src/engine/renderer/tr_backend.c index 6bb12e2..dfd67d6 100644 --- a/src/engine/renderer/tr_backend.c +++ b/src/engine/renderer/tr_backend.c @@ -719,6 +719,14 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int image = vk_create_image(cols, rows, VK_FORMAT_R8G8B8A8_UNORM, 1, false); vk_upload_image_data(image.handle, cols, rows, false, data, 4); } + // D3D + if (dx.active) { + int image_index = tr.scratchImage[client]->index; + Dx_Image& image = dx_world.images[image_index]; + image.texture->Release(); + image = dx_create_image(cols, rows, DXGI_FORMAT_R8G8B8A8_UNORM, 1, false, image_index); + dx_upload_image_data(image.texture, cols, rows, false, data, 4); + } } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing @@ -730,6 +738,11 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int const Vk_Image& image = vk_world.images[tr.scratchImage[client]->index]; vk_upload_image_data(image.handle, cols, rows, false, data, 4); } + // D3D + if (dx.active) { + const Dx_Image& image = dx_world.images[tr.scratchImage[client]->index]; + dx_upload_image_data(image.texture, cols, rows, false, data, 4); + } } } } diff --git a/src/engine/renderer/tr_image.c b/src/engine/renderer/tr_image.c index ebdee44..65b717a 100644 --- a/src/engine/renderer/tr_image.c +++ b/src/engine/renderer/tr_image.c @@ -686,6 +686,60 @@ static int upload_gl_image(const Image_Upload_Data& upload_data, int texture_add return internal_format; } +// D3D +static Dx_Image upload_dx_image(const Image_Upload_Data& upload_data, bool repeat_texture, int image_index) { + int w = upload_data.base_level_width; + int h = upload_data.base_level_height; + + bool has_alpha = false; + for (int i = 0; i < w * h; i++) { + if (upload_data.buffer[i*4 + 3] != 255) { + has_alpha = true; + break; + } + } + + DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; + if (r_texturebits->integer <= 16) { + format = has_alpha ? DXGI_FORMAT_B4G4R4A4_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM; + } + + int bytes_per_pixel = 4; + + if (format == DXGI_FORMAT_B5G5R5A1_UNORM) { + bytes_per_pixel = 2; + auto p = (uint16_t*)upload_data.buffer; + for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { + byte r = upload_data.buffer[i+0]; + byte g = upload_data.buffer[i+1]; + byte b = upload_data.buffer[i+2]; + + *p = uint32_t((b/255.0) * 31.0 + 0.5) | + (uint32_t((g/255.0) * 31.0 + 0.5) << 5) | + (uint32_t((r/255.0) * 31.0 + 0.5) << 10) | + (1 << 15); + } + } else if (format == DXGI_FORMAT_B4G4R4A4_UNORM) { + bytes_per_pixel = 2; + auto p = (uint16_t*)upload_data.buffer; + for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { + byte r = upload_data.buffer[i+0]; + byte g = upload_data.buffer[i+1]; + byte b = upload_data.buffer[i+2]; + byte a = upload_data.buffer[i+3]; + + *p = uint32_t((a/255.0) * 15.0 + 0.5) | + (uint32_t((r/255.0) * 15.0 + 0.5) << 4) | + (uint32_t((g/255.0) * 15.0 + 0.5) << 8) | + (uint32_t((b/255.0) * 15.0 + 0.5) << 12); + } + } + + Dx_Image image = dx_create_image(w, h, format, upload_data.mip_levels, repeat_texture, image_index); + dx_upload_image_data(image.texture, w, h, upload_data.mip_levels > 1, upload_data.buffer, bytes_per_pixel); + return image; +} + // VULKAN static Vk_Image upload_vk_image(const Image_Upload_Data& upload_data, bool repeat_texture) { int w = upload_data.base_level_width; @@ -788,6 +842,10 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height if (vk.active) { vk_world.images[image->index] = upload_vk_image(upload_data, glWrapClampMode == GL_REPEAT); } + // D3D + if (dx.active) { + dx_world.images[image->index] = upload_dx_image(upload_data, glWrapClampMode == GL_REPEAT, image->index); + } if (isLightmap) { GL_SelectTexture( 0 ); diff --git a/src/engine/renderer/tr_init.c b/src/engine/renderer/tr_init.c index 50efa8f..5c59ffb 100644 --- a/src/engine/renderer/tr_init.c +++ b/src/engine/renderer/tr_init.c @@ -31,6 +31,10 @@ glstate_t glState; Vk_Instance vk; Vk_World vk_world; +// D3D +Dx_Instance dx; +Dx_World dx_world; + static void GfxInfo_f( void ); cvar_t *r_renderAPI; @@ -1116,6 +1120,7 @@ void RE_Shutdown( qboolean destroyWindow ) { // VULKAN if (vk.active) { + dx_release_resources(); vk_release_resources(); if (destroyWindow) { dx_shutdown(); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 1f61f6c..bc35160 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -34,6 +34,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // VULKAN #include "vk.h" +// D3D +#include "dx.h" + #define GL_INDEX_TYPE GL_UNSIGNED_INT typedef unsigned int glIndex_t; @@ -932,6 +935,10 @@ extern glstate_t glState; // outside of TR since it shouldn't be cleared during extern Vk_Instance vk; // shouldn't be cleared during ref re-init extern Vk_World vk_world; // this data is cleared during ref re-init +// D3D +extern Dx_Instance dx; // shouldn't be cleared during ref re-init +extern Dx_World dx_world; // this data is cleared during ref re-init + // // cvars diff --git a/src/engine/renderer/vk.cpp b/src/engine/renderer/vk.cpp index db0080e..b71e020 100644 --- a/src/engine/renderer/vk.cpp +++ b/src/engine/renderer/vk.cpp @@ -797,269 +797,6 @@ static void deinit_vulkan_library() { VkPipeline create_pipeline(const Vk_Pipeline_Def&); -void wait_for_previous_frame(); - -// Helper function for acquiring the first available hardware adapter that supports Direct3D 12. -// If no such adapter can be found, *ppAdapter will be set to nullptr. -void get_hardware_adapter(IDXGIFactory4* p_factory, IDXGIAdapter1** pp_adapter) { - ComPtr adapter; - *pp_adapter = nullptr; - - for (UINT adapter_index = 0; DXGI_ERROR_NOT_FOUND != p_factory->EnumAdapters1(adapter_index, &adapter); ++adapter_index) { - DXGI_ADAPTER_DESC1 desc; - adapter->GetDesc1(&desc); - - if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { - // Don't select the Basic Render Driver adapter. - // If you want a software adapter, pass in "/warp" on the command line. - continue; - } - - // Check to see if the adapter supports Direct3D 12, but don't create the - // actual device yet. - if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { - break; - } - } - *pp_adapter = adapter.Detach(); -} - -void dx_initialize() { -#if defined(_DEBUG) - // Enable the D3D12 debug layer - { - ComPtr debug_controller; - if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) { - debug_controller->EnableDebugLayer(); - } - } -#endif - - ComPtr factory; - DX_CHECK(CreateDXGIFactory1(IID_PPV_ARGS(&factory))); - - ComPtr hardware_adapter; - get_hardware_adapter(factory.Get(), &hardware_adapter); - DX_CHECK(D3D12CreateDevice(hardware_adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&vk.dx_device))); - - // Describe and create the command queue. - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - - DX_CHECK(vk.dx_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&vk.dx_command_queue))); - - // Describe and create the swap chain. - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; - swapChainDesc.BufferCount = D3D_FRAME_COUNT; - swapChainDesc.Width = glConfig.vidWidth; - swapChainDesc.Height = glConfig.vidHeight; - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swapChainDesc.SampleDesc.Count = 1; - - ComPtr swapchain; - DX_CHECK(factory->CreateSwapChainForHwnd( - vk.dx_command_queue, // Swap chain needs the queue so that it can force a flush on it. - g_wv.hWnd_dx, - &swapChainDesc, - nullptr, - nullptr, - &swapchain - )); - - // 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. - { - // Describe and create a render target view (RTV) descriptor heap. - D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; - rtvHeapDesc.NumDescriptors = D3D_FRAME_COUNT; - rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - DX_CHECK(vk.dx_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&vk.dx_rtv_heap))); - - vk.dx_rtv_descriptor_size = vk.dx_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - } - - // Create frame resources. - { - CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(vk.dx_rtv_heap->GetCPUDescriptorHandleForHeapStart()); - - // 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.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))); - - // 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(&vk.dx_root_signature))); - } - - // Create the pipeline state, which includes compiling and loading shaders. - { - ComPtr vertexShader; - ComPtr pixelShader; - -#if defined(_DEBUG) - // Enable better shader debugging with the graphics debugging tools. - UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; -#else - UINT compileFlags = 0; -#endif - - DX_CHECK(D3DCompileFromFile(L"../src/engine/renderer/shaders/shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr)); - DX_CHECK(D3DCompileFromFile(L"../src/engine/renderer/shaders/shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr)); - - // Define the vertex input layout. - D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } - }; - - // Describe and create the graphics pipeline state object (PSO). - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; - psoDesc.pRootSignature = vk.dx_root_signature; - psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); - psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); - psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); - psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); - psoDesc.DepthStencilState.DepthEnable = FALSE; - psoDesc.DepthStencilState.StencilEnable = FALSE; - psoDesc.SampleMask = UINT_MAX; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; - psoDesc.SampleDesc.Count = 1; - DX_CHECK(vk.dx_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&vk.dx_pipeline_state))); - } - - DX_CHECK(vk.dx_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vk.dx_command_allocator, vk.dx_pipeline_state, 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 the vertex buffer. - { - struct Vertex - { - XMFLOAT3 position; - XMFLOAT4 color; - }; - - float aspect_ratio = float(glConfig.vidWidth) / float(glConfig.vidHeight); - - // Define the geometry for a triangle. - Vertex triangleVertices[] = - { - { { 0.0f, 0.25f * aspect_ratio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, - { { 0.25f, -0.25f * aspect_ratio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, - { { -0.25f, -0.25f * aspect_ratio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } - }; - - const UINT vertexBufferSize = sizeof(triangleVertices); - - // Note: using upload heaps to transfer static data like vert buffers is not - // recommended. Every time the GPU needs it, the upload heap will be marshalled - // over. Please read up on Default Heap usage. An upload heap is used here for - // code simplicity and because there are very few verts to actually transfer. - DX_CHECK(vk.dx_device->CreateCommittedResource( - &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), - D3D12_HEAP_FLAG_NONE, - &CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize), - D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, - IID_PPV_ARGS(&vk.dx_vertex_buffer))); - - // Copy the triangle data to the vertex buffer. - UINT8* pVertexDataBegin; - CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. - DX_CHECK(vk.dx_vertex_buffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin))); - memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices)); - vk.dx_vertex_buffer->Unmap(0, nullptr); - - // Initialize the vertex buffer view. - vk.dx_vertex_buffer_view.BufferLocation = vk.dx_vertex_buffer->GetGPUVirtualAddress(); - vk.dx_vertex_buffer_view.StrideInBytes = sizeof(Vertex); - vk.dx_vertex_buffer_view.SizeInBytes = vertexBufferSize; - } - - // 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())); - } - - // Wait for the command list to execute; we are reusing the same command - // list in our main loop but for now, we just want to wait for setup to - // complete before continuing. - wait_for_previous_frame(); - } -} - -void dx_shutdown() { - vk.dx_swapchain.Reset(); - - vk.dx_command_allocator->Release(); - vk.dx_command_allocator = nullptr; - - for (int i = 0; i < D3D_FRAME_COUNT; i++) { - vk.dx_render_targets[i]->Release(); - } - - vk.dx_rtv_heap->Release(); - vk.dx_rtv_heap = nullptr; - - vk.dx_root_signature->Release(); - vk.dx_root_signature = nullptr; - - vk.dx_command_queue->Release(); - vk.dx_command_queue = nullptr; - - vk.dx_command_list->Release(); - vk.dx_command_list = nullptr; - - vk.dx_pipeline_state->Release(); - vk.dx_pipeline_state = nullptr; - - vk.dx_fence->Release(); - vk.dx_fence = nullptr; - - ::CloseHandle(vk.dx_fence_event); - vk.dx_fence_event = NULL; - - vk.dx_vertex_buffer->Release(); - vk.dx_vertex_buffer = nullptr; - - vk.dx_device->Release(); - vk.dx_device = nullptr; -} - void vk_initialize() { init_vulkan_library(); @@ -2552,80 +2289,6 @@ 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)); - - // Set necessary state. - vk.dx_command_list->SetGraphicsRootSignature(vk.dx_root_signature); - - CD3DX12_VIEWPORT viewport(0.0f, 0.0f, static_cast(glConfig.vidWidth), static_cast(glConfig.vidHeight)); - vk.dx_command_list->RSSetViewports(1, &viewport); - - CD3DX12_RECT scissorRect(0, 0, static_cast(glConfig.vidWidth), static_cast(glConfig.vidHeight)); - vk.dx_command_list->RSSetScissorRects(1, &scissorRect); - - // 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); - vk.dx_command_list->OMSetRenderTargets(1, &rtv_handle, FALSE, nullptr); - - // Record commands. - const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; - vk.dx_command_list->ClearRenderTargetView(rtv_handle, clearColor, 0, nullptr); - vk.dx_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - vk.dx_command_list->IASetVertexBuffers(0, 1, &vk.dx_vertex_buffer_view); - vk.dx_command_list->DrawInstanced(3, 1, 0, 0); - - // 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 3bb024b..ebc2d13 100644 --- a/src/engine/renderer/vk.h +++ b/src/engine/renderer/vk.h @@ -8,18 +8,6 @@ #define VK_NO_PROTOTYPES #include "vulkan/vulkan.h" -#include "D3d12.h" -#include "D3d12SDKLayers.h" -#include "DXGI1_4.h" -#include "wrl.h" -#include "d3dx12.h" -#include "D3Dcompiler.h" -#include -#include "../../engine/platform/win_local.h" - -using Microsoft::WRL::ComPtr; -using namespace DirectX; - const int MAX_SWAPCHAIN_IMAGES = 8; const int MAX_VK_SAMPLERS = 32; const int MAX_VK_PIPELINES = 1024; @@ -28,20 +16,12 @@ const int MAX_VK_IMAGES = 2048; // should be the same as MAX_DRAWIMAGES const int IMAGE_CHUNK_SIZE = 32 * 1024 * 1024; const int MAX_IMAGE_CHUNKS = 16; -const int D3D_FRAME_COUNT = 2; - #define VK_CHECK(function_call) { \ VkResult result = function_call; \ if (result < 0) \ ri.Error(ERR_FATAL, "Vulkan: error code %d returned by %s", result, #function_call); \ } -#define DX_CHECK(function_call) { \ - HRESULT hr = function_call; \ - if (FAILED(hr)) \ - ri.Error(ERR_FATAL, "Direct3D: error returned by %s", #function_call); \ -} - enum class Vk_Shader_Type { single_texture, multi_texture_mul, @@ -96,13 +76,9 @@ struct Vk_Image { // After calling this function we get fully functional vulkan subsystem. void vk_initialize(); -void dx_initialize(); - // Shutdown vulkan subsystem by releasing resources acquired by Vk_Instance. void vk_shutdown(); -void dx_shutdown(); - // Releases vulkan resources allocated during program execution. // This effectively puts vulkan subsystem into initial state (the state we have after vk_initialize call). void vk_release_resources(); @@ -125,35 +101,12 @@ 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. // This structure is initialized/deinitialized by vk_initialize/vk_shutdown functions correspondingly. struct Vk_Instance { bool active = false; - - ID3D12Device* dx_device = nullptr; - ID3D12CommandQueue* dx_command_queue = nullptr; - ComPtr dx_swapchain; - ID3D12DescriptorHeap* dx_rtv_heap = nullptr; - UINT dx_rtv_descriptor_size = 0; - ID3D12Resource* dx_render_targets[D3D_FRAME_COUNT]; - ID3D12RootSignature* dx_root_signature = nullptr; - 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; - - ID3D12Resource* dx_vertex_buffer = nullptr; - D3D12_VERTEX_BUFFER_VIEW dx_vertex_buffer_view; - VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE; diff --git a/visual-studio/renderer.vcxproj b/visual-studio/renderer.vcxproj index 20ca452..d827183 100644 --- a/visual-studio/renderer.vcxproj +++ b/visual-studio/renderer.vcxproj @@ -93,6 +93,7 @@ + @@ -129,6 +130,7 @@ + diff --git a/visual-studio/renderer.vcxproj.filters b/visual-studio/renderer.vcxproj.filters index b253c68..46c90f6 100644 --- a/visual-studio/renderer.vcxproj.filters +++ b/visual-studio/renderer.vcxproj.filters @@ -113,6 +113,9 @@ Source Files\spirv + + Source Files + @@ -148,5 +151,8 @@ Source Files + + Source Files + \ No newline at end of file