agc: encode acquire-memory byte ranges in standard packets

This commit is contained in:
nmzik
2026-10-15 23:34:42 +02:00
parent c8caa27936
commit a5ceb48184
6 changed files with 75 additions and 68 deletions
@@ -231,7 +231,6 @@ constexpr auto MakeCustomOpcodeDispatchTable() {
g_cp_op_custom_func[Pm4::R_WAIT_FLIP_DONE] = CpOpWaitFlipDone; 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_PUSH_MARKER] = CpOpPushMarker;
g_cp_op_custom_func[Pm4::R_POP_MARKER] = CpOpPopMarker; 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_FLIP] = CpOpFlip; 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_RELEASE_MEM] = CpOpReleaseMem;
g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState; g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState;
@@ -1290,8 +1290,8 @@ KYTY_HW_UC_PARSER(HwUcSetGdsOaRegisters) {
KYTY_CP_OP_PARSER(CpOpAcquireMem) { KYTY_CP_OP_PARSER(CpOpAcquireMem) {
KYTY_PROFILER_FUNCTION(); KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0055800 && cmd_id != 0xc0061050); EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0055800 && cmd_id != 0xc0065800);
return (cmd_id == 0xc0061050 ? 7 : 6); return KYTY_PM4_LEN(cmd_id) - 1;
} }
KYTY_CP_OP_PARSER(CpOpDispatchDirect) { KYTY_CP_OP_PARSER(CpOpDispatchDirect) {
-1
View File
@@ -23,7 +23,6 @@ constexpr auto MakeRegisterNames() {
names[R_DISPATCH_RESET] = "R_DISPATCH_RESET"; names[R_DISPATCH_RESET] = "R_DISPATCH_RESET";
names[R_PUSH_MARKER] = "R_PUSH_MARKER"; names[R_PUSH_MARKER] = "R_PUSH_MARKER";
names[R_POP_MARKER] = "R_POP_MARKER"; names[R_POP_MARKER] = "R_POP_MARKER";
names[R_ACQUIRE_MEM] = "R_ACQUIRE_MEM";
names[R_WRITE_DATA] = "R_WRITE_DATA"; names[R_WRITE_DATA] = "R_WRITE_DATA";
names[R_FLIP] = "R_FLIP"; names[R_FLIP] = "R_FLIP";
names[R_RELEASE_MEM] = "R_RELEASE_MEM"; names[R_RELEASE_MEM] = "R_RELEASE_MEM";
-1
View File
@@ -86,7 +86,6 @@ constexpr uint32_t R_WAIT_FLIP_DONE = 0x06;
constexpr uint32_t R_DISPATCH_RESET = 0x09; constexpr uint32_t R_DISPATCH_RESET = 0x09;
constexpr uint32_t R_PUSH_MARKER = 0x0B; constexpr uint32_t R_PUSH_MARKER = 0x0B;
constexpr uint32_t R_POP_MARKER = 0x0C; 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_WRITE_DATA = 0x15;
constexpr uint32_t R_FLIP = 0x17; constexpr uint32_t R_FLIP = 0x17;
constexpr uint32_t R_RELEASE_MEM = 0x18; constexpr uint32_t R_RELEASE_MEM = 0x18;
+13 -39
View File
@@ -3022,39 +3022,8 @@ uint32_t* KYTY_SYSV_ABI AgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uin
engine, cb_db_op, gcr_cntl, reinterpret_cast<uint64_t>(base), size_bytes, poll_cycles); engine, cb_db_op, gcr_cntl, reinterpret_cast<uint64_t>(base), size_bytes, poll_cycles);
} }
bool no_size = (static_cast<int64_t>(size_bytes) == -1);
auto vaddr = reinterpret_cast<uint64_t>(base);
EXIT_NOT_IMPLEMENTED(buf == nullptr); EXIT_NOT_IMPLEMENTED(buf == nullptr);
static std::atomic<uint32_t> warning_log_count {0};
const bool log_warning = (warning_log_count.fetch_add(1) < 64);
if (!no_size && (size_bytes & 0xffu) != 0) {
if (log_warning) {
LOGF_COLOR(Log::Color::Red, "\t warning: size_bytes is not 256-byte aligned\n");
}
}
if (!no_size && (size_bytes >> 40u) != 0) {
if (log_warning) {
LOGF_COLOR(Log::Color::Red, "\t warning: size_bytes is too large\n");
}
}
if ((vaddr & 0xffu) != 0) {
if (log_warning) {
LOGF_COLOR(Log::Color::Red, "\t warning: base is not 256-byte aligned\n");
}
}
if ((vaddr >> 40u) != 0) {
if (log_warning) {
LOGF_COLOR(Log::Color::Red, "\t warning: base is too high\n");
}
}
if (engine > 1) {
if (log_warning) {
LOGF_COLOR(Log::Color::Red, "\t warning: unsupported engine 0x%02" PRIx8 "\n", engine);
}
}
auto* cmd = buf->AllocateDW(8); auto* cmd = buf->AllocateDW(8);
if (cmd == nullptr) { if (cmd == nullptr) {
@@ -3062,14 +3031,19 @@ uint32_t* KYTY_SYSV_ABI AgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uin
return nullptr; return nullptr;
} }
cmd[0] = KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_ACQUIRE_MEM); constexpr uint64_t address_mask = 0xffffffffffffull;
cmd[1] = (static_cast<uint32_t>(engine & 1u) << 31u) | cb_db_op; const auto vaddr = reinterpret_cast<uint64_t>(base);
cmd[2] = (no_size ? 0 : static_cast<uint32_t>(size_bytes >> 8u)); // Cover the byte range in 256-byte units, bounded by the 48-bit address space.
cmd[3] = 0; const auto size = std::min((((vaddr & 0xffu) + size_bytes) & address_mask) + 0xffu,
(address_mask & ~uint64_t {0xff}) - vaddr) >> 8u;
cmd[0] = KYTY_PM4(8, Pm4::IT_ACQUIRE_MEM, 0u);
cmd[1] = (static_cast<uint32_t>(engine) << 31u) | (cb_db_op & 0x06007fc0u);
cmd[2] = static_cast<uint32_t>(size);
cmd[3] = static_cast<uint32_t>((size >> 32u) & 0xffu);
cmd[4] = static_cast<uint32_t>(vaddr >> 8u); cmd[4] = static_cast<uint32_t>(vaddr >> 8u);
cmd[5] = 0; cmd[5] = static_cast<uint32_t>(vaddr >> 40u);
cmd[6] = poll_cycles / 40; cmd[6] = std::min(poll_cycles >> 4u, 0xffffu);
cmd[7] = gcr_cntl; cmd[7] = gcr_cntl & 0x7ffffu;
return cmd; return cmd;
} }
@@ -3083,7 +3057,7 @@ uint32_t KYTY_SYSV_ABI AgcDcbAcquireMemGetSize() {
uint32_t* KYTY_SYSV_ABI AgcAcbAcquireMem(CommandBuffer* buf, uint32_t gcr_cntl, uint32_t* KYTY_SYSV_ABI AgcAcbAcquireMem(CommandBuffer* buf, uint32_t gcr_cntl,
const volatile void* base, uint64_t size_bytes, const volatile void* base, uint64_t size_bytes,
uint32_t poll_cycles) { uint32_t poll_cycles) {
return AgcDcbAcquireMem(buf, 1, 0, gcr_cntl, base, size_bytes, poll_cycles); return AgcDcbAcquireMem(buf, 0, 0, gcr_cntl, base, size_bytes, poll_cycles);
} }
uint32_t KYTY_SYSV_ABI AgcAcbAcquireMemGetSize() { uint32_t KYTY_SYSV_ABI AgcAcbAcquireMemGetSize() {
+60 -24
View File
@@ -45742,29 +45742,6 @@ void CheckVulkan13FeatureRequirements() {
std::printf("[host] %-32s ok\n", "Vulkan13FeatureRequirements"); std::printf("[host] %-32s ok\n", "Vulkan13FeatureRequirements");
} }
void CheckPm4AcquireMemNoOp(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
const std::array<uint32_t, 6> standard_payload{0x00400000u, 1u, 0u,
0u, 0u, 10u};
const std::array<uint32_t, 7> custom_payload{
0xdeadbeefu, 0xffffffffu, 0x80000000u, 0x13579bdfu,
0x2468ace0u, 0xaaaaaaaau, 0x55555555u};
const auto standard_before = standard_payload;
const auto custom_before = custom_payload;
Require("Pm4AcquireMemNoOp", "recognized packets",
CpOpAcquireMem(processor, 0xC0055800u, standard_payload.data(), 0,
0) == 6 &&
CpOpAcquireMem(processor, 0xc0061050u, custom_payload.data(), 0,
0) == 7 &&
standard_payload == standard_before &&
custom_payload == custom_before,
"ACQUIRE_MEM did not consume both pinned packet forms as "
"side-effect-free "
"no-ops");
std::printf("[host] %-32s ok\n", "Pm4AcquireMemNoOp");
}
void CheckPm4SyntheticOcclusionCounterDump(RenderContext &renderer) { void CheckPm4SyntheticOcclusionCounterDump(RenderContext &renderer) {
GraphicsInitJmpTables(); GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0); CommandProcessor processor(renderer, 0);
@@ -46531,6 +46508,64 @@ struct CommandBufferLayout {
static_assert(offsetof(CommandBufferLayout, cursor_up) == 0x10); static_assert(offsetof(CommandBufferLayout, cursor_up) == 0x10);
static_assert(offsetof(CommandBufferLayout, reserved_dw) == 0x30); static_assert(offsetof(CommandBufferLayout, reserved_dw) == 0x30);
void CheckPm4AcquireMemPackets(RenderContext &renderer) {
constexpr const char *name = "Pm4AcquireMemPackets";
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
// Byte ranges cover every touched 256-byte block, including partial first and last blocks.
struct Case {
const char *label;
bool async;
uint8_t engine;
uint32_t cb_db, gcr;
uint64_t base, size;
uint32_t poll;
std::array<uint32_t, 8> expected;
};
const Case cases[]{
{"captured byte range", false, 1, 0, 0x9000, 0x2036000308ull, 0x20, 400,
{0xc0065800, 0x80000000, 1, 0, 0x20360003, 0, 25, 0x9000}},
{"crossing high address", false, 0, 0x7fc0, 0x20, 0xab12345678f0ull, 0x40, 400,
{0xc0065800, 0x7fc0, 2, 0, 0x12345678, 0xab, 25, 0x20}},
{"full range", false, 0, 0x7fc0, 0x20, 0, UINT64_MAX, 400,
{0xc0065800, 0x7fc0, UINT32_MAX, 0xff, 0, 0, 25, 0x20}},
{"wide range and saturated poll", false, 1, 0, 0x380, 0, 0x10000000000ull, UINT32_MAX,
{0xc0065800, 0x80000000, 0, 1, 0, 0, 0xffff, 0x380}},
{"async byte range", true, 0, 0, 0x9000, 0x2036000308ull, 0x20, 400,
{0xc0065800, 0, 1, 0, 0x20360003, 0, 25, 0x9000}},
};
Require(name, "packet sizes", Gen5::AgcDcbAcquireMemGetSize() == 32 &&
Gen5::AgcAcbAcquireMemGetSize() == 32,
"native single ACQUIRE_MEM packet size changed");
for (const auto &test : cases) {
std::array<uint32_t, 8> packet{};
CommandBufferLayout layout{packet.data(), packet.data() + packet.size(),
packet.data(), packet.data() + packet.size(),
nullptr, nullptr, 0};
auto *buffer = reinterpret_cast<Gen5::CommandBuffer *>(&layout);
const auto *base = reinterpret_cast<const volatile void *>(test.base);
auto *emitted = test.async
? Gen5::AgcAcbAcquireMem(buffer, test.gcr, base, test.size, test.poll)
: Gen5::AgcDcbAcquireMem(buffer, test.engine, test.cb_db, test.gcr,
base, test.size, test.poll);
Require(name, test.label, emitted == packet.data() &&
layout.cursor_up == packet.data() + packet.size() && packet == test.expected,
"ACQUIRE_MEM differs from the pinned native packet");
const auto tick = renderer.GetCommandScheduler().CurrentTick();
Pm4Execution execution;
Require(name, test.label,
processor.Process(execution, packet) == Pm4ProcessResult::Complete &&
packet == test.expected && renderer.GetCommandScheduler().CurrentTick() == tick,
"native ACQUIRE_MEM was not consumed without mutation or queue submission");
}
const std::array<uint32_t, 7> ps4_packet{0xc0055800, 0x00400000, 1, 0, 0, 0, 10};
Pm4Execution execution;
Require(name, "native PS4 packet",
processor.Process(execution, ps4_packet) == Pm4ProcessResult::Complete,
"seven-DWORD ACQUIRE_MEM was not consumed");
std::printf("[host] %-32s ok\n", name);
}
void CheckPm4WaitPackets(RenderContext &renderer) { void CheckPm4WaitPackets(RenderContext &renderer) {
GraphicsInitJmpTables(); GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0); CommandProcessor processor(renderer, 0);
@@ -48240,6 +48275,7 @@ int main(int argc, char **argv) {
} }
if (argc == 2 && std::strcmp(argv[1], "--pm4-wait-only") == 0) { if (argc == 2 && std::strcmp(argv[1], "--pm4-wait-only") == 0) {
VulkanHarness vulkan; VulkanHarness vulkan;
CheckPm4AcquireMemPackets(vulkan.RuntimeRenderer());
CheckPm4WaitPackets(vulkan.RuntimeRenderer()); CheckPm4WaitPackets(vulkan.RuntimeRenderer());
CheckPm4WaitResume(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer());
return 0; return 0;
@@ -48512,7 +48548,7 @@ int main(int argc, char **argv) {
CheckErrorDialogLifecycle(); CheckErrorDialogLifecycle();
CheckSigninDialogLifecycle(); CheckSigninDialogLifecycle();
CheckVulkan13FeatureRequirements(); CheckVulkan13FeatureRequirements();
CheckPm4AcquireMemNoOp(vulkan.RuntimeRenderer()); CheckPm4AcquireMemPackets(vulkan.RuntimeRenderer());
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer()); CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
CheckPm4Predication(vulkan.RuntimeRenderer()); CheckPm4Predication(vulkan.RuntimeRenderer());
CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer()); CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer());