diff --git a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp b/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp index bfe44a3a58..80872086a9 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp @@ -180,7 +180,8 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) m_state.root_signature = dx_pipeline->GetRootSignature(); m_dirty_bits |= DirtyState_RootSignature | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV | DirtyState_SRV_Descriptor | - DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor; + DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor | + DirtyState_VS_SRV_Descriptor; } if (dx_pipeline->UseIntegerRTV() != m_state.using_integer_rtv) { @@ -362,6 +363,11 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) if (!ApplyState()) return; + // DX12 is great and doesn't include the base vertex in SV_VertexID + if (static_cast(m_current_pipeline)->GetUsage() == + AbstractPipelineUsage::GXUber) + g_dx_context->GetCommandList()->SetGraphicsRoot32BitConstant( + ROOT_PARAMETER_BASE_VERTEX_CONSTANT, base_vertex, 0); g_dx_context->GetCommandList()->DrawIndexedInstanced(num_indices, 1, base_index, base_vertex, 0); } @@ -494,18 +500,22 @@ void Renderer::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle) m_dirty_bits |= DirtyState_PS_UAV; } -void Renderer::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 stride, u32 size) +void Renderer::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESCRIPTOR_HANDLE srv, + u32 stride, u32 size) { - if (m_state.vertex_buffer.BufferLocation == address && - m_state.vertex_buffer.StrideInBytes == stride && m_state.vertex_buffer.SizeInBytes == size) + if (m_state.vertex_buffer.BufferLocation != address || + m_state.vertex_buffer.StrideInBytes != stride || m_state.vertex_buffer.SizeInBytes != size) { - return; + m_state.vertex_buffer.BufferLocation = address; + m_state.vertex_buffer.StrideInBytes = stride; + m_state.vertex_buffer.SizeInBytes = size; + m_dirty_bits |= DirtyState_VertexBuffer; + } + if (m_state.vs_srv.ptr != srv.ptr) + { + m_state.vs_srv = srv; + m_dirty_bits |= DirtyState_VS_SRV; } - - m_state.vertex_buffer.BufferLocation = address; - m_state.vertex_buffer.StrideInBytes = stride; - m_state.vertex_buffer.SizeInBytes = size; - m_dirty_bits |= DirtyState_VertexBuffer; } void Renderer::SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format) @@ -535,15 +545,17 @@ bool Renderer::ApplyState() // Clear bits before actually changing state. Some state (e.g. cbuffers) can't be set // if utility pipelines are bound. const u32 dirty_bits = m_dirty_bits; - m_dirty_bits &= ~( - DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Viewport | DirtyState_ScissorRect | - DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV | - DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor | - DirtyState_VertexBuffer | DirtyState_IndexBuffer | DirtyState_PrimitiveTopology); + m_dirty_bits &= + ~(DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Viewport | + DirtyState_ScissorRect | DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | + DirtyState_GS_CBV | DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | + DirtyState_UAV_Descriptor | DirtyState_VertexBuffer | DirtyState_IndexBuffer | + DirtyState_PrimitiveTopology | DirtyState_VS_SRV_Descriptor); auto* const cmdlist = g_dx_context->GetCommandList(); + auto* const pipeline = static_cast(m_current_pipeline); if (dirty_bits & DirtyState_Pipeline) - cmdlist->SetPipelineState(static_cast(m_current_pipeline)->GetPipeline()); + cmdlist->SetPipelineState(pipeline->GetPipeline()); if (dirty_bits & DirtyState_Framebuffer) BindFramebuffer(static_cast(m_current_framebuffer)); @@ -572,8 +584,7 @@ bool Renderer::ApplyState() m_state.sampler_descriptor_base); } - if (static_cast(m_current_pipeline)->GetUsage() != - AbstractPipelineUsage::Utility) + if (pipeline->GetUsage() != AbstractPipelineUsage::Utility) { if (dirty_bits & DirtyState_VS_CBV) { @@ -590,6 +601,13 @@ bool Renderer::ApplyState() } } + if (dirty_bits & DirtyState_VS_SRV_Descriptor && + pipeline->GetUsage() == AbstractPipelineUsage::GXUber) + { + cmdlist->SetGraphicsRootDescriptorTable(ROOT_PARAMETER_VS_SRV, + m_state.vertex_srv_descriptor_base); + } + if (dirty_bits & DirtyState_GS_CBV) { cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_GS_CBV, @@ -642,7 +660,9 @@ void Renderer::UpdateDescriptorTables() const bool sampler_update_failed = (m_dirty_bits & DirtyState_Samplers) && !UpdateSamplerDescriptorTable(); const bool uav_update_failed = (m_dirty_bits & DirtyState_PS_UAV) && !UpdateUAVDescriptorTable(); - if (texture_update_failed || sampler_update_failed || uav_update_failed) + const bool srv_update_failed = + (m_dirty_bits & DirtyState_VS_SRV) && !UpdateVSSRVDescriptorTable(); + if (texture_update_failed || sampler_update_failed || uav_update_failed || srv_update_failed) { WARN_LOG_FMT(VIDEO, "Executing command list while waiting for temporary {}", texture_update_failed ? "descriptors" : "samplers"); @@ -652,6 +672,7 @@ void Renderer::UpdateDescriptorTables() UpdateSRVDescriptorTable(); UpdateSamplerDescriptorTable(); UpdateUAVDescriptorTable(); + UpdateVSSRVDescriptorTable(); } } @@ -701,6 +722,26 @@ bool Renderer::UpdateUAVDescriptorTable() return true; } +bool Renderer::UpdateVSSRVDescriptorTable() +{ + if (!g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader || + static_cast(m_current_pipeline)->GetUsage() != + AbstractPipelineUsage::GXUber) + { + return true; + } + + DescriptorHandle handle; + if (!g_dx_context->GetDescriptorAllocator()->Allocate(1, &handle)) + return false; + + g_dx_context->GetDevice()->CopyDescriptorsSimple(1, handle.cpu_handle, m_state.vs_srv, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + m_state.vertex_srv_descriptor_base = handle.gpu_handle; + m_dirty_bits = (m_dirty_bits & ~DirtyState_VS_SRV) | DirtyState_VS_SRV_Descriptor; + return true; +} + bool Renderer::UpdateComputeUAVDescriptorTable() { DescriptorHandle handle; diff --git a/Source/Core/VideoBackends/D3D12/D3D12Renderer.h b/Source/Core/VideoBackends/D3D12/D3D12Renderer.h index 9624199aa4..19188a5acf 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Renderer.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Renderer.h @@ -88,7 +88,8 @@ public: void SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle); // Graphics vertex/index buffer binding. - void SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 stride, u32 size); + void SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESCRIPTOR_HANDLE srv, + u32 stride, u32 size); void SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format); // Binds all dirty state @@ -126,6 +127,8 @@ private: DirtyState_RootSignature = (1 << 17), DirtyState_ComputeRootSignature = (1 << 18), DirtyState_DescriptorHeaps = (1 << 19), + DirtyState_VS_SRV = (1 << 20), + DirtyState_VS_SRV_Descriptor = (1 << 21), DirtyState_All = DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Textures | DirtyState_Samplers | @@ -133,7 +136,8 @@ private: DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV | DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor | DirtyState_VertexBuffer | DirtyState_IndexBuffer | DirtyState_PrimitiveTopology | - DirtyState_RootSignature | DirtyState_ComputeRootSignature | DirtyState_DescriptorHeaps + DirtyState_RootSignature | DirtyState_ComputeRootSignature | DirtyState_DescriptorHeaps | + DirtyState_VS_SRV | DirtyState_VS_SRV_Descriptor }; void CheckForSwapChainChanges(); @@ -144,6 +148,7 @@ private: void UpdateDescriptorTables(); bool UpdateSRVDescriptorTable(); bool UpdateUAVDescriptorTable(); + bool UpdateVSSRVDescriptorTable(); bool UpdateComputeUAVDescriptorTable(); bool UpdateSamplerDescriptorTable(); @@ -157,11 +162,13 @@ private: DXShader* compute_shader = nullptr; std::array constant_buffers = {}; std::array textures = {}; + D3D12_CPU_DESCRIPTOR_HANDLE vs_srv = {}; D3D12_CPU_DESCRIPTOR_HANDLE ps_uav = {}; SamplerStateSet samplers = {}; const DXTexture* compute_image_texture = nullptr; D3D12_VIEWPORT viewport = {}; D3D12_RECT scissor = {}; + D3D12_GPU_DESCRIPTOR_HANDLE vertex_srv_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE srv_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE sampler_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE uav_descriptor_base = {}; diff --git a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp index b85e4e459c..5a9efc0d6c 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp @@ -64,6 +64,18 @@ bool VertexManager::Initialize() &srv_desc, dh.cpu_handle); } + if (!g_dx_context->GetDescriptorHeapManager().Allocate(&m_vertex_srv)) + { + PanicAlertFmt("Failed to allocate descriptor for vertex srv"); + return false; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {DXGI_FORMAT_R32_UINT, D3D12_SRV_DIMENSION_BUFFER, + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; + srv_desc.Buffer.NumElements = m_vertex_stream_buffer.GetSize() / sizeof(u32); + g_dx_context->GetDevice()->CreateShaderResourceView(m_vertex_stream_buffer.GetBuffer(), &srv_desc, + m_vertex_srv.cpu_handle); + UploadAllConstants(); return true; } @@ -115,7 +127,8 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in ADDSTAT(g_stats.this_frame.bytes_vertex_streamed, static_cast(vertex_data_size)); ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast(index_data_size)); - Renderer::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), vertex_stride, + Renderer::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), + m_vertex_srv.cpu_handle, vertex_stride, m_vertex_stream_buffer.GetSize()); Renderer::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT); diff --git a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h index 6bcff92bb7..2dd2cdfee9 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h +++ b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h @@ -46,6 +46,7 @@ protected: StreamBuffer m_uniform_stream_buffer; StreamBuffer m_texel_stream_buffer; std::array m_texel_buffer_views = {}; + DescriptorHandle m_vertex_srv = {}; }; } // namespace DX12 diff --git a/Source/Core/VideoBackends/D3D12/DX12Context.cpp b/Source/Core/VideoBackends/D3D12/DX12Context.cpp index e3ff8a7417..5cf0e414c4 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Context.cpp +++ b/Source/Core/VideoBackends/D3D12/DX12Context.cpp @@ -261,6 +261,16 @@ bool DXContext::CreateDescriptorHeaps() return true; } +static void SetRootParamConstant(D3D12_ROOT_PARAMETER* rp, u32 shader_reg, u32 num_values, + D3D12_SHADER_VISIBILITY visibility) +{ + rp->ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + rp->Constants.Num32BitValues = num_values; + rp->Constants.ShaderRegister = shader_reg; + rp->Constants.RegisterSpace = 0; + rp->ShaderVisibility = visibility; +} + static void SetRootParamCBV(D3D12_ROOT_PARAMETER* rp, u32 shader_reg, D3D12_SHADER_VISIBILITY visibility) { @@ -345,6 +355,11 @@ bool DXContext::CreateGXRootSignature() param_count++; SetRootParamCBV(¶ms[param_count], 0, D3D12_SHADER_VISIBILITY_GEOMETRY); param_count++; + SetRootParamTable(¶ms[param_count], &ranges[param_count], D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3, + 1, D3D12_SHADER_VISIBILITY_VERTEX); + param_count++; + SetRootParamConstant(¶ms[param_count], 2, 1, D3D12_SHADER_VISIBILITY_VERTEX); + param_count++; // Since these must be contiguous, pixel lighting goes to bbox if not enabled. if (g_ActiveConfig.bBBoxEnable) diff --git a/Source/Core/VideoBackends/D3D12/DX12Context.h b/Source/Core/VideoBackends/D3D12/DX12Context.h index d1c5b38798..ad9ef9fe74 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Context.h +++ b/Source/Core/VideoBackends/D3D12/DX12Context.h @@ -27,6 +27,8 @@ enum ROOT_PARAMETER ROOT_PARAMETER_VS_CBV, ROOT_PARAMETER_VS_CBV2, ROOT_PARAMETER_GS_CBV, + ROOT_PARAMETER_VS_SRV, + ROOT_PARAMETER_BASE_VERTEX_CONSTANT, ROOT_PARAMETER_PS_UAV_OR_CBV2, ROOT_PARAMETER_PS_CBV2, // ROOT_PARAMETER_PS_UAV_OR_CBV2 if bbox is not enabled NUM_ROOT_PARAMETERS diff --git a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp index 99cf3955c9..b901b994e0 100644 --- a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp +++ b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp @@ -87,7 +87,7 @@ void VideoBackend::FillBackendInfo() g_Config.backend_info.bSupportsLodBiasInSampler = true; g_Config.backend_info.bSupportsSettingObjectNames = true; g_Config.backend_info.bSupportsPartialMultisampleResolve = true; - g_Config.backend_info.bSupportsDynamicVertexLoader = false; + g_Config.backend_info.bSupportsDynamicVertexLoader = true; // We can only check texture support once we have a device. if (g_dx_context) diff --git a/Source/Core/VideoCommon/UberShaderVertex.cpp b/Source/Core/VideoCommon/UberShaderVertex.cpp index b49a5d8954..04f2b27a14 100644 --- a/Source/Core/VideoCommon/UberShaderVertex.cpp +++ b/Source/Core/VideoCommon/UberShaderVertex.cpp @@ -60,11 +60,34 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config SSBO_BINDING(1) readonly restrict buffer Vertices {{ uint vertex_buffer[]; }}; +)"); + if (api_type == APIType::D3D) + { + // Write a function to get an offset into vertex_buffer corresponding to this vertex. + // This must be done differently for D3D compared to OpenGL/Vulkan/Metal, as on OpenGL, etc., + // gl_VertexID starts counting at the base vertex specified in glDrawElementsBaseVertex, + // while on D3D, SV_VertexID (which spirv-cross translates gl_VertexID into) starts counting + // at 0 regardless of the BaseVertexLocation value passed to DrawIndexed. In both cases, + // offset 0 of vertex_buffer corresponds to index 0 with basevertex set to 0, so we have to + // manually apply the basevertex offset for D3D + // D3D12 uses a root constant for this uniform, since it changes with every draw. + // D3D11 doesn't currently support dynamic vertex loader, and we'll have to figure something + // out for it if we want to support it in the future. + out.Write("UBO_BINDING(std140, 3) uniform DX_Constants {{\n" + " uint base_vertex;\n" + "}};\n\n" + "uint GetVertexBaseOffset() {{\n" + " return (gl_VertexID + base_vertex) * vertex_stride;\n" + "}}\n"); + } + else + { + out.Write("uint GetVertexBaseOffset() {{\n" + " return gl_VertexID * vertex_stride;\n" + "}}\n"); + } -uint GetVertexBaseOffset() {{ - return gl_VertexID * vertex_stride; -}} - + out.Write(R"( uint4 load_input_uint4_ubyte4(uint vtx_offset, uint attr_offset) {{ uint value = vertex_buffer[vtx_offset + attr_offset]; return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);