DX12: First texture.

This commit is contained in:
Artem Kharytoniuk 2017-11-18 00:46:16 +01:00
parent a32a863d83
commit c70309c539
11 changed files with 673 additions and 399 deletions

497
src/engine/renderer/dx.cpp Normal file
View File

@ -0,0 +1,497 @@
#include "tr_local.h"
#include <functional>
#include "D3d12.h"
#include "D3d12SDKLayers.h"
#include "DXGI1_4.h"
#include "wrl.h"
#include "d3dx12.h"
#include "D3Dcompiler.h"
#include <DirectXMath.h>
// 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<IDXGIAdapter1> 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<void(ID3D12GraphicsCommandList*)> 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<ID3D12Debug> debug_controller;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) {
debug_controller->EnableDebugLayer();
}
}
#endif
ComPtr<IDXGIFactory4> factory;
DX_CHECK(CreateDXGIFactory1(IID_PPV_ARGS(&factory)));
ComPtr<IDXGIAdapter1> 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<IDXGISwapChain1> 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<ID3DBlob> signature;
ComPtr<ID3DBlob> 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<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> 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<void**>(&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<ID3D12Resource> 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<float>(glConfig.vidWidth), static_cast<float>(glConfig.vidHeight));
dx.command_list->RSSetViewports(1, &viewport);
CD3DX12_RECT scissorRect(0, 0, static_cast<LONG>(glConfig.vidWidth), static_cast<LONG>(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();
}

78
src/engine/renderer/dx.h Normal file
View File

@ -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 <DirectXMath.h>
#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<IDXGISwapChain3> 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];
};

View File

@ -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);
}

View File

@ -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);
}
}
}
}

View File

@ -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 );

View File

@ -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();

View File

@ -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

View File

@ -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<IDXGIAdapter1> 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<ID3D12Debug> debug_controller;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) {
debug_controller->EnableDebugLayer();
}
}
#endif
ComPtr<IDXGIFactory4> factory;
DX_CHECK(CreateDXGIFactory1(IID_PPV_ARGS(&factory)));
ComPtr<IDXGIAdapter1> 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<IDXGISwapChain1> 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<ID3DBlob> signature;
ComPtr<ID3DBlob> 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<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> 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<void**>(&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<float>(glConfig.vidWidth), static_cast<float>(glConfig.vidHeight));
vk.dx_command_list->RSSetViewports(1, &viewport);
CD3DX12_RECT scissorRect(0, 0, static_cast<LONG>(glConfig.vidWidth), static_cast<LONG>(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;

View File

@ -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 <DirectXMath.h>
#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<IDXGISwapChain3> 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;

View File

@ -93,6 +93,7 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\engine\renderer\dx.cpp" />
<ClCompile Include="..\src\engine\renderer\shaders\spirv\multi_texture_add_frag.cpp" />
<ClCompile Include="..\src\engine\renderer\shaders\spirv\multi_texture_clipping_plane_vert.cpp" />
<ClCompile Include="..\src\engine\renderer\shaders\spirv\multi_texture_mul_frag.cpp" />
@ -129,6 +130,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\engine\qcommon\cm_public.h" />
<ClInclude Include="..\src\engine\renderer\dx.h" />
<ClInclude Include="..\src\engine\renderer\vk.h" />
<ClInclude Include="..\src\game\q_shared.h" />
<ClInclude Include="..\src\engine\qcommon\qcommon.h" />

View File

@ -113,6 +113,9 @@
<ClCompile Include="..\src\engine\renderer\shaders\spirv\single_texture_clipping_plane_vert.cpp">
<Filter>Source Files\spirv</Filter>
</ClCompile>
<ClCompile Include="..\src\engine\renderer\dx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\engine\qcommon\cm_public.h">
@ -148,5 +151,8 @@
<ClInclude Include="..\src\engine\renderer\vk.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\engine\renderer\dx.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
</Project>