VideoBackends:D3D12: Dynamic vertex loader support

This commit is contained in:
TellowKrinkle 2022-06-20 02:55:49 -05:00
parent 936b4d5d0d
commit dae56a24b8
8 changed files with 129 additions and 27 deletions

View File

@ -180,7 +180,8 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline)
m_state.root_signature = dx_pipeline->GetRootSignature(); m_state.root_signature = dx_pipeline->GetRootSignature();
m_dirty_bits |= DirtyState_RootSignature | DirtyState_PS_CBV | DirtyState_VS_CBV | m_dirty_bits |= DirtyState_RootSignature | DirtyState_PS_CBV | DirtyState_VS_CBV |
DirtyState_GS_CBV | DirtyState_SRV_Descriptor | 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) 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()) if (!ApplyState())
return; return;
// DX12 is great and doesn't include the base vertex in SV_VertexID
if (static_cast<const DXPipeline*>(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); 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; 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 && if (m_state.vertex_buffer.BufferLocation != address ||
m_state.vertex_buffer.StrideInBytes == stride && m_state.vertex_buffer.SizeInBytes == size) 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) 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 // Clear bits before actually changing state. Some state (e.g. cbuffers) can't be set
// if utility pipelines are bound. // if utility pipelines are bound.
const u32 dirty_bits = m_dirty_bits; const u32 dirty_bits = m_dirty_bits;
m_dirty_bits &= ~( m_dirty_bits &=
DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Viewport | DirtyState_ScissorRect | ~(DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Viewport |
DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV | DirtyState_ScissorRect | DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV |
DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor | DirtyState_GS_CBV | DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor |
DirtyState_VertexBuffer | DirtyState_IndexBuffer | DirtyState_PrimitiveTopology); DirtyState_UAV_Descriptor | DirtyState_VertexBuffer | DirtyState_IndexBuffer |
DirtyState_PrimitiveTopology | DirtyState_VS_SRV_Descriptor);
auto* const cmdlist = g_dx_context->GetCommandList(); auto* const cmdlist = g_dx_context->GetCommandList();
auto* const pipeline = static_cast<const DXPipeline*>(m_current_pipeline);
if (dirty_bits & DirtyState_Pipeline) if (dirty_bits & DirtyState_Pipeline)
cmdlist->SetPipelineState(static_cast<const DXPipeline*>(m_current_pipeline)->GetPipeline()); cmdlist->SetPipelineState(pipeline->GetPipeline());
if (dirty_bits & DirtyState_Framebuffer) if (dirty_bits & DirtyState_Framebuffer)
BindFramebuffer(static_cast<DXFramebuffer*>(m_current_framebuffer)); BindFramebuffer(static_cast<DXFramebuffer*>(m_current_framebuffer));
@ -572,8 +584,7 @@ bool Renderer::ApplyState()
m_state.sampler_descriptor_base); m_state.sampler_descriptor_base);
} }
if (static_cast<const DXPipeline*>(m_current_pipeline)->GetUsage() != if (pipeline->GetUsage() != AbstractPipelineUsage::Utility)
AbstractPipelineUsage::Utility)
{ {
if (dirty_bits & DirtyState_VS_CBV) 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) if (dirty_bits & DirtyState_GS_CBV)
{ {
cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_GS_CBV, cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_GS_CBV,
@ -642,7 +660,9 @@ void Renderer::UpdateDescriptorTables()
const bool sampler_update_failed = const bool sampler_update_failed =
(m_dirty_bits & DirtyState_Samplers) && !UpdateSamplerDescriptorTable(); (m_dirty_bits & DirtyState_Samplers) && !UpdateSamplerDescriptorTable();
const bool uav_update_failed = (m_dirty_bits & DirtyState_PS_UAV) && !UpdateUAVDescriptorTable(); 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 {}", WARN_LOG_FMT(VIDEO, "Executing command list while waiting for temporary {}",
texture_update_failed ? "descriptors" : "samplers"); texture_update_failed ? "descriptors" : "samplers");
@ -652,6 +672,7 @@ void Renderer::UpdateDescriptorTables()
UpdateSRVDescriptorTable(); UpdateSRVDescriptorTable();
UpdateSamplerDescriptorTable(); UpdateSamplerDescriptorTable();
UpdateUAVDescriptorTable(); UpdateUAVDescriptorTable();
UpdateVSSRVDescriptorTable();
} }
} }
@ -701,6 +722,26 @@ bool Renderer::UpdateUAVDescriptorTable()
return true; return true;
} }
bool Renderer::UpdateVSSRVDescriptorTable()
{
if (!g_ActiveConfig.backend_info.bSupportsDynamicVertexLoader ||
static_cast<const DXPipeline*>(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() bool Renderer::UpdateComputeUAVDescriptorTable()
{ {
DescriptorHandle handle; DescriptorHandle handle;

View File

@ -88,7 +88,8 @@ public:
void SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle); void SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle);
// Graphics vertex/index buffer binding. // 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); void SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format);
// Binds all dirty state // Binds all dirty state
@ -126,6 +127,8 @@ private:
DirtyState_RootSignature = (1 << 17), DirtyState_RootSignature = (1 << 17),
DirtyState_ComputeRootSignature = (1 << 18), DirtyState_ComputeRootSignature = (1 << 18),
DirtyState_DescriptorHeaps = (1 << 19), DirtyState_DescriptorHeaps = (1 << 19),
DirtyState_VS_SRV = (1 << 20),
DirtyState_VS_SRV_Descriptor = (1 << 21),
DirtyState_All = DirtyState_All =
DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Textures | DirtyState_Samplers | 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_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV |
DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor | DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor |
DirtyState_VertexBuffer | DirtyState_IndexBuffer | DirtyState_PrimitiveTopology | 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(); void CheckForSwapChainChanges();
@ -144,6 +148,7 @@ private:
void UpdateDescriptorTables(); void UpdateDescriptorTables();
bool UpdateSRVDescriptorTable(); bool UpdateSRVDescriptorTable();
bool UpdateUAVDescriptorTable(); bool UpdateUAVDescriptorTable();
bool UpdateVSSRVDescriptorTable();
bool UpdateComputeUAVDescriptorTable(); bool UpdateComputeUAVDescriptorTable();
bool UpdateSamplerDescriptorTable(); bool UpdateSamplerDescriptorTable();
@ -157,11 +162,13 @@ private:
DXShader* compute_shader = nullptr; DXShader* compute_shader = nullptr;
std::array<D3D12_GPU_VIRTUAL_ADDRESS, 3> constant_buffers = {}; std::array<D3D12_GPU_VIRTUAL_ADDRESS, 3> constant_buffers = {};
std::array<D3D12_CPU_DESCRIPTOR_HANDLE, MAX_TEXTURES> textures = {}; std::array<D3D12_CPU_DESCRIPTOR_HANDLE, MAX_TEXTURES> textures = {};
D3D12_CPU_DESCRIPTOR_HANDLE vs_srv = {};
D3D12_CPU_DESCRIPTOR_HANDLE ps_uav = {}; D3D12_CPU_DESCRIPTOR_HANDLE ps_uav = {};
SamplerStateSet samplers = {}; SamplerStateSet samplers = {};
const DXTexture* compute_image_texture = nullptr; const DXTexture* compute_image_texture = nullptr;
D3D12_VIEWPORT viewport = {}; D3D12_VIEWPORT viewport = {};
D3D12_RECT scissor = {}; D3D12_RECT scissor = {};
D3D12_GPU_DESCRIPTOR_HANDLE vertex_srv_descriptor_base = {};
D3D12_GPU_DESCRIPTOR_HANDLE srv_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE srv_descriptor_base = {};
D3D12_GPU_DESCRIPTOR_HANDLE sampler_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE sampler_descriptor_base = {};
D3D12_GPU_DESCRIPTOR_HANDLE uav_descriptor_base = {}; D3D12_GPU_DESCRIPTOR_HANDLE uav_descriptor_base = {};

View File

@ -64,6 +64,18 @@ bool VertexManager::Initialize()
&srv_desc, dh.cpu_handle); &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(); UploadAllConstants();
return true; 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<int>(vertex_data_size)); ADDSTAT(g_stats.this_frame.bytes_vertex_streamed, static_cast<int>(vertex_data_size));
ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast<int>(index_data_size)); ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast<int>(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()); m_vertex_stream_buffer.GetSize());
Renderer::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), Renderer::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(),
m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT); m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);

View File

@ -46,6 +46,7 @@ protected:
StreamBuffer m_uniform_stream_buffer; StreamBuffer m_uniform_stream_buffer;
StreamBuffer m_texel_stream_buffer; StreamBuffer m_texel_stream_buffer;
std::array<DescriptorHandle, NUM_TEXEL_BUFFER_FORMATS> m_texel_buffer_views = {}; std::array<DescriptorHandle, NUM_TEXEL_BUFFER_FORMATS> m_texel_buffer_views = {};
DescriptorHandle m_vertex_srv = {};
}; };
} // namespace DX12 } // namespace DX12

View File

@ -261,6 +261,16 @@ bool DXContext::CreateDescriptorHeaps()
return true; 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, static void SetRootParamCBV(D3D12_ROOT_PARAMETER* rp, u32 shader_reg,
D3D12_SHADER_VISIBILITY visibility) D3D12_SHADER_VISIBILITY visibility)
{ {
@ -345,6 +355,11 @@ bool DXContext::CreateGXRootSignature()
param_count++; param_count++;
SetRootParamCBV(&params[param_count], 0, D3D12_SHADER_VISIBILITY_GEOMETRY); SetRootParamCBV(&params[param_count], 0, D3D12_SHADER_VISIBILITY_GEOMETRY);
param_count++; param_count++;
SetRootParamTable(&params[param_count], &ranges[param_count], D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3,
1, D3D12_SHADER_VISIBILITY_VERTEX);
param_count++;
SetRootParamConstant(&params[param_count], 2, 1, D3D12_SHADER_VISIBILITY_VERTEX);
param_count++;
// Since these must be contiguous, pixel lighting goes to bbox if not enabled. // Since these must be contiguous, pixel lighting goes to bbox if not enabled.
if (g_ActiveConfig.bBBoxEnable) if (g_ActiveConfig.bBBoxEnable)

View File

@ -27,6 +27,8 @@ enum ROOT_PARAMETER
ROOT_PARAMETER_VS_CBV, ROOT_PARAMETER_VS_CBV,
ROOT_PARAMETER_VS_CBV2, ROOT_PARAMETER_VS_CBV2,
ROOT_PARAMETER_GS_CBV, ROOT_PARAMETER_GS_CBV,
ROOT_PARAMETER_VS_SRV,
ROOT_PARAMETER_BASE_VERTEX_CONSTANT,
ROOT_PARAMETER_PS_UAV_OR_CBV2, ROOT_PARAMETER_PS_UAV_OR_CBV2,
ROOT_PARAMETER_PS_CBV2, // ROOT_PARAMETER_PS_UAV_OR_CBV2 if bbox is not enabled ROOT_PARAMETER_PS_CBV2, // ROOT_PARAMETER_PS_UAV_OR_CBV2 if bbox is not enabled
NUM_ROOT_PARAMETERS NUM_ROOT_PARAMETERS

View File

@ -87,7 +87,7 @@ void VideoBackend::FillBackendInfo()
g_Config.backend_info.bSupportsLodBiasInSampler = true; g_Config.backend_info.bSupportsLodBiasInSampler = true;
g_Config.backend_info.bSupportsSettingObjectNames = true; g_Config.backend_info.bSupportsSettingObjectNames = true;
g_Config.backend_info.bSupportsPartialMultisampleResolve = 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. // We can only check texture support once we have a device.
if (g_dx_context) if (g_dx_context)

View File

@ -60,11 +60,34 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
SSBO_BINDING(1) readonly restrict buffer Vertices {{ SSBO_BINDING(1) readonly restrict buffer Vertices {{
uint vertex_buffer[]; 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() {{ out.Write(R"(
return gl_VertexID * vertex_stride;
}}
uint4 load_input_uint4_ubyte4(uint vtx_offset, uint attr_offset) {{ uint4 load_input_uint4_ubyte4(uint vtx_offset, uint attr_offset) {{
uint value = vertex_buffer[vtx_offset + attr_offset]; uint value = vertex_buffer[vtx_offset + attr_offset];
return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); return uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);