mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-11-12 09:10:21 +01:00
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
This commit is contained in:
1 parent
f42f72d649
commit
a5d307ee7a
15 files changed
+203
-161
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#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<u32, IR::VectorReg> vgpr_map;
|
||||
std::array<IR::Attribute, MaxInterpVgpr> vgpr_to_interp{};
|
||||
std::optional<FetchShaderData> fetch_data{};
|
||||
bool opcode_missing = false;
|
||||
u32 pc{};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,8 @@ enum class Attribute : u64 {
|
||||
SampleMask = 95,
|
||||
PackedAncillary = 96,
|
||||
SubgroupLtMask = 97,
|
||||
BaseVertex = 98,
|
||||
BaseInstance = 99,
|
||||
Max,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -71,6 +71,8 @@ using OutputMap = std::array<Output, 4>;
|
||||
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;
|
||||
|
||||
@@ -65,7 +65,7 @@ static std::span<const u32> NextPacket(std::span<const u32> 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<const u32> dcb, std::span<c
|
||||
ce_task = ProcessCeUpdate(ccb);
|
||||
RESUME_GFX(ce_task);
|
||||
}
|
||||
const bool host_markers_enabled = rasterizer && EmulatorSettings.IsVkHostMarkersEnabled();
|
||||
const bool guest_markers_enabled = rasterizer && EmulatorSettings.IsVkGuestMarkersEnabled();
|
||||
|
||||
const auto base_addr = reinterpret_cast<uintptr_t>(dcb.data());
|
||||
while (!dcb.empty()) {
|
||||
@@ -427,16 +425,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
if (DebugState.DumpingCurrentReg()) {
|
||||
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||
}
|
||||
if (rasterizer) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast<uintptr_t>(header),
|
||||
cs_program);
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> dcb, std::span<c
|
||||
DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast<uintptr_t>(header),
|
||||
cs_program);
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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 <bool is_indirect>
|
||||
Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> 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<const u32> acb, u32 vqid) {
|
||||
DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast<uintptr_t>(header),
|
||||
cs_program);
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(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<const u32> acb, u32 vqid) {
|
||||
DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast<uintptr_t>(header),
|
||||
cs_program);
|
||||
}
|
||||
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
|
||||
const auto cmd_address = reinterpret_cast<const void*>(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<const void*>(header);
|
||||
rasterizer->ScopeMarker("asc[{}]:{}:DispatchIndirect",
|
||||
fmt::make_format_args(vqid, cmd_address),
|
||||
[&] { rasterizer->DispatchIndirect(ib_address, 0, size); });
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::WriteData: {
|
||||
|
||||
@@ -222,6 +222,7 @@ private:
|
||||
|
||||
Vulkan::Rasterizer* rasterizer{};
|
||||
Libraries::VideoOut::VideoOutPort* vo_port{};
|
||||
const bool guest_markers_enabled;
|
||||
std::jthread process_thread{};
|
||||
std::atomic<u32> num_submits{};
|
||||
std::atomic<u32> num_commands{};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<size_t, std::unique_ptr<Program>> program_cache;
|
||||
tsl::robin_map<ComputePipelineKey, std::unique_ptr<ComputePipeline>> compute_pipelines;
|
||||
tsl::robin_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_pipelines;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<VAddr> mapped_ranges;
|
||||
Common::SharedFirstMutex mapped_ranges_mutex;
|
||||
PipelineCache pipeline_cache;
|
||||
const bool host_markers_enabled;
|
||||
const bool guest_markers_enabled;
|
||||
|
||||
using RenderTargetInfo = std::pair<VideoCore::ImageId, VideoCore::TextureCache::ImageDesc>;
|
||||
std::array<RenderTargetInfo, AmdGpu::NUM_COLOR_BUFFERS> cb_descs;
|
||||
|
||||
Reference in new issue
Block a user