From a5d307ee7ab3d1eee7bf82016cb2b5da5401b491 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Mon, 14 Sep 2026 11:37:43 +0300 Subject: [PATCH] shader_recompiler: Implement support for base vertex/instance in indirect draws (#5028) * shader_recompiler: Add base vertex and base instance attributes * shader_recompiler: Emit prologue after parsing entire shader It is needed to know if a fetch shader exists and if it has base vertex/instance add * liverpool: Consolidate scope marker usage into a single helper Its a lot less noise * shader_recompiler: Implement support for base vertex/instance for indirect draws A normal indexed draw has 3 main parameters other the number of indices; firstIndex, vertexOffset and firstInstance Of those firstIndex is already provided by PM4 draw packet and simply offsets the index buffer base address. The other parameters, at least on GCN are implemented in software, the driver can put those values somewhere in user data and fetch shader will add them to the vgpr of the vertex/instance index respectively. The emulator already supports this, by parsing the fetch program, finding any such additions, which user data register is used and applying the parameters to the draw itself. Note that in these cases the final vertex/instance index the shader sees is biased by these values, which is consistent with the provided gl_VertexIndex and gl_InstanceID vulkan builtins Indirect draws work similarly but since the vertexOffset and firstInstance are in GPU modified buffers, the command processor can optionally copy them to a user data of the PM4 packets choosing. To emulate this, add the packet provided offset into the shader runtime state and optionally set those registers to the vulkan builtins, overwritting the user data load before them. Additionally since the vertex/instance index builtins have to optionally be unbiased because the hardware only provides unbiased values. This wasnt an issue with normal draws because the act of discovering a base offset to either builtin implied the index was biased, but now its not guaranteed as such. An indirect draw might not have a fetch shader at all and manually add the base vertex/instance itself, where in that case the base would be added twice --- .../spirv/emit_spirv_context_get_set.cpp | 4 + .../backend/spirv/spirv_emit_context.cpp | 10 +- .../backend/spirv/spirv_emit_context.h | 1 + .../frontend/translate/translate.cpp | 61 ++++- .../frontend/translate/translate.h | 2 + src/shader_recompiler/ir/attribute.cpp | 4 + src/shader_recompiler/ir/attribute.h | 2 + src/shader_recompiler/recompiler.cpp | 6 +- src/shader_recompiler/runtime_info.h | 2 + src/video_core/amdgpu/liverpool.cpp | 230 +++++++----------- src/video_core/amdgpu/liverpool.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 5 +- .../renderer_vulkan/vk_pipeline_cache.h | 8 +- .../renderer_vulkan/vk_rasterizer.cpp | 14 +- .../renderer_vulkan/vk_rasterizer.h | 14 +- 15 files changed, 203 insertions(+), 161 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 31e1cea5a..601453aa0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -172,6 +172,10 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) { return ctx.OpLoad(ctx.U32[1], ctx.vertex_index); case IR::Attribute::InstanceId: return ctx.OpLoad(ctx.U32[1], ctx.instance_id); + case IR::Attribute::BaseVertex: + return ctx.OpLoad(ctx.U32[1], ctx.base_vertex); + case IR::Attribute::BaseInstance: + return ctx.OpLoad(ctx.U32[1], ctx.base_instance); case IR::Attribute::WorkgroupIndex: return ctx.workgroup_index_id; case IR::Attribute::WorkgroupId: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 4e64d5f49..d01a22239 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -5,6 +5,7 @@ #include "common/div_ceil.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/frontend/fetch_shader.h" +#include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/microinstruction.h" #include "shader_recompiler/runtime_info.h" #include "video_core/buffer_cache/buffer_cache.h" @@ -322,8 +323,15 @@ void EmitContext::DefineInputs() { switch (sw_stage) { case SwStage::Vertex: { vertex_index = DefineVariable(U32[1], spv::BuiltIn::VertexIndex, spv::StorageClass::Input); - base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); instance_id = DefineVariable(U32[1], spv::BuiltIn::InstanceIndex, spv::StorageClass::Input); + if (info.loads.Get(IR::Attribute::BaseVertex)) { + base_vertex = + DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); + } + if (info.loads.Get(IR::Attribute::BaseInstance)) { + base_instance = + DefineVariable(U32[1], spv::BuiltIn::BaseInstance, spv::StorageClass::Input); + } const auto fetch_shader = Gcn::ParseFetchShader(info); if (!fetch_shader) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 4c7d130b3..66a43e941 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -253,6 +253,7 @@ public: Id instance_id{}; Id push_data_block{}; Id base_vertex{}; + Id base_instance{}; Id frag_coord{}; Id front_facing{}; Id frag_depth{}; diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 29b19220f..d0ec062a0 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -61,6 +61,16 @@ static IR::VectorReg IterateBarycentrics(const RuntimeInfo& runtime_info, auto&& return IR::VectorReg(dst_vreg); } +static s8 UdRegFromShOffset(u16 sgpr_offset, HwStage hw_stage) { + static constexpr std::array indirect_sgpr_offsets{0u, 0x4cu, 0u, 0xccu, 0u, 0x14cu}; + if (sgpr_offset) { + const u32 ud_reg = sgpr_offset - indirect_sgpr_offsets[u32(hw_stage)]; + ASSERT_MSG(ud_reg < 16, "Out of bounds indirect SGPR copy"); + return ud_reg; + } + return -1; +} + Translator::Translator(Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_) : info{info_}, runtime_info{runtime_info_}, profile{profile_}, next_vgpr_num{runtime_info.props.num_allocated_vgprs} { @@ -84,9 +94,32 @@ void Translator::EmitPrologue(IR::Block* first_block) { IR::VectorReg dst_vreg = IR::VectorReg::V0; switch (info.sw_stage) { - case SwStage::Vertex: + case SwStage::Vertex: { + const s8 base_vertex_sgpr = + UdRegFromShOffset(runtime_info.sw.vs.vertex_sgpr_offset, info.hw_stage); + if (base_vertex_sgpr != -1) { + ir.SetScalarReg(IR::ScalarReg(base_vertex_sgpr), + ir.GetAttributeU32(IR::Attribute::BaseVertex)); + } + const s8 base_instance_sgpr = + UdRegFromShOffset(runtime_info.sw.vs.instance_sgpr_offset, info.hw_stage); + if (base_instance_sgpr != -1) { + ir.SetScalarReg(IR::ScalarReg(base_instance_sgpr), + ir.GetAttributeU32(IR::Attribute::BaseInstance)); + } + // v0: vertex ID, always present - ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::VertexId)); + IR::U32 vertex_id = ir.GetAttributeU32(IR::Attribute::VertexId); + if (base_vertex_sgpr != -1) { + if (!fetch_data || fetch_data->vertex_offset_sgpr == -1) { + vertex_id = ir.ISub(vertex_id, ir.GetAttributeU32(IR::Attribute::BaseVertex)); + } else { + ASSERT_MSG(fetch_data->vertex_offset_sgpr == base_vertex_sgpr, + "Fetch shader in indirect draw uses wrong base vertex"); + } + } + ir.SetVectorReg(dst_vreg++, vertex_id); + if (info.hw_stage == HwStage::Local) { // v1: rel patch ID if (runtime_info.props.num_input_vgprs > 0) { @@ -96,10 +129,6 @@ void Translator::EmitPrologue(IR::Block* first_block) { if (runtime_info.props.num_input_vgprs > 1) { ++dst_vreg; } - // v3: instance ID, plain - if (runtime_info.props.num_input_vgprs > 2) { - ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::InstanceId)); - } } else { // v1: instance ID, step rate 0 if (runtime_info.props.num_input_vgprs > 0) { @@ -121,12 +150,24 @@ void Translator::EmitPrologue(IR::Block* first_block) { ir.SetVectorReg(dst_vreg++, ir.Imm32(0)); } } - // v3: instance ID, plain - if (runtime_info.props.num_input_vgprs > 2) { - ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::InstanceId)); + } + + // v3: instance ID, plain + if (runtime_info.props.num_input_vgprs > 2) { + IR::U32 instance_id = ir.GetAttributeU32(IR::Attribute::InstanceId); + if (base_instance_sgpr != -1) { + if (!fetch_data || fetch_data->instance_offset_sgpr == -1) { + instance_id = + ir.ISub(instance_id, ir.GetAttributeU32(IR::Attribute::BaseInstance)); + } else { + ASSERT_MSG(fetch_data->instance_offset_sgpr == base_instance_sgpr, + "Fetch shader in indirect draw uses wrong base instance"); + } } + ir.SetVectorReg(dst_vreg++, instance_id); } break; + } case SwStage::Fragment: { dst_vreg = IterateBarycentrics(runtime_info, [this](u32 vreg, IR::Attribute attrib, u32 comp) { @@ -1091,7 +1132,7 @@ void Translator::EmitFetch(const GcnInst& inst) { info.has_fetch_shader = true; info.fetch_shader_sgpr_base = code_sgpr_base; - const auto fetch_data = ParseFetchShader(info); + fetch_data = ParseFetchShader(info); ASSERT(fetch_data.has_value()); if (EmulatorSettings.IsDumpShaders()) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 3ba0a5e5e..6655849d2 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -5,6 +5,7 @@ #include #include +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/frontend/instruction.h" #include "shader_recompiler/info.h" #include "shader_recompiler/ir/basic_block.h" @@ -391,6 +392,7 @@ private: u32 next_vgpr_num; std::unordered_map vgpr_map; std::array vgpr_to_interp{}; + std::optional fetch_data{}; bool opcode_missing = false; u32 pc{}; }; diff --git a/src/shader_recompiler/ir/attribute.cpp b/src/shader_recompiler/ir/attribute.cpp index d1d9691d3..14fd11b75 100644 --- a/src/shader_recompiler/ir/attribute.cpp +++ b/src/shader_recompiler/ir/attribute.cpp @@ -168,6 +168,10 @@ std::string NameOf(Attribute attribute) { return "PackedAncillary"; case Attribute::SubgroupLtMask: return "SubgroupLtMask"; + case Attribute::BaseVertex: + return "BaseVertex"; + case Attribute::BaseInstance: + return "BaseInstance"; default: break; } diff --git a/src/shader_recompiler/ir/attribute.h b/src/shader_recompiler/ir/attribute.h index a7e52d54f..658c2048b 100644 --- a/src/shader_recompiler/ir/attribute.h +++ b/src/shader_recompiler/ir/attribute.h @@ -92,6 +92,8 @@ enum class Attribute : u64 { SampleMask = 95, PackedAncillary = 96, SubgroupLtMask = 97, + BaseVertex = 98, + BaseInstance = 99, Max, }; diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 74005d5e5..73953b00f 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -33,7 +33,6 @@ IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) { void EmitControlFlowGraph(IR::Program& program, Pools& pools, Gcn::CFG& cfg, RuntimeInfo& runtime_info, const Profile& profile) { Gcn::Translator translator{program.info, runtime_info, profile}; - bool emit_prologue = true; for (auto& block : cfg) { const u32 start = block.begin_index; const u32 size = block.end_index - start + 1; @@ -42,12 +41,9 @@ void EmitControlFlowGraph(IR::Program& program, Pools& pools, Gcn::CFG& cfg, block.ir_block = ir_block; translator.Translate(ir_block, block.begin, block.cond, std::span{program.ins_list}.subspan(start, size)); - if (emit_prologue) { - translator.EmitPrologue(ir_block); - emit_prologue = false; - } program.blocks.push_back(ir_block); } + translator.EmitPrologue(program.blocks.front()); ASSERT_MSG(!program.info.translation_failed, "Shader translation has failed"); for (auto& block : cfg) { auto* ir_block = block.ir_block; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index e1cb1c65a..5b5712114 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -71,6 +71,8 @@ using OutputMap = std::array; struct SwVertexRuntimeInfo { u32 step_rate_0; u32 step_rate_1; + u16 vertex_sgpr_offset{}; + u16 instance_sgpr_offset{}; bool tess_emulated_primitive{}; bool operator==(const SwVertexRuntimeInfo& other) const noexcept = default; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index cc7ce6850..9794aa193 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -65,7 +65,7 @@ static std::span NextPacket(std::span span, size_t offset) return span.subspan(offset); } -Liverpool::Liverpool() { +Liverpool::Liverpool() : guest_markers_enabled{EmulatorSettings.IsVkGuestMarkersEnabled()} { num_counter_pairs = Libraries::Kernel::sceKernelIsNeoMode() ? 16 : 8; process_thread = std::jthread{std::bind_front(&Liverpool::Process, this)}; } @@ -229,8 +229,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(dcb.data()); while (!dcb.empty()) { @@ -427,16 +425,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndex2", cmd_address)); - rasterizer->Draw(true); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->Draw(true); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker("gfx:{}:DrawIndex2", fmt::make_format_args(cmd_address), + [&] { rasterizer->Draw(true); }); break; } case PM4ItOpcode::DrawIndexOffset2: { @@ -448,17 +442,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndexOffset2", cmd_address)); - rasterizer->Draw(true, draw_index_off->index_offset); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->Draw(true, draw_index_off->index_offset); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndexOffset2", fmt::make_format_args(cmd_address), + [&] { rasterizer->Draw(true, draw_index_off->index_offset); }); break; } case PM4ItOpcode::DrawIndexAuto: { @@ -468,17 +458,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndexAuto", cmd_address)); - rasterizer->Draw(false); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->Draw(false); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker("gfx:{}:DrawIndexAuto", fmt::make_format_args(cmd_address), + [&] { rasterizer->Draw(false); }); break; } case PM4ItOpcode::DrawIndirect: { @@ -488,17 +473,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndirect", fmt::make_format_args(cmd_address), [&] { + rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0, + draw_indirect->base_vtx_loc, + draw_indirect->start_inst_loc); + }); break; } case PM4ItOpcode::DrawIndirectMulti: { @@ -508,19 +492,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndirectMulti", cmd_address)); - rasterizer->DrawIndirect(false, indirect_args_addr, offset, - draw_indirect->stride, draw_indirect->count, 0); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DrawIndirect(false, indirect_args_addr, offset, - draw_indirect->stride, draw_indirect->count, 0); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndirectMulti", fmt::make_format_args(cmd_address), [&] { + rasterizer->DrawIndirect(false, indirect_args_addr, offset, + draw_indirect->stride, draw_indirect->count, 0, + draw_indirect->base_vtx_loc, + draw_indirect->start_inst_loc); + }); break; } case PM4ItOpcode::DrawIndexIndirect: { @@ -531,17 +513,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndexIndirect", fmt::make_format_args(cmd_address), [&] { + rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0, + draw_index_indirect->base_vtx_loc, + draw_index_indirect->start_inst_loc); + }); break; } case PM4ItOpcode::DrawIndexIndirectMulti: { @@ -551,21 +532,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndexIndirectMulti", cmd_address)); - rasterizer->DrawIndirect(true, indirect_args_addr, offset, - draw_index_indirect->stride, - draw_index_indirect->count, 0); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DrawIndirect(true, indirect_args_addr, offset, - draw_index_indirect->stride, - draw_index_indirect->count, 0); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndexIndirectMulti", fmt::make_format_args(cmd_address), [&] { + rasterizer->DrawIndirect( + true, indirect_args_addr, offset, draw_index_indirect->stride, + draw_index_indirect->count, 0, draw_index_indirect->base_vtx_loc, + draw_index_indirect->start_inst_loc); + }); break; } case PM4ItOpcode::DrawIndexIndirectCountMulti: { @@ -575,27 +552,20 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); } - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address)); - rasterizer->DrawIndirect(true, indirect_args_addr, offset, - draw_index_indirect->stride, - draw_index_indirect->count, - draw_index_indirect->count_indirect_enable.Value() - ? draw_index_indirect->count_addr - : 0); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DrawIndirect(true, indirect_args_addr, offset, - draw_index_indirect->stride, - draw_index_indirect->count, - draw_index_indirect->count_indirect_enable.Value() - ? draw_index_indirect->count_addr - : 0); - } + if (!rasterizer) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DrawIndexIndirectCountMulti", fmt::make_format_args(cmd_address), [&] { + rasterizer->DrawIndirect( + true, indirect_args_addr, offset, draw_index_indirect->stride, + draw_index_indirect->count, + draw_index_indirect->count_indirect_enable.Value() + ? draw_index_indirect->count_addr + : 0, + draw_index_indirect->base_vtx_loc, draw_index_indirect->start_inst_loc); + }); break; } case PM4ItOpcode::DispatchDirect: { @@ -609,17 +579,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), cs_program); } - if (rasterizer && (cs_program.dispatch_initiator & 1)) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DispatchDirect", cmd_address)); - rasterizer->DispatchDirect(); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DispatchDirect(); - } + if (!rasterizer || (cs_program.dispatch_initiator & 1) == 0) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker("gfx:{}:DispatchDirect", fmt::make_format_args(cmd_address), + [&] { rasterizer->DispatchDirect(); }); break; } case PM4ItOpcode::DispatchIndirect: { @@ -632,17 +597,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), cs_program); } - if (rasterizer && (cs_program.dispatch_initiator & 1)) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("gfx:{}:DispatchIndirect", cmd_address)); - rasterizer->DispatchIndirect(indirect_args_addr, offset, size); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DispatchIndirect(indirect_args_addr, offset, size); - } + if (!rasterizer || (cs_program.dispatch_initiator & 1) == 0) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker( + "gfx:{}:DispatchIndirect", fmt::make_format_args(cmd_address), + [&] { rasterizer->DispatchIndirect(indirect_args_addr, offset, size); }); break; } case PM4ItOpcode::NumInstances: { @@ -906,7 +867,6 @@ template Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { FIBER_ENTER(acb_task_name[vqid]); auto& queue = asc_queues[{vqid}]; - const bool host_markers_enabled = rasterizer && EmulatorSettings.IsVkHostMarkersEnabled(); struct IndirectPatch { const PM4Header* header; @@ -1076,17 +1036,13 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), cs_program); } - if (rasterizer && (cs_program.dispatch_initiator & 1)) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("asc[{}]:{}:DispatchDirect", vqid, cmd_address)); - rasterizer->DispatchDirect(); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DispatchDirect(); - } + if (!rasterizer || (cs_program.dispatch_initiator & 1) == 0) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker("asc[{}]:{}:DispatchDirect", + fmt::make_format_args(vqid, cmd_address), + [&] { rasterizer->DispatchDirect(); }); break; } case PM4ItOpcode::DispatchIndirect: { @@ -1099,17 +1055,13 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), cs_program); } - if (rasterizer && (cs_program.dispatch_initiator & 1)) { - const auto cmd_address = reinterpret_cast(header); - if (host_markers_enabled) { - rasterizer->ScopeMarkerBegin( - fmt::format("asc[{}]:{}:DispatchIndirect", vqid, cmd_address)); - rasterizer->DispatchIndirect(ib_address, 0, size); - rasterizer->ScopeMarkerEnd(); - } else { - rasterizer->DispatchIndirect(ib_address, 0, size); - } + if (!rasterizer || (cs_program.dispatch_initiator & 1) == 0) { + break; } + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarker("asc[{}]:{}:DispatchIndirect", + fmt::make_format_args(vqid, cmd_address), + [&] { rasterizer->DispatchIndirect(ib_address, 0, size); }); break; } case PM4ItOpcode::WriteData: { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 249ea2ba6..8ad9d7c0c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -222,6 +222,7 @@ private: Vulkan::Rasterizer* rasterizer{}; Libraries::VideoOut::VideoOutPort* vo_port{}; + const bool guest_markers_enabled; std::jthread process_thread{}; std::atomic num_submits{}; std::atomic num_commands{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ebb31a6f2..10357ae05 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -231,6 +231,8 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(HwStage stage, SwStag case SwStage::Vertex: info.sw.vs.step_rate_0 = regs.vgt_instance_step_rate_0; info.sw.vs.step_rate_1 = regs.vgt_instance_step_rate_1; + info.sw.vs.vertex_sgpr_offset = draw_indirect_params.vertex_sgpr_offset; + info.sw.vs.instance_sgpr_offset = draw_indirect_params.instance_sgpr_offset; info.sw.vs.tess_emulated_primitive = regs.primitive_type == AmdGpu::PrimitiveType::RectList || regs.primitive_type == AmdGpu::PrimitiveType::QuadList; @@ -322,7 +324,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, PipelineCache::~PipelineCache() = default; -const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { +const GraphicsPipeline* PipelineCache::GetGraphicsPipeline(const DrawIndirectParams params) { + draw_indirect_params = params; if (!RefreshGraphicsKey()) { return nullptr; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 34d643de3..03cbc3462 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -63,6 +63,11 @@ struct Program { } }; +struct DrawIndirectParams { + u16 vertex_sgpr_offset; + u32 instance_sgpr_offset; +}; + class PipelineCache { public: explicit PipelineCache(const Instance& instance, Scheduler& scheduler, @@ -76,7 +81,7 @@ public: bool LoadGraphicsPipeline(Serialization::Archive& ar); bool LoadPipelineStage(Serialization::Archive& ar, size_t stage); - const GraphicsPipeline* GetGraphicsPipeline(); + const GraphicsPipeline* GetGraphicsPipeline(const DrawIndirectParams params = {}); const ComputePipeline* GetComputePipeline(); @@ -122,6 +127,7 @@ private: vk::UniquePipelineLayout pipeline_layout; Shader::Profile profile{}; Shader::Pools pools; + DrawIndirectParams draw_indirect_params{}; tsl::robin_map> program_cache; tsl::robin_map> compute_pipelines; tsl::robin_map> graphics_pipelines; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e4428cd0f..ee05a2f20 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -9,6 +9,7 @@ #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_hle.h" @@ -38,7 +39,9 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, buffer_cache{instance, scheduler, liverpool_, texture_cache, page_manager}, texture_cache{instance, scheduler, liverpool_, buffer_cache, page_manager}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, - pipeline_cache{instance, scheduler, liverpool} { + pipeline_cache{instance, scheduler, liverpool}, + host_markers_enabled{EmulatorSettings.IsVkHostMarkersEnabled()}, + guest_markers_enabled{EmulatorSettings.IsVkGuestMarkersEnabled()} { if (!EmulatorSettings.IsNullGPU()) { liverpool->BindRasterizer(this); } @@ -236,7 +239,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { } void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 stride, - u32 max_count, VAddr count_address) { + u32 max_count, VAddr count_address, u16 vertex_sgpr_offset, + u16 instance_sgpr_offset) { RENDERER_TRACE; scheduler.PopPendingOperations(); @@ -245,7 +249,11 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } - const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); + const DrawIndirectParams params = { + .vertex_sgpr_offset = vertex_sgpr_offset, + .instance_sgpr_offset = instance_sgpr_offset, + }; + const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(params); if (!pipeline) { return; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 416588336..ff81db0e5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -44,11 +44,21 @@ public: void Draw(bool is_indexed, u32 index_offset = 0); void DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 size, u32 max_count, - VAddr count_address); + VAddr count_address, u16 vertex_sgpr_offset, u16 instance_sgpr_offset); void DispatchDirect(); void DispatchIndirect(VAddr address, u32 offset, u32 size); + void ScopeMarker(fmt::string_view fmt, fmt::format_args args, auto&& func) { + if (host_markers_enabled) { + ScopeMarkerBegin(fmt::vformat(fmt, args)); + func(); + ScopeMarkerEnd(); + } else { + func(); + } + } + void ScopeMarkerBegin(const std::string_view& str, bool from_guest = false); void ScopeMarkerEnd(bool from_guest = false); void ScopedMarkerInsert(const std::string_view& str, bool from_guest = false); @@ -128,6 +138,8 @@ private: boost::icl::interval_set mapped_ranges; Common::SharedFirstMutex mapped_ranges_mutex; PipelineCache pipeline_cache; + const bool host_markers_enabled; + const bool guest_markers_enabled; using RenderTargetInfo = std::pair; std::array cb_descs;