diff --git a/src/engine/renderer/dx.cpp b/src/engine/renderer/dx.cpp index ec6660e..dd374e1 100644 --- a/src/engine/renderer/dx.cpp +++ b/src/engine/renderer/dx.cpp @@ -10,6 +10,21 @@ #include "D3Dcompiler.h" #include +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 recorder) { +static void record_and_run_commands(ID3D12CommandQueue* command_queue, std::function 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(&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(p_data); + assert(((size_t)dx.vertex_buffer_ptr & 0xffff) == 0); + + dx.index_buffer_ptr = static_cast(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(tess.numVertexes * sizeof(vec4_t)); + xyz_view.StrideInBytes = static_cast(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(indexes_size); + index_view.Format = DXGI_FORMAT_R32_UINT; + dx.command_list->IASetIndexBuffer(&index_view); + + dx.index_buffer_offset += static_cast(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(tess.numVertexes * sizeof(color4ub_t)); + color_st_views[0].StrideInBytes = static_cast(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(tess.numVertexes * sizeof(vec2_t)); + color_st_views[1].StrideInBytes = static_cast(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(tess.numVertexes * sizeof(vec2_t)); + color_st_views[2].StrideInBytes = static_cast(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); diff --git a/src/engine/renderer/dx.h b/src/engine/renderer/dx.h index 22e4d49..bc910e6 100644 --- a/src/engine/renderer/dx.h +++ b/src/engine/renderer/dx.h @@ -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]; }; diff --git a/src/engine/renderer/shaders/shaders.hlsl b/src/engine/renderer/shaders/shaders.hlsl index 2eb058f..b4e38d9 100644 --- a/src/engine/renderer/shaders/shaders.hlsl +++ b/src/engine/renderer/shaders/shaders.hlsl @@ -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; diff --git a/src/engine/renderer/tr_backend.c b/src/engine/renderer/tr_backend.c index dfd67d6..9082627 100644 --- a/src/engine/renderer/tr_backend.c +++ b/src/engine/renderer/tr_backend.c @@ -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; + } } } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index bc35160..fd1ea76 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -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; diff --git a/src/engine/renderer/tr_shade.c b/src/engine/renderer/tr_shade.c index 819d088..845b981 100644 --- a/src/engine/renderer/tr_shade.c +++ b/src/engine/renderer/tr_shade.c @@ -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 ) ) { diff --git a/src/engine/renderer/tr_shader.c b/src/engine/renderer/tr_shader.c index 6e6db72..4130e06 100644 --- a/src/engine/renderer/tr_shader.c +++ b/src/engine/renderer/tr_shader.c @@ -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); } }