graphics: apply indirect draw offsets to guest user SGPRs

This commit is contained in:
Claxten
2026-10-07 19:19:14 +04:00
committed by nmzik
parent 5297abb7ac
commit 9f83fd5f0c
5 changed files with 123 additions and 12 deletions
@@ -23,6 +23,12 @@ enum class ContextStateOperation : uint32_t {
PushClear = 3,
};
struct IndirectDrawRegisters {
uint32_t vertex_offset;
uint32_t instance_offset;
uint32_t index_offset;
};
class Pm4Execution {
public:
[[nodiscard]] bool MadeProgress() const noexcept { return m_made_progress; }
@@ -79,10 +85,11 @@ public:
void DrawIndex(DrawIndexArgs args);
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count);
void DrawIndexAuto(DrawAutoArgs args);
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
void DrawIndirect(uint32_t data_offset, IndirectDrawRegisters registers,
uint32_t draw_initiator, bool indexed);
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
uint32_t draw_initiator, bool indexed);
IndirectDrawRegisters registers, uint32_t draw_initiator, bool indexed);
void WriteAtEndOfPipe32(uint32_t cache_policy, uint32_t event_write_dest,
uint32_t eop_event_type, uint32_t cache_action, uint32_t event_index,
uint32_t event_write_source, void* dst_gpu_addr, uint32_t value,
@@ -1641,8 +1641,12 @@ KYTY_CP_OP_PARSER(CpOpDrawIndirect) {
const auto data_offset = buffer[0];
const auto draw_initiator = buffer[3];
const bool indexed = (cmd_id == 0xc0032500);
// The native indexed packet enables the high-half first-index destination in word 3.
const IndirectDrawRegisters registers {
buffer[1] & 0xffffu, buffer[2] & 0xffffu,
indexed && (buffer[2] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP};
cp.DrawIndirect(data_offset, draw_initiator, indexed);
cp.DrawIndirect(data_offset, registers, draw_initiator, indexed);
return 4;
}
@@ -1660,13 +1664,17 @@ KYTY_CP_OP_PARSER(CpOpDrawIndirectMulti) {
const auto stride_in_bytes = buffer[7];
const auto draw_initiator = buffer[8];
const bool indexed = (cmd_id == 0xc0083800);
// Multi-draw moves the first-index enable to word 4; the destinations stay in 2/3.
const IndirectDrawRegisters registers {
buffer[1] & 0xffffu, buffer[2] & 0xffffu,
indexed && (buffer[3] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP};
if (count_indirect == 0) {
count_addr = nullptr;
}
cp.DrawIndirectMulti(data_offset, max_count_or_count, count_addr, stride_in_bytes,
draw_initiator, indexed);
registers, draw_initiator, indexed);
return 9;
}
+25 -4
View File
@@ -875,15 +875,32 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
DrawIndex({.index_count = index_count, .index_addr = index_addr});
}
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters registers,
uint32_t vertex_offset, uint32_t instance_offset,
uint32_t first_index) {
const auto write = [&](uint32_t location, uint32_t value) {
if (location == Pm4::SH_NOP) {
return;
}
EXIT_NOT_IMPLEMENTED(location >= Pm4::SH_NUM ||
g_hw_sh_indirect_func[location] == nullptr);
g_hw_sh_indirect_func[location](cp, location, value);
};
write(registers.vertex_offset, vertex_offset);
write(registers.instance_offset, instance_offset);
write(registers.index_offset, first_index);
}
void CommandProcessor::DrawIndirect(uint32_t data_offset, IndirectDrawRegisters registers,
uint32_t draw_initiator, bool indexed) {
const auto args_size = indexed ? sizeof(DrawIndexedIndirectArgs) : sizeof(DrawIndirectArgs);
DrawIndirectMulti(data_offset, 1, nullptr, args_size, draw_initiator, indexed);
DrawIndirectMulti(data_offset, 1, nullptr, args_size, registers, draw_initiator, indexed);
}
void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
const volatile uint32_t* count_addr,
uint32_t stride_in_bytes, uint32_t draw_initiator,
bool indexed) {
uint32_t stride_in_bytes, IndirectDrawRegisters registers,
uint32_t draw_initiator, bool indexed) {
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != (indexed ? 0u : 2u));
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
@@ -918,6 +935,8 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
if (!indexed) {
auto* args = reinterpret_cast<const DrawIndirectArgs*>(args_addr);
PatchIndirectDrawOffsets(*this, registers, args->start_vertex_location,
args->start_instance_location, 0);
m_num_instances = args->instance_count;
DrawIndexAuto({.vertex_count = args->vertex_count_per_instance,
.instance_count = args->instance_count,
@@ -928,6 +947,8 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
}
auto* args = reinterpret_cast<const DrawIndexedIndirectArgs*>(args_addr);
PatchIndirectDrawOffsets(*this, registers, args->base_vertex_location,
args->start_instance_location, args->start_index_location);
auto* index_addr = reinterpret_cast<const void*>(
m_index_base_addr + static_cast<uint64_t>(args->start_index_location) * index_size);
@@ -1284,8 +1284,10 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
: ResolveDrawOffsets(ucfg.GetIndexOffset(), state.vertex_info[0]);
DrawEmitInfo emit {};
emit.vertex_offset = vertex_offset + args.base_vertex;
emit.first_instance = instance_offset;
// Native mesh prologs consume the offsets written to the packet's user SGPRs.
const bool mesh_indirect = indirect && state.vertex_info[0].logical_stage == ShaderType::Mesh;
emit.vertex_offset = mesh_indirect ? 0 : vertex_offset + args.base_vertex;
emit.first_instance = mesh_indirect ? 0 : instance_offset;
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
primitive_restart);
@@ -1373,8 +1375,10 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
indirect ? std::pair<int32_t, uint32_t> {0, args.first_instance}
: ResolveDrawOffsets(ucfg.GetIndexOffset(), state.vertex_info[0]);
DrawEmitInfo emit {};
emit.first_vertex = static_cast<uint32_t>(vertex_offset + static_cast<int32_t>(args.first_vertex));
emit.first_instance = instance_offset;
const bool mesh_indirect = indirect && state.vertex_info[0].logical_stage == ShaderType::Mesh;
emit.first_vertex = mesh_indirect ? 0 :
static_cast<uint32_t>(vertex_offset + static_cast<int32_t>(args.first_vertex));
emit.first_instance = mesh_indirect ? 0 : instance_offset;
DrawIndexBufferSource index_source {};
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false);