agc: emit native wait-on-address packets

This commit is contained in:
nmzik
2026-08-20 22:45:08 +02:00
parent f119e4baca
commit 86dbf89188
7 changed files with 281 additions and 81 deletions
@@ -216,6 +216,7 @@ constexpr auto MakeOpcodeDispatchTable() {
g_cp_op_func[Pm4::IT_COND_EXEC] = CpOpCondExec;
g_cp_op_func[Pm4::IT_SET_PREDICATION] = CpOpSetPredication;
g_cp_op_func[Pm4::IT_WRITE_DATA] = CpOpWriteData;
g_cp_op_func[Pm4::IT_WAIT_REG_MEM] = CpOpWaitRegMem32;
g_cp_op_func[Pm4::IT_INDIRECT_BUFFER] = CpOpIndirectBuffer;
g_cp_op_func[Pm4::IT_COPY_DATA] = CpOpCopyData;
g_cp_op_func[Pm4::IT_EVENT_WRITE] = CpOpEventWrite;
@@ -238,6 +239,7 @@ constexpr auto MakeOpcodeDispatchTable() {
g_cp_op_func[Pm4::IT_WAIT_ON_CE_COUNTER] = CpOpWaitOnCeCounter;
g_cp_op_func[Pm4::IT_WAIT_ON_DE_COUNTER_DIFF] = CpOpWaitOnDeCounterDiff;
g_cp_op_func[Pm4::IT_GET_LOD_STATS] = CpOpGetLodStats;
g_cp_op_func[Pm4::IT_WAIT_REG_MEM_64] = CpOpWaitRegMem64;
return g_cp_op_func;
}
@@ -246,12 +248,10 @@ constexpr auto MakeCustomOpcodeDispatchTable() {
std::array<cp_op_parser_func_t, Pm4::R_NUM> g_cp_op_custom_func {};
g_cp_op_custom_func[Pm4::R_DISPATCH_RESET] = CpOpDispatchReset;
g_cp_op_custom_func[Pm4::R_WAIT_MEM_32] = CpOpWaitRegMem32;
g_cp_op_custom_func[Pm4::R_WAIT_FLIP_DONE] = CpOpWaitFlipDone;
g_cp_op_custom_func[Pm4::R_PUSH_MARKER] = CpOpPushMarker;
g_cp_op_custom_func[Pm4::R_POP_MARKER] = CpOpPopMarker;
g_cp_op_custom_func[Pm4::R_ACQUIRE_MEM] = CpOpAcquireMem;
g_cp_op_custom_func[Pm4::R_WAIT_MEM_64] = CpOpWaitRegMem64;
g_cp_op_custom_func[Pm4::R_FLIP] = CpOpFlip;
g_cp_op_custom_func[Pm4::R_RELEASE_MEM] = CpOpReleaseMem;
g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState;
@@ -2866,6 +2866,10 @@ KYTY_CP_OP_PARSER(CpOpSetShaderReg) {
KYTY_CP_OP_PARSER(CpOpSetUconfigReg) {
KYTY_PROFILER_FUNCTION();
if (Gen5::AgcIsWaitUserDataPacket(cmd_id, buffer)) {
return KYTY_PM4_LEN(cmd_id) - 1u;
}
auto raw_cmd_offset = buffer[0];
auto cmd_offset = NormalizeRegisterOffset(raw_cmd_offset);
@@ -2956,19 +2960,21 @@ template <typename T>
static uint32_t CpOpWaitRegMemSized(CommandProcessor& cp, uint32_t cmd_id, const uint32_t* buffer) {
static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t));
constexpr auto value_dw = static_cast<uint32_t>(sizeof(T) / sizeof(uint32_t));
constexpr auto value_dw = static_cast<uint32_t>(sizeof(T) / sizeof(uint32_t));
constexpr auto payload_dw = 4u + value_dw * 2u;
constexpr auto packet_dw = payload_dw + 1u;
constexpr auto register_id =
(sizeof(T) == sizeof(uint32_t) ? Pm4::R_WAIT_MEM_32 : Pm4::R_WAIT_MEM_64);
constexpr auto opcode =
(sizeof(T) == sizeof(uint32_t) ? Pm4::IT_WAIT_REG_MEM : Pm4::IT_WAIT_REG_MEM_64);
EXIT_NOT_IMPLEMENTED(KYTY_PM4_R(cmd_id) != register_id);
EXIT_NOT_IMPLEMENTED(((cmd_id >> 8u) & 0xffu) != opcode || KYTY_PM4_R(cmd_id) != 0u);
EXIT_NOT_IMPLEMENTED(KYTY_PM4_LEN(cmd_id) != packet_dw);
auto* addr = reinterpret_cast<const T*>(buffer[0] | (static_cast<uint64_t>(buffer[1]) << 32u));
auto mask = CpOpWaitRegMemReadValue<T>(buffer + 2u);
auto ref = CpOpWaitRegMemReadValue<T>(buffer + 2u + value_dw);
auto ctrl = buffer[2u + value_dw * 2u];
constexpr auto address_align_mask = (sizeof(T) == sizeof(uint32_t) ? 0x3u : 0x7u);
auto ctrl = buffer[0];
auto* addr = reinterpret_cast<const T*>((buffer[1] & ~address_align_mask) |
(static_cast<uint64_t>(buffer[2] & 0x3ffffu) << 32u));
auto ref = CpOpWaitRegMemReadValue<T>(buffer + 3u);
auto mask = CpOpWaitRegMemReadValue<T>(buffer + 3u + value_dw);
auto poll = buffer[3u + value_dw * 2u];
EXIT_NOT_IMPLEMENTED((ctrl & 0x10u) == 0);
+2 -2
View File
@@ -21,12 +21,10 @@ constexpr auto MakeRegisterNames() {
names[R_DRAW_RESET] = "R_DRAW_RESET";
names[R_WAIT_FLIP_DONE] = "R_WAIT_FLIP_DONE";
names[R_DISPATCH_RESET] = "R_DISPATCH_RESET";
names[R_WAIT_MEM_32] = "R_WAIT_MEM_32";
names[R_PUSH_MARKER] = "R_PUSH_MARKER";
names[R_POP_MARKER] = "R_POP_MARKER";
names[R_ACQUIRE_MEM] = "R_ACQUIRE_MEM";
names[R_WRITE_DATA] = "R_WRITE_DATA";
names[R_WAIT_MEM_64] = "R_WAIT_MEM_64";
names[R_FLIP] = "R_FLIP";
names[R_RELEASE_MEM] = "R_RELEASE_MEM";
names[R_DMA_DATA] = "R_DMA_DATA";
@@ -62,6 +60,7 @@ constexpr auto MakeOpcodeNames() {
names[IT_WRITE_DATA] = "IT_WRITE_DATA";
names[IT_MEM_SEMAPHORE] = "IT_MEM_SEMAPHORE";
names[IT_DRAW_INDEX_INDIRECT_MULTI] = "IT_DRAW_INDEX_INDIRECT_MULTI";
names[IT_WAIT_REG_MEM] = "IT_WAIT_REG_MEM";
names[IT_INDIRECT_BUFFER] = "IT_INDIRECT_BUFFER";
names[IT_COPY_DATA] = "IT_COPY_DATA";
names[IT_CP_DMA] = "IT_CP_DMA";
@@ -91,6 +90,7 @@ constexpr auto MakeOpcodeNames() {
names[IT_DISPATCH_DRAW_PREAMBLE] = "IT_DISPATCH_DRAW_PREAMBLE";
names[IT_DISPATCH_DRAW] = "IT_DISPATCH_DRAW";
names[IT_GET_LOD_STATS] = "IT_GET_LOD_STATS";
names[IT_WAIT_REG_MEM_64] = "IT_WAIT_REG_MEM_64";
names[IT_SET_CONTEXT_REG_INDIRECT] = "IT_SET_CONTEXT_REG_INDIRECT";
return names;
}
+2 -2
View File
@@ -42,6 +42,7 @@ constexpr uint32_t IT_WRITE_DATA = 0x37;
constexpr uint32_t IT_MEM_SEMAPHORE = 0x39;
constexpr uint32_t IT_DRAW_INDEX_INDIRECT_MULTI = 0x38;
constexpr uint32_t IT_DISPATCH_DRAW_PREAMBLE = 0x3A;
constexpr uint32_t IT_WAIT_REG_MEM = 0x3C;
constexpr uint32_t IT_INDIRECT_BUFFER = 0x3F;
constexpr uint32_t IT_COPY_DATA = 0x40;
constexpr uint32_t IT_CP_DMA = 0x41;
@@ -70,6 +71,7 @@ constexpr uint32_t IT_WAIT_ON_CE_COUNTER = 0x86;
constexpr uint32_t IT_WAIT_ON_DE_COUNTER_DIFF = 0x88;
constexpr uint32_t IT_DISPATCH_DRAW = 0x8D;
constexpr uint32_t IT_GET_LOD_STATS = 0x8E;
constexpr uint32_t IT_WAIT_REG_MEM_64 = 0x93;
constexpr uint32_t IT_SET_CONTEXT_REG_INDIRECT = 0x9F;
/* Custom codes. Implemented via IT_NOP */
@@ -78,12 +80,10 @@ constexpr uint32_t R_ZERO = 0x00;
constexpr uint32_t R_DRAW_RESET = 0x05;
constexpr uint32_t R_WAIT_FLIP_DONE = 0x06;
constexpr uint32_t R_DISPATCH_RESET = 0x09;
constexpr uint32_t R_WAIT_MEM_32 = 0x0A;
constexpr uint32_t R_PUSH_MARKER = 0x0B;
constexpr uint32_t R_POP_MARKER = 0x0C;
constexpr uint32_t R_ACQUIRE_MEM = 0x14;
constexpr uint32_t R_WRITE_DATA = 0x15;
constexpr uint32_t R_WAIT_MEM_64 = 0x16;
constexpr uint32_t R_FLIP = 0x17;
constexpr uint32_t R_RELEASE_MEM = 0x18;
constexpr uint32_t R_DMA_DATA = 0x19;
+93 -42
View File
@@ -3705,6 +3705,33 @@ uint32_t* KYTY_SYSV_ABI AgcDcbGetLodStats(CommandBuffer* buf, uint8_t cache_poli
return cmd;
}
static constexpr uint32_t AGC_WAIT_USER_DATA_REGISTER = 0x342u;
static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_32_TAG = 0xc8010000u;
static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_64_TAG = 0xc8020000u;
static constexpr uint32_t AGC_WAIT_USER_DATA_END_TAG = 0xc8000000u;
static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_DW = 4u;
static constexpr uint32_t AGC_WAIT_USER_DATA_END_DW = 3u;
static uint32_t* get_agc_wait_packet(uint32_t* cmd) {
if (cmd == nullptr || KYTY_PM4_LEN(cmd[0]) != AGC_WAIT_USER_DATA_BEGIN_DW ||
!AgcIsWaitUserDataPacket(cmd[0], cmd + 1)) {
return nullptr;
}
auto* wait = cmd + AGC_WAIT_USER_DATA_BEGIN_DW;
const auto op = (wait[0] >> 8u) & 0xffu;
const auto len = KYTY_PM4_LEN(wait[0]);
const auto tag = cmd[2] & 0xffff0000u;
if (KYTY_PM4_R(wait[0]) != 0u ||
!((op == Pm4::IT_WAIT_REG_MEM && len == 7u && tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG) ||
(op == Pm4::IT_WAIT_REG_MEM_64 && len == 9u &&
tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG))) {
return nullptr;
}
return wait;
}
int KYTY_SYSV_ABI AgcWaitRegMemPatchAddress(uint32_t* cmd, const volatile void* address) {
PRINT_NAME();
@@ -3712,21 +3739,20 @@ int KYTY_SYSV_ABI AgcWaitRegMemPatchAddress(uint32_t* cmd, const volatile void*
"\t address = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(cmd), reinterpret_cast<uint64_t>(address));
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
auto vaddr = reinterpret_cast<uint64_t>(address);
auto op = (cmd[0] >> 8u) & 0xffu;
if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_32) {
cmd[1] = static_cast<uint32_t>(vaddr) & ~0x3u;
cmd[2] = static_cast<uint32_t>(vaddr >> 32u) & 0x3ffffu;
} else if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_64) {
cmd[1] = static_cast<uint32_t>(vaddr) & ~0x7u;
cmd[2] = static_cast<uint32_t>(vaddr >> 32u) & 0x3ffffu;
} else {
EXIT("unsupported waitOnAddress packet for address patch: 0x%08" PRIx32 "\n", cmd[0]);
auto* wait = get_agc_wait_packet(cmd);
if (wait == nullptr) {
return GRAPHICS5_ERROR_INVALID_PACKET;
}
const auto vaddr = reinterpret_cast<uint64_t>(address);
const auto op = (wait[0] >> 8u) & 0xffu;
const auto align_mask = (op == Pm4::IT_WAIT_REG_MEM ? 0x3u : 0x7u);
cmd[2] = (cmd[2] & 0xffff0000u) | static_cast<uint32_t>((vaddr >> 32u) & 0xffffu);
cmd[3] = static_cast<uint32_t>(vaddr);
wait[2] = (wait[2] & align_mask) | (static_cast<uint32_t>(vaddr) & ~align_mask);
wait[3] = (wait[3] & 0xfffc0000u) |
(static_cast<uint32_t>(vaddr >> 32u) & 0x3ffffu);
return OK;
}
@@ -3737,19 +3763,14 @@ int KYTY_SYSV_ABI AgcWaitRegMemPatchReference(uint32_t* cmd, uint64_t reference)
"\t reference = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(cmd), reference);
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
auto op = (cmd[0] >> 8u) & 0xffu;
if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_32) {
cmd[4] = static_cast<uint32_t>(reference & 0xffffffffu);
} else if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_64) {
cmd[5] = static_cast<uint32_t>(reference & 0xffffffffu);
cmd[6] = static_cast<uint32_t>((reference >> 32u) & 0xffffffffu);
} else {
EXIT("unsupported waitOnAddress packet for reference patch: 0x%08" PRIx32 "\n", cmd[0]);
auto* wait = get_agc_wait_packet(cmd);
if (wait == nullptr) {
return GRAPHICS5_ERROR_INVALID_PACKET;
}
// The native patch helper changes only the low reference DWORD for both packet sizes.
wait[4] = static_cast<uint32_t>(reference);
return OK;
}
@@ -3837,6 +3858,22 @@ static uint32_t wait_reg_mem64_control(uint8_t compare_function, uint8_t op, uin
((static_cast<uint32_t>(cache_policy) & 0x3u) << 25u);
}
bool AgcIsWaitUserDataPacket(uint32_t cmd_id, const uint32_t* payload) {
if (payload == nullptr || ((cmd_id >> 8u) & 0xffu) != Pm4::IT_SET_UCONFIG_REG ||
KYTY_PM4_R(cmd_id) != 1u || payload[0] != AGC_WAIT_USER_DATA_REGISTER) {
return false;
}
const auto size_dw = KYTY_PM4_LEN(cmd_id);
if (size_dw != AGC_WAIT_USER_DATA_BEGIN_DW && size_dw != AGC_WAIT_USER_DATA_END_DW) {
return false;
}
const auto tag = payload[1] & 0xffff0000u;
return (size_dw == AGC_WAIT_USER_DATA_BEGIN_DW &&
(tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG || tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG)) ||
(size_dw == AGC_WAIT_USER_DATA_END_DW && payload[1] == AGC_WAIT_USER_DATA_END_TAG);
}
uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function,
uint8_t op, uint8_t cache_policy,
const volatile void* address, uint64_t reference,
@@ -3872,39 +3909,53 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
auto address_value = reinterpret_cast<uint64_t>(address);
bool wait32 = (size == 0);
auto poll = wait_reg_mem_poll_cycles_to_packet(poll_cycles);
const auto wait_dw = wait32 ? 7u : 9u;
const auto total_dw = AGC_WAIT_USER_DATA_BEGIN_DW + wait_dw + AGC_WAIT_USER_DATA_END_DW;
auto* cmd = buf->AllocateDW(wait32 ? 7 : 9);
auto* cmd = buf->AllocateDW(total_dw);
if (cmd == nullptr) {
return nullptr;
}
cmd[0] = KYTY_PM4(AGC_WAIT_USER_DATA_BEGIN_DW, Pm4::IT_SET_UCONFIG_REG, 1u);
cmd[1] = AGC_WAIT_USER_DATA_REGISTER;
cmd[2] = (wait32 ? AGC_WAIT_USER_DATA_BEGIN_32_TAG : AGC_WAIT_USER_DATA_BEGIN_64_TAG) |
static_cast<uint32_t>((address_value >> 32u) & 0xffffu);
cmd[3] = static_cast<uint32_t>(address_value);
auto* wait = cmd + AGC_WAIT_USER_DATA_BEGIN_DW;
auto* end = wait + wait_dw;
end[0] = KYTY_PM4(AGC_WAIT_USER_DATA_END_DW, Pm4::IT_SET_UCONFIG_REG, 1u);
end[1] = AGC_WAIT_USER_DATA_REGISTER;
end[2] = AGC_WAIT_USER_DATA_END_TAG;
if (wait32) {
cmd[0] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32);
cmd[1] = static_cast<uint32_t>(address_value) & ~0x3u;
cmd[2] = static_cast<uint32_t>(address_value >> 32u) & 0x3ffffu;
cmd[3] = static_cast<uint32_t>(mask & 0xffffffffu);
cmd[4] = static_cast<uint32_t>(reference & 0xffffffffu);
cmd[5] = wait_reg_mem32_control(compare_function, op, cache_policy);
cmd[6] = poll;
wait[0] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0u);
wait[1] = wait_reg_mem32_control(compare_function, op, cache_policy);
wait[2] = static_cast<uint32_t>(address_value) & ~0x3u;
wait[3] = static_cast<uint32_t>(address_value >> 32u) & 0x3ffffu;
wait[4] = static_cast<uint32_t>(reference);
wait[5] = static_cast<uint32_t>(mask);
wait[6] = poll;
return cmd;
}
cmd[0] = KYTY_PM4(9, Pm4::IT_NOP, Pm4::R_WAIT_MEM_64);
cmd[1] = static_cast<uint32_t>(address_value) & ~0x7u;
cmd[2] = static_cast<uint32_t>(address_value >> 32u) & 0x3ffffu;
cmd[3] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(mask) & 0xffffffffu);
cmd[4] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(mask) >> 32u) & 0xffffffffu);
cmd[5] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(reference) & 0xffffffffu);
cmd[6] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(reference) >> 32u) & 0xffffffffu);
cmd[7] = wait_reg_mem64_control(compare_function, op, cache_policy);
cmd[8] = poll;
wait[0] = KYTY_PM4(9, Pm4::IT_WAIT_REG_MEM_64, 0u);
wait[1] = wait_reg_mem64_control(compare_function, op, cache_policy);
wait[2] = static_cast<uint32_t>(address_value) & ~0x7u;
wait[3] = static_cast<uint32_t>(address_value >> 32u) & 0x3ffffu;
wait[4] = static_cast<uint32_t>(reference);
wait[5] = static_cast<uint32_t>(reference >> 32u);
wait[6] = static_cast<uint32_t>(mask);
wait[7] = static_cast<uint32_t>(mask >> 32u);
wait[8] = poll;
return cmd;
}
uint32_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size) {
uint64_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size) {
PRINT_NAME();
switch (size) {
+2 -1
View File
@@ -270,7 +270,8 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
uint8_t op, uint8_t cache_policy,
const volatile void* address, uint64_t reference,
uint64_t mask, uint32_t poll_cycles);
uint32_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size);
uint64_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size);
bool AgcIsWaitUserDataPacket(uint32_t cmd_id, const uint32_t* payload);
uint32_t* KYTY_SYSV_ABI AgcDcbSetMarker(CommandBuffer* buf, const char* str, uint32_t color);
uint32_t* KYTY_SYSV_ABI AgcDcbPushMarker(CommandBuffer* buf, const char* str, uint32_t color);
uint32_t* KYTY_SYSV_ABI AgcDcbPopMarker(CommandBuffer* buf);
+166 -24
View File
@@ -2035,12 +2035,12 @@ public:
commands[2] = static_cast<uint32_t>(address(&prefix));
commands[3] = static_cast<uint32_t>(address(&prefix) >> 32u);
commands[4] = 11;
commands[5] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32);
commands[6] = static_cast<uint32_t>(address(&label));
commands[7] = static_cast<uint32_t>(address(&label) >> 32u);
commands[8] = UINT32_MAX;
commands[5] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0);
commands[6] = 0x10u | 3u;
commands[7] = static_cast<uint32_t>(address(&label));
commands[8] = static_cast<uint32_t>(address(&label) >> 32u);
commands[9] = 1;
commands[10] = 0x10u | 3u;
commands[10] = UINT32_MAX;
commands[12] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
commands[13] = 0;
commands[14] = static_cast<uint32_t>(address(&suffix));
@@ -22184,24 +22184,159 @@ void CheckPm4PolygonOffsetRegisters(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", "Pm4PolygonOffset");
}
struct AgcCommandBufferLayout {
using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t,
void *);
uint32_t *bottom;
uint32_t *top;
uint32_t *cursor_up;
uint32_t *cursor_down;
Callback callback;
void *user_data;
uint32_t reserved_dw;
};
static_assert(offsetof(AgcCommandBufferLayout, cursor_up) == 0x10);
static_assert(offsetof(AgcCommandBufferLayout, reserved_dw) == 0x30);
void CheckAgcWaitPackets(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer);
constexpr auto packet_mismatch = static_cast<int>(0x8a6c000cu);
const auto execute = [&](uint32_t *packet, uint32_t size_dw) {
Pm4Execution execution;
return processor.Process(execution, packet, size_dw) ==
Pm4ProcessResult::Complete;
};
alignas(8) uint32_t label32 = 0x11223344u;
std::array<uint32_t, 16> packet32{};
AgcCommandBufferLayout dcb32{packet32.data(),
packet32.data() + packet32.size(),
packet32.data(),
packet32.data() + packet32.size(),
nullptr,
nullptr,
0};
const auto address32 = reinterpret_cast<uint64_t>(&label32);
auto *emitted32 = Gen5::AgcDcbWaitRegMem(
reinterpret_cast<Gen5::CommandBuffer *>(&dcb32), 0, 3, 0, 0,
&label32, label32, UINT32_MAX, 400);
Require(
"AgcWaitPackets", "native 32-bit packet",
Gen5::AgcDcbWaitOnAddressGetSize(0) == 56 &&
emitted32 == packet32.data() &&
dcb32.cursor_up == packet32.data() + 14 &&
packet32[0] == 0xc0027904u && packet32[1] == 0x342u &&
packet32[2] ==
(0xc8010000u |
(static_cast<uint32_t>(address32 >> 32u) & 0xffffu)) &&
packet32[3] == static_cast<uint32_t>(address32) &&
packet32[4] == 0xc0053c00u && packet32[5] == 0x13u &&
packet32[6] == (static_cast<uint32_t>(address32) & ~0x3u) &&
packet32[7] ==
(static_cast<uint32_t>(address32 >> 32u) & 0x3ffffu) &&
packet32[8] == label32 && packet32[9] == UINT32_MAX &&
packet32[10] == 25u && packet32[11] == 0xc0017904u &&
packet32[12] == 0x342u && packet32[13] == 0xc8000000u &&
execute(packet32.data(), 14),
"32-bit waitOnAddress stream differs from native AGC");
alignas(8) uint32_t patched_label32 = 0xaabbccddu;
const auto patched_address32 = reinterpret_cast<uint64_t>(&patched_label32);
packet32[6] |= 0x2u;
packet32[7] |= 0xa5a40000u;
Require(
"AgcWaitPackets", "native 32-bit patch",
Gen5::AgcWaitRegMemPatchAddress(packet32.data(), &patched_label32) == 0 &&
Gen5::AgcWaitRegMemPatchReference(packet32.data(), patched_label32) == 0 &&
packet32[2] ==
(0xc8010000u |
(static_cast<uint32_t>(patched_address32 >> 32u) & 0xffffu)) &&
packet32[3] == static_cast<uint32_t>(patched_address32) &&
packet32[6] ==
((static_cast<uint32_t>(patched_address32) & ~0x3u) | 0x2u) &&
packet32[7] ==
((static_cast<uint32_t>(patched_address32 >> 32u) & 0x3ffffu) |
0xa5a40000u) &&
packet32[8] == patched_label32 && execute(packet32.data(), 14),
"32-bit native wait patch did not update metadata and hardware packet");
alignas(8) uint64_t label64 = 0x1122334455667788ull;
std::array<uint32_t, 16> packet64{};
AgcCommandBufferLayout dcb64{packet64.data(),
packet64.data() + packet64.size(),
packet64.data(),
packet64.data() + packet64.size(),
nullptr,
nullptr,
0};
const auto address64 = reinterpret_cast<uint64_t>(&label64);
auto *emitted64 = Gen5::AgcDcbWaitRegMem(
reinterpret_cast<Gen5::CommandBuffer *>(&dcb64), 1, 3, 0, 0,
&label64, label64, UINT64_MAX, 400);
Require(
"AgcWaitPackets", "native 64-bit packet",
Gen5::AgcDcbWaitOnAddressGetSize(1) == 64 &&
Gen5::AgcDcbWaitOnAddressGetSize(2) == 0 &&
emitted64 == packet64.data() &&
dcb64.cursor_up == packet64.data() + packet64.size() &&
packet64[0] == 0xc0027904u && packet64[1] == 0x342u &&
packet64[2] ==
(0xc8020000u |
(static_cast<uint32_t>(address64 >> 32u) & 0xffffu)) &&
packet64[3] == static_cast<uint32_t>(address64) &&
packet64[4] == 0xc0079300u && packet64[5] == 0x13u &&
packet64[6] == (static_cast<uint32_t>(address64) & ~0x7u) &&
packet64[7] ==
(static_cast<uint32_t>(address64 >> 32u) & 0x3ffffu) &&
packet64[8] == static_cast<uint32_t>(label64) &&
packet64[9] == static_cast<uint32_t>(label64 >> 32u) &&
packet64[10] == UINT32_MAX && packet64[11] == UINT32_MAX &&
packet64[12] == 25u && packet64[13] == 0xc0017904u &&
packet64[14] == 0x342u && packet64[15] == 0xc8000000u &&
execute(packet64.data(), packet64.size()),
"64-bit waitOnAddress stream differs from native AGC");
alignas(8) uint64_t patched_label64 = 0x11223344aabbccddull;
const auto patched_address64 = reinterpret_cast<uint64_t>(&patched_label64);
packet64[6] |= 0x5u;
packet64[7] |= 0x5a580000u;
Require(
"AgcWaitPackets", "native 64-bit patch",
Gen5::AgcWaitRegMemPatchAddress(packet64.data(), &patched_label64) == 0 &&
Gen5::AgcWaitRegMemPatchReference(packet64.data(),
0xdeadbeefaabbccddull) == 0 &&
packet64[2] ==
(0xc8020000u |
(static_cast<uint32_t>(patched_address64 >> 32u) & 0xffffu)) &&
packet64[3] == static_cast<uint32_t>(patched_address64) &&
packet64[6] ==
((static_cast<uint32_t>(patched_address64) & ~0x7u) | 0x5u) &&
packet64[7] ==
((static_cast<uint32_t>(patched_address64 >> 32u) & 0x3ffffu) |
0x5a580000u) &&
packet64[8] == 0xaabbccddu && packet64[9] == 0x11223344u &&
execute(packet64.data(), packet64.size()),
"64-bit native wait patch did not preserve the native high reference DWORD");
std::array<uint32_t, 16> invalid{};
const uint32_t short_payload = 0x342u;
Require("AgcWaitPackets", "invalid packet",
Gen5::AgcWaitRegMemPatchAddress(invalid.data(), &label32) ==
packet_mismatch &&
Gen5::AgcWaitRegMemPatchReference(invalid.data(), label32) ==
packet_mismatch &&
!Gen5::AgcIsWaitUserDataPacket(
KYTY_PM4(2, Pm4::IT_SET_UCONFIG_REG, 1), &short_payload),
"native wait patch accepted a packet without the AGC metadata prefix");
std::printf("[host] %-32s ok\n", "AgcWaitPackets");
}
void CheckPm4ContextStateOperations(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer);
constexpr auto primitive_type = static_cast<Prospero::PrimitiveType>(0x35u);
struct AgcCommandBufferLayout {
using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t,
void *);
uint32_t *bottom;
uint32_t *top;
uint32_t *cursor_up;
uint32_t *cursor_down;
Callback callback;
void *user_data;
uint32_t reserved_dw;
};
static_assert(offsetof(AgcCommandBufferLayout, cursor_up) == 0x10);
static_assert(offsetof(AgcCommandBufferLayout, reserved_dw) == 0x30);
HW::RenderControl original_control{};
original_control.depth_clear_enable = true;
@@ -22366,12 +22501,12 @@ void CheckPm4WaitResume(RenderContext &renderer) {
child[2] = static_cast<uint32_t>(address(&child_observation));
child[3] = static_cast<uint32_t>(address(&child_observation) >> 32u);
child[4] = 0;
child[5] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32);
child[6] = static_cast<uint32_t>(address(&label));
child[7] = static_cast<uint32_t>(address(&label) >> 32u);
child[8] = UINT32_MAX;
child[5] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0);
child[6] = 0x10u | 3u;
child[7] = static_cast<uint32_t>(address(&label));
child[8] = static_cast<uint32_t>(address(&label) >> 32u);
child[9] = 1;
child[10] = 0x10u | 3u;
child[10] = UINT32_MAX;
std::array<uint32_t, 4> nested{};
nested[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0);
@@ -22578,6 +22713,12 @@ int main(int argc, char **argv) {
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--agc-wait-only") == 0) {
VulkanHarness vulkan;
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckPm4WaitResume(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--rewind-only") == 0) {
VulkanHarness vulkan;
CheckPm4RewindResume(vulkan.RuntimeRenderer());
@@ -22741,6 +22882,7 @@ int main(int argc, char **argv) {
CheckPm4DirectShaderRegisterFallback(vulkan.RuntimeRenderer());
CheckPm4GuardBandRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
CheckPm4WaitResume(vulkan.RuntimeRenderer());
CheckPm4RewindResume(vulkan.RuntimeRenderer());