graphics: apply indirect draw offsets to guest user SGPRs
This commit is contained in:
parent
5297abb7ac
commit
9f83fd5f0c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -40613,6 +40613,77 @@ void CheckPm4DrawIndirectMultiPacket(RenderContext &renderer) {
|
|||
}
|
||||
}
|
||||
renderer.GetCommandScheduler().Finish();
|
||||
processor.BufferInit();
|
||||
auto &shaders = processor.GetShCtx();
|
||||
const auto &gs = shaders.GetVs().gs_user_sgpr.value;
|
||||
const auto &hs = shaders.GetVs().hs_user_sgpr.value;
|
||||
const auto check_offsets = [&](auto &commands, const auto &sgprs,
|
||||
std::initializer_list<uint32_t> expected_offsets,
|
||||
const char *label) {
|
||||
Require("Pm4DrawIndirectMulti", label,
|
||||
execute(commands.data(), commands.size()) &&
|
||||
std::equal(expected_offsets.begin(), expected_offsets.end(), sgprs + 9),
|
||||
"indirect draw did not write the expected user SGPR offsets");
|
||||
};
|
||||
// These locations match the captured NGG prolog: user SGPR9/10 become s17/s18.
|
||||
shaders.SetGsUserSgpr(9, 17821, HW::UserSgprType::Unknown);
|
||||
shaders.SetGsUserSgpr(10, 0x898f3f24u, HW::UserSgprType::Unknown);
|
||||
arguments = {0, 1};
|
||||
std::array<uint32_t, 5> indexed_packet{
|
||||
KYTY_PM4(5, Pm4::IT_DRAW_INDEX_INDIRECT, 0), 0,
|
||||
Pm4::SPI_SHADER_USER_DATA_GS_0 + 9,
|
||||
Pm4::SPI_SHADER_USER_DATA_GS_0 + 10, 0};
|
||||
check_offsets(indexed_packet, gs, {0, 0}, "clear stale offsets");
|
||||
arguments = {0, 1, 5, static_cast<uint32_t>(-17), 3};
|
||||
indexed_packet[2] |= (Pm4::SPI_SHADER_USER_DATA_GS_0 + 11) << 16u;
|
||||
// Native sceAgcDcbDrawIndexIndirect (0x5a00) emits first-index enable in word3 bit28.
|
||||
indexed_packet[3] |= 1u << 28u;
|
||||
check_offsets(indexed_packet, gs, {static_cast<uint32_t>(-17), 3, 5}, "indexed offsets");
|
||||
indexed_packet[3] ^= (1u << 28u) | (1u << 27u);
|
||||
arguments[2] = 6;
|
||||
check_offsets(indexed_packet, gs, {static_cast<uint32_t>(-17), 3, 5},
|
||||
"index destination disabled");
|
||||
|
||||
arguments = {0, 1, 31, 5};
|
||||
std::array<uint32_t, 5> auto_packet{
|
||||
KYTY_PM4(5, Pm4::IT_DRAW_INDIRECT, 0), 0,
|
||||
Pm4::SPI_SHADER_USER_DATA_HS_0 + 9,
|
||||
Pm4::SPI_SHADER_USER_DATA_HS_0 + 10, 2};
|
||||
check_offsets(auto_packet, hs, {31, 5}, "non-indexed offsets");
|
||||
Require("Pm4DrawIndirectMulti", "shader stage", gs[9] == static_cast<uint32_t>(-17),
|
||||
"non-indexed draw patched the wrong shader stage");
|
||||
|
||||
arguments = {0, 1, 6, 41, 7, 0, 1, 8, 43, 9, 0, 1, 10, 45, 11};
|
||||
uint32_t count = 2;
|
||||
const auto multi_count_address = reinterpret_cast<uint64_t>(&count);
|
||||
// Native sceAgcDcbDrawIndexIndirectMulti (0x5800) puts this enable in word4 bit28.
|
||||
std::array<uint32_t, 10> multi_packet{
|
||||
KYTY_PM4(10, Pm4::IT_DRAW_INDEX_INDIRECT_MULTI, 0), 0,
|
||||
indexed_packet[2], Pm4::SPI_SHADER_USER_DATA_GS_0 + 10,
|
||||
(1u << 30u) | (1u << 28u) | Pm4::SH_NOP, 3,
|
||||
static_cast<uint32_t>(multi_count_address),
|
||||
static_cast<uint32_t>(multi_count_address >> 32u), 20, 0};
|
||||
check_offsets(multi_packet, gs, {43, 9, 8}, "counted multi-draw offsets");
|
||||
count = 0;
|
||||
check_offsets(multi_packet, gs, {43, 9, 8}, "empty multi-draw");
|
||||
count = 3;
|
||||
multi_packet[5] = 1;
|
||||
multi_packet[3] |= 1u << 28u;
|
||||
multi_packet[4] &= ~(1u << 28u);
|
||||
check_offsets(multi_packet, gs, {41, 7, 8}, "multi-draw enable word and count clamp");
|
||||
|
||||
arguments = {0, 1, 51, 13, 0, 1, 53, 15};
|
||||
std::array<uint32_t, 10> auto_multi_packet{
|
||||
KYTY_PM4(10, Pm4::IT_DRAW_INDIRECT_MULTI, 0), 0,
|
||||
auto_packet[2], auto_packet[3], Pm4::SH_NOP, 2, 0, 0, 16, 2};
|
||||
check_offsets(auto_multi_packet, hs, {53, 15}, "non-indexed multi-draw offsets");
|
||||
arguments = {0, 1, 99, 101};
|
||||
auto_packet[2] = Pm4::SH_NOP;
|
||||
check_offsets(auto_packet, hs, {53, 101}, "independent destinations");
|
||||
auto_packet[3] = Pm4::SH_NOP;
|
||||
arguments[3] = 103;
|
||||
check_offsets(auto_packet, hs, {53, 101}, "disabled destinations");
|
||||
processor.BufferFlush();
|
||||
std::printf("[host] %-32s ok\n", "Pm4DrawIndirectMulti");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue