DX12: Initial setup that can render some broken geometry.

This commit is contained in:
Artem Kharytoniuk 2017-12-05 22:09:14 +01:00
parent c70309c539
commit b835efea2b
7 changed files with 397 additions and 70 deletions

View File

@ -10,6 +10,21 @@
#include "D3Dcompiler.h"
#include <DirectXMath.h>
const int VERTEX_CHUNK_SIZE = 512 * 1024;
const int XYZ_SIZE = 4 * VERTEX_CHUNK_SIZE;
const int COLOR_SIZE = 1 * VERTEX_CHUNK_SIZE;
const int ST0_SIZE = 2 * VERTEX_CHUNK_SIZE;
const int ST1_SIZE = 2 * VERTEX_CHUNK_SIZE;
const int XYZ_OFFSET = 0;
const int COLOR_OFFSET = XYZ_OFFSET + XYZ_SIZE;
const int ST0_OFFSET = COLOR_OFFSET + COLOR_SIZE;
const int ST1_OFFSET = ST0_OFFSET + ST0_SIZE;
static const int VERTEX_BUFFER_SIZE = XYZ_SIZE + COLOR_SIZE + ST0_SIZE + ST1_SIZE;
static const int INDEX_BUFFER_SIZE = 2 * 1024 * 1024;
// 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) {
@ -68,11 +83,11 @@ static void wait_for_queue_idle(ID3D12CommandQueue* command_queue) {
}
}
static void record_and_run_commands(ID3D12CommandAllocator* command_allocator, ID3D12CommandQueue* command_queue,
std::function<void(ID3D12GraphicsCommandList*)> recorder) {
static void record_and_run_commands(ID3D12CommandQueue* command_queue, std::function<void(ID3D12GraphicsCommandList*)> recorder) {
dx.helper_command_allocator->Reset();
ID3D12GraphicsCommandList* command_list;
DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, command_allocator,
DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, dx.helper_command_allocator,
nullptr, IID_PPV_ARGS(&command_list)));
recorder(command_list);
@ -168,14 +183,20 @@ void dx_initialize() {
}
DX_CHECK(dx.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&dx.command_allocator)));
DX_CHECK(dx.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&dx.helper_command_allocator)));
//
// Create the root signature.
//
{
CD3DX12_DESCRIPTOR_RANGE ranges[1];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0);
CD3DX12_DESCRIPTOR_RANGE ranges[2];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1);
CD3DX12_ROOT_PARAMETER root_parameters[1];
CD3DX12_ROOT_PARAMETER root_parameters[3];
root_parameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
root_parameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL);
root_parameters[2].InitAsConstants(32, 0);
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
@ -219,8 +240,8 @@ void dx_initialize() {
// 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 }
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 2, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
// Describe and create the graphics pipeline state object (PSO).
@ -230,6 +251,7 @@ void dx_initialize() {
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;
@ -247,51 +269,6 @@ void dx_initialize() {
// 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)));
@ -310,6 +287,30 @@ void dx_initialize() {
wait_for_previous_frame();
}
//
// Geometry buffers.
//
{
// store geometry in upload heap since Q3 regenerates it every frame
DX_CHECK(dx.device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(VERTEX_BUFFER_SIZE + INDEX_BUFFER_SIZE),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&dx.geometry_buffer)));
void* p_data;
CD3DX12_RANGE read_range(0, 0);
DX_CHECK(dx.geometry_buffer->Map(0, &read_range, &p_data));
dx.vertex_buffer_ptr = static_cast<byte*>(p_data);
assert(((size_t)dx.vertex_buffer_ptr & 0xffff) == 0);
dx.index_buffer_ptr = static_cast<byte*>(p_data) + VERTEX_BUFFER_SIZE;
assert(((size_t)dx.index_buffer_ptr & 0xffff) == 0);
}
dx.active = true;
}
@ -319,6 +320,9 @@ void dx_shutdown() {
dx.command_allocator->Release();
dx.command_allocator = nullptr;
dx.helper_command_allocator->Release();
dx.helper_command_allocator = nullptr;
for (int i = 0; i < D3D_FRAME_COUNT; i++) {
dx.render_targets[i]->Release();
}
@ -347,8 +351,8 @@ void dx_shutdown() {
::CloseHandle(dx.fence_event);
dx.fence_event = NULL;
dx.vertex_buffer->Release();
dx.vertex_buffer = nullptr;
dx.geometry_buffer->Release();
dx.geometry_buffer = nullptr;
dx.device->Release();
dx.device = nullptr;
@ -361,6 +365,11 @@ void dx_release_resources() {
}
}
Com_Memset(&dx_world, 0, sizeof(dx_world));
// Reset geometry buffer's current offsets.
dx.xyz_elements = 0;
dx.color_st_elements = 0;
dx.index_buffer_offset = 0;
}
Dx_Image dx_create_image(int width, int height, DXGI_FORMAT format, int mip_levels, bool repeat_texture, int image_index) {
@ -401,6 +410,8 @@ Dx_Image dx_create_image(int width, int height, DXGI_FORMAT format, int mip_leve
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);
dx_world.current_image_indices[glState.currenttmu] = image_index;
}
return image;
@ -423,8 +434,7 @@ void dx_upload_image_data(ID3D12Resource* texture, int width, int height, bool m
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)
record_and_run_commands(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));
@ -436,6 +446,259 @@ void dx_upload_image_data(ID3D12Resource* texture, int width, int height, bool m
});
}
ID3D12PipelineState* dx_find_pipeline(const Vk_Pipeline_Def& def) {
return dx.pipeline_state;
}
static void get_mvp_transform(float* mvp) {
if (backEnd.projection2D) {
float mvp0 = 2.0f / glConfig.vidWidth;
float mvp5 = 2.0f / glConfig.vidHeight;
mvp[0] = mvp0; mvp[1] = 0.0f; mvp[2] = 0.0f; mvp[3] = 0.0f;
mvp[4] = 0.0f; mvp[5] = -mvp5; mvp[6] = 0.0f; mvp[7] = 0.0f;
mvp[8] = 0.0f; mvp[9] = 0.0f; mvp[10] = 1.0f; mvp[11] = 0.0f;
mvp[12] = -1.0f; mvp[13] = 1.0f; mvp[14] = 0.0f; mvp[15] = 1.0f;
} else {
const float* p = backEnd.viewParms.projectionMatrix;
// update q3's proj matrix (opengl) to vulkan conventions: z - [0, 1] instead of [-1, 1] and invert y direction
float zNear = r_znear->value;
float zFar = backEnd.viewParms.zFar;
float P10 = -zFar / (zFar - zNear);
float P14 = -zFar*zNear / (zFar - zNear);
float P5 = -p[5];
float proj[16] = {
p[0], p[1], p[2], p[3],
p[4], P5, p[6], p[7],
p[8], p[9], P10, p[11],
p[12], p[13], P14, p[15]
};
myGlMultMatrix(vk_world.modelview_transform, proj, mvp);
}
}
void dx_bind_geometry() {
if (!dx.active)
return;
// xyz stream
{
if ((dx.xyz_elements + tess.numVertexes) * sizeof(vec4_t) > XYZ_SIZE)
ri.Error(ERR_DROP, "dx_bind_geometry: vertex buffer overflow (xyz)\n");
byte* dst = dx.vertex_buffer_ptr + XYZ_OFFSET + dx.xyz_elements * sizeof(vec4_t);
Com_Memcpy(dst, tess.xyz, tess.numVertexes * sizeof(vec4_t));
uint32_t xyz_offset = XYZ_OFFSET + dx.xyz_elements * sizeof(vec4_t);
D3D12_VERTEX_BUFFER_VIEW xyz_view;
xyz_view.BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + xyz_offset;
xyz_view.SizeInBytes = static_cast<UINT>(tess.numVertexes * sizeof(vec4_t));
xyz_view.StrideInBytes = static_cast<UINT>(sizeof(vec4_t));
dx.command_list->IASetVertexBuffers(0, 1, &xyz_view);
dx.xyz_elements += tess.numVertexes;
}
// indexes stream
{
std::size_t indexes_size = tess.numIndexes * sizeof(uint32_t);
if (dx.index_buffer_offset + indexes_size > INDEX_BUFFER_SIZE)
ri.Error(ERR_DROP, "dx_bind_geometry: index buffer overflow\n");
byte* dst = dx.index_buffer_ptr + dx.index_buffer_offset;
Com_Memcpy(dst, tess.indexes, indexes_size);
D3D12_INDEX_BUFFER_VIEW index_view;
index_view.BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + VERTEX_BUFFER_SIZE + dx.index_buffer_offset;
index_view.SizeInBytes = static_cast<UINT>(indexes_size);
index_view.Format = DXGI_FORMAT_R32_UINT;
dx.command_list->IASetIndexBuffer(&index_view);
dx.index_buffer_offset += static_cast<int>(indexes_size);
}
//
// Specify push constants.
//
float root_constants[16 + 12 + 4]; // mvp transform + eye transform + clipping plane in eye space
get_mvp_transform(root_constants);
int root_constant_count = 16;
if (backEnd.viewParms.isPortal) {
// Eye space transform.
// NOTE: backEnd.or.modelMatrix incorporates s_flipMatrix, so it should be taken into account
// when computing clipping plane too.
float* eye_xform = root_constants + 16;
for (int i = 0; i < 12; i++) {
eye_xform[i] = backEnd.or.modelMatrix[(i%4)*4 + i/4 ];
}
// Clipping plane in eye coordinates.
float world_plane[4];
world_plane[0] = backEnd.viewParms.portalPlane.normal[0];
world_plane[1] = backEnd.viewParms.portalPlane.normal[1];
world_plane[2] = backEnd.viewParms.portalPlane.normal[2];
world_plane[3] = backEnd.viewParms.portalPlane.dist;
float eye_plane[4];
eye_plane[0] = DotProduct (backEnd.viewParms.or.axis[0], world_plane);
eye_plane[1] = DotProduct (backEnd.viewParms.or.axis[1], world_plane);
eye_plane[2] = DotProduct (backEnd.viewParms.or.axis[2], world_plane);
eye_plane[3] = DotProduct (world_plane, backEnd.viewParms.or.origin) - world_plane[3];
// Apply s_flipMatrix to be in the same coordinate system as eye_xfrom.
root_constants[28] = -eye_plane[1];
root_constants[29] = eye_plane[2];
root_constants[30] = -eye_plane[0];
root_constants[31] = eye_plane[3];
root_constant_count += 16;
}
dx.command_list->SetGraphicsRoot32BitConstants(2, root_constant_count, root_constants, 0);
}
static D3D12_RECT get_viewport_rect() {
D3D12_RECT r;
if (backEnd.projection2D) {
r.left = 0.0f;
r.top = 0.0f;
r.right = glConfig.vidWidth;
r.bottom = glConfig.vidHeight;
} else {
r.left = backEnd.viewParms.viewportX;
r.top = glConfig.vidHeight - (backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight);
r.right = r.left + backEnd.viewParms.viewportWidth;
r.bottom = r.top + backEnd.viewParms.viewportHeight;
}
return r;
}
static D3D12_VIEWPORT get_viewport(Vk_Depth_Range depth_range) {
D3D12_RECT r = get_viewport_rect();
D3D12_VIEWPORT viewport;
viewport.TopLeftX = (float)r.left;
viewport.TopLeftY = (float)r.top;
viewport.Width = (float)(r.right - r.left);
viewport.Height = (float)(r.bottom - r.top);
if (depth_range == Vk_Depth_Range::force_zero) {
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 0.0f;
} else if (depth_range == Vk_Depth_Range::force_one) {
viewport.MinDepth = 1.0f;
viewport.MaxDepth = 1.0f;
} else if (depth_range == Vk_Depth_Range::weapon) {
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 0.3f;
} else {
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
}
return viewport;
}
static D3D12_RECT get_scissor_rect() {
D3D12_RECT r = get_viewport_rect();
if (r.left < 0)
r.left = 0;
if (r.top < 0)
r.top = 0;
if (r.right > glConfig.vidWidth)
r.right = glConfig.vidWidth;
if (r.bottom > glConfig.vidHeight)
r.bottom = glConfig.vidHeight;
return r;
}
void dx_shade_geometry(ID3D12PipelineState* pipeline_state, bool multitexture, Vk_Depth_Range depth_range, bool indexed) {
// color
{
if ((dx.color_st_elements + tess.numVertexes) * sizeof(color4ub_t) > COLOR_SIZE)
ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (color)\n");
byte* dst = dx.vertex_buffer_ptr + COLOR_OFFSET + dx.color_st_elements * sizeof(color4ub_t);
Com_Memcpy(dst, tess.svars.colors, tess.numVertexes * sizeof(color4ub_t));
}
// st0
{
if ((dx.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST0_SIZE)
ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st0)\n");
byte* dst = dx.vertex_buffer_ptr + ST0_OFFSET + dx.color_st_elements * sizeof(vec2_t);
Com_Memcpy(dst, tess.svars.texcoords[0], tess.numVertexes * sizeof(vec2_t));
}
// st1
if (multitexture) {
if ((dx.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST1_SIZE)
ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st1)\n");
byte* dst = dx.vertex_buffer_ptr + ST1_OFFSET + dx.color_st_elements * sizeof(vec2_t);
Com_Memcpy(dst, tess.svars.texcoords[1], tess.numVertexes * sizeof(vec2_t));
}
// configure vertex data stream
D3D12_VERTEX_BUFFER_VIEW color_st_views[3];
color_st_views[0].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + COLOR_OFFSET + dx.color_st_elements * sizeof(color4ub_t);
color_st_views[0].SizeInBytes = static_cast<UINT>(tess.numVertexes * sizeof(color4ub_t));
color_st_views[0].StrideInBytes = static_cast<UINT>(sizeof(color4ub_t));
color_st_views[1].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + ST0_OFFSET + dx.color_st_elements * sizeof(vec2_t);
color_st_views[1].SizeInBytes = static_cast<UINT>(tess.numVertexes * sizeof(vec2_t));
color_st_views[1].StrideInBytes = static_cast<UINT>(sizeof(vec2_t));
color_st_views[2].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + ST1_OFFSET + dx.color_st_elements * sizeof(vec2_t);
color_st_views[2].SizeInBytes = static_cast<UINT>(tess.numVertexes * sizeof(vec2_t));
color_st_views[2].StrideInBytes = static_cast<UINT>(sizeof(vec2_t));
dx.command_list->IASetVertexBuffers(1, multitexture ? 3 : 2, color_st_views);
dx.color_st_elements += tess.numVertexes;
// bind descriptor sets
D3D12_GPU_DESCRIPTOR_HANDLE handle = dx.srv_heap->GetGPUDescriptorHandleForHeapStart();
handle.ptr += dx.srv_descriptor_size * dx_world.current_image_indices[0];
dx.command_list->SetGraphicsRootDescriptorTable(0, handle);
if (multitexture) {
handle = dx.srv_heap->GetGPUDescriptorHandleForHeapStart();
handle.ptr += dx.srv_descriptor_size * dx_world.current_image_indices[1];
dx.command_list->SetGraphicsRootDescriptorTable(1, handle);
}
// bind pipeline
dx.command_list->SetPipelineState(pipeline_state);
// configure pipeline's dynamic state
D3D12_RECT scissor_rect = get_scissor_rect();
dx.command_list->RSSetScissorRects(1, &scissor_rect);
D3D12_VIEWPORT viewport = get_viewport(depth_range);
dx.command_list->RSSetViewports(1, &viewport);
/*if (tess.shader->polygonOffset) {
vkCmdSetDepthBias(vk.command_buffer, r_offsetUnits->value, 0.0f, r_offsetFactor->value);
}*/
// issue draw call
if (indexed)
dx.command_list->DrawIndexedInstanced(tess.numIndexes, 1, 0, 0, 0);
else
dx.command_list->DrawInstanced(tess.numVertexes, 1, 0, 0);
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
@ -475,17 +738,18 @@ void dx_begin_frame() {
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.xyz_elements = 0;
dx.color_st_elements = 0;
dx.index_buffer_offset = 0;
}
void dx_end_frame() {
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);

View File

@ -37,10 +37,13 @@ void dx_release_resources();
//
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);
ID3D12PipelineState* dx_find_pipeline(const Vk_Pipeline_Def& def);
//
// Rendering setup.
//
void dx_bind_geometry();
void dx_shade_geometry(ID3D12PipelineState* pipeline_state, bool multitexture, Vk_Depth_Range depth_range, bool indexed = true);
void dx_begin_frame();
void dx_end_frame();
@ -55,6 +58,7 @@ struct Dx_Instance {
ID3D12Resource* render_targets[D3D_FRAME_COUNT];
ID3D12RootSignature* root_signature = nullptr;
ID3D12CommandAllocator* command_allocator = nullptr;
ID3D12CommandAllocator* helper_command_allocator = nullptr;
ID3D12GraphicsCommandList* command_list = nullptr;
ID3D12PipelineState* pipeline_state = nullptr;
@ -63,11 +67,17 @@ struct Dx_Instance {
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;
byte* vertex_buffer_ptr = nullptr; // pointer to mapped vertex buffer
int xyz_elements = 0;
int color_st_elements = 0;
byte* index_buffer_ptr = nullptr; // pointer to mapped index buffer
int index_buffer_offset = 0;
ID3D12Resource* geometry_buffer = nullptr;
};
struct Dx_World {
@ -75,4 +85,9 @@ struct Dx_World {
// Resources.
//
Dx_Image images[MAX_VK_IMAGES];
//
// State.
//
int current_image_indices[2];
};

View File

@ -4,6 +4,11 @@ struct PSInput
float2 uv : TEXCOORD;
};
cbuffer Constants : register(b0)
{
float4x4 clip_space_xform;
};
Texture2D texture0 : register(t0);
SamplerState sampler0 : register(s0);
@ -11,7 +16,7 @@ PSInput VSMain(float4 position : POSITION, float2 uv : TEXCOORD)
{
PSInput result;
result.position = position;
result.position = mul(clip_space_xform, position);
result.uv = uv;
return result;

View File

@ -68,6 +68,10 @@ void GL_Bind( image_t *image ) {
VkDescriptorSet set = vk_world.images[final_image->index].descriptor_set;
vk_world.current_descriptor_sets[glState.currenttmu] = set;
}
// D3D
if (dx.active) {
dx_world.current_image_indices[glState.currenttmu] = final_image->index;
}
}
}

View File

@ -325,6 +325,12 @@ typedef struct {
VkPipeline vk_pipeline = VK_NULL_HANDLE;
VkPipeline vk_portal_pipeline = VK_NULL_HANDLE;
VkPipeline vk_mirror_pipeline = VK_NULL_HANDLE;
// D3D
ID3D12PipelineState* dx_pipeline_state = nullptr;
ID3D12PipelineState* dx_portal_pipeline_state = nullptr;
ID3D12PipelineState* dx_mirror_pipeline_state = nullptr;
} shaderStage_t;
struct shaderCommands_s;

View File

@ -748,6 +748,9 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input )
// VULKAN
vk_bind_geometry();
// DX12
dx_bind_geometry();
for ( int stage = 0; stage < MAX_SHADER_STAGES; stage++ )
{
shaderStage_t *pStage = tess.xstages[stage];
@ -817,6 +820,26 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input )
vk_shade_geometry(pipeline, multitexture, depth_range);
}
// DX12
if (dx.active) {
ID3D12PipelineState* pipeline_state = pStage->dx_pipeline_state;
if (backEnd.viewParms.isMirror)
pipeline_state = pStage->dx_mirror_pipeline_state;
else if (backEnd.viewParms.isPortal)
pipeline_state = pStage->dx_portal_pipeline_state;
Vk_Depth_Range depth_range = Vk_Depth_Range::normal;
if (input->shader->isSky) {
depth_range = Vk_Depth_Range::force_one;
if (r_showsky->integer)
depth_range = Vk_Depth_Range::force_zero;
} else if (backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) {
depth_range = Vk_Depth_Range::weapon;
}
dx_shade_geometry(pipeline_state, multitexture, depth_range);
}
// allow skipping out to show just lightmaps during development
if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap ) )
{

View File

@ -2161,7 +2161,8 @@ static shader_t *FinishShader( void ) {
}
// VULKAN: create pipelines for each shader stage
if (vk.active) {
// DX12
if (vk.active || dx.active) {
Vk_Pipeline_Def def;
def.face_culling = shader.cullType;
def.polygon_offset = (shader.polygonOffset == qtrue);
@ -2181,15 +2182,24 @@ static shader_t *FinishShader( void ) {
def.clipping_plane = false;
def.mirror = false;
pStage->vk_pipeline = vk_find_pipeline(def);
if (vk.active)
pStage->vk_pipeline = vk_find_pipeline(def);
if (dx.active)
pStage->dx_pipeline_state = dx_find_pipeline(def);
def.clipping_plane = true;
def.mirror = false;
pStage->vk_portal_pipeline = vk_find_pipeline(def);
if (vk.active)
pStage->vk_portal_pipeline = vk_find_pipeline(def);
if (dx.active)
pStage->dx_portal_pipeline_state = dx_find_pipeline(def);
def.clipping_plane = true;
def.mirror = true;
pStage->vk_mirror_pipeline = vk_find_pipeline(def);
if (vk.active)
pStage->vk_mirror_pipeline = vk_find_pipeline(def);
if (dx.active)
pStage->dx_mirror_pipeline_state = dx_find_pipeline(def);
}
}