mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 05:16:56 +01:00
agc: emit async memory semaphores and execute them in the CP
This commit is contained in:
@@ -98,6 +98,7 @@ public:
|
||||
|
||||
template <typename T>
|
||||
void WaitRegMem(uint32_t func, const T* addr, T ref, T mask, uint32_t poll, uint32_t wait_op);
|
||||
void MemSemaphore(uint64_t* counter, bool signal, bool write);
|
||||
void WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw_num, uint32_t write_control);
|
||||
void WriteReferenceClock(uint64_t dst_address, uint32_t num_bytes);
|
||||
void DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cache_policy,
|
||||
|
||||
@@ -202,6 +202,7 @@ constexpr auto MakeOpcodeDispatchTable() {
|
||||
g_cp_op_func[Pm4::IT_DMA_DATA] = CpOpDmaData;
|
||||
g_cp_op_func[Pm4::IT_ACQUIRE_MEM] = CpOpAcquireMem;
|
||||
g_cp_op_func[Pm4::IT_RELEASE_MEM] = CpOpReleaseMem;
|
||||
g_cp_op_func[Pm4::IT_MEM_SEMAPHORE] = CpOpMemSemaphore;
|
||||
g_cp_op_func[Pm4::IT_REWIND] = CpOpRewind;
|
||||
g_cp_op_func[Pm4::IT_SET_CONTEXT_REG] = CpOpSetContextReg;
|
||||
g_cp_op_func[Pm4::IT_SET_SH_REG] = CpOpSetShaderReg;
|
||||
|
||||
@@ -136,6 +136,7 @@ uint32_t CpOpEventWrite(CommandProcessor&, uint32_t, const uint32_t*, uint32_t,
|
||||
uint32_t CpOpDmaData(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpAcquireMem(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpReleaseMem(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpMemSemaphore(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpSetContextReg(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpSetShaderReg(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpSetUconfigReg(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
|
||||
@@ -2060,6 +2060,21 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
KYTY_CP_OP_PARSER(CpOpMemSemaphore) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
const auto control = buffer[2];
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0023900 || (control & ~0xe0110000u) != 0 ||
|
||||
(control >> 29u) < 6u);
|
||||
|
||||
auto* counter = reinterpret_cast<uint64_t*>(
|
||||
(buffer[0] & ~0x7u) | (static_cast<uint64_t>(buffer[1] & 0x3ffffu) << 32u));
|
||||
// A signal updates the counter before the next packet, so its mailbox stall never waits.
|
||||
cp.MemSemaphore(counter, (control >> 29u) == 6u, ((control >> 20u) & 0x1u) != 0);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
KYTY_CP_OP_PARSER(CpOpSetContextReg) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
|
||||
@@ -281,6 +281,16 @@ template void CommandProcessor::WaitRegMem<uint32_t>(uint32_t, const uint32_t*,
|
||||
template void CommandProcessor::WaitRegMem<uint64_t>(uint32_t, const uint64_t*, uint64_t, uint64_t,
|
||||
uint32_t, uint32_t);
|
||||
|
||||
void CommandProcessor::MemSemaphore(uint64_t* counter, bool signal, bool write) {
|
||||
if (signal) {
|
||||
*counter = write ? 1u : *counter + 1u;
|
||||
} else if (*counter == 0) {
|
||||
SuspendPm4();
|
||||
} else {
|
||||
(*counter)--;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw_num,
|
||||
uint32_t write_control) {
|
||||
const uint32_t dst_sel = ((write_control >> 30u) & 0x1u) | ((write_control >> 7u) & 0x1eu);
|
||||
|
||||
+42
-2
@@ -2480,6 +2480,9 @@ static constexpr uint32_t AGC_DRAW_INDIRECT_MULTI_END_TAG = 0xc6000000u;
|
||||
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_MEM_SEMAPHORE_SIGNAL_BEGIN_TAG = 0xc9010000u;
|
||||
static constexpr uint32_t AGC_MEM_SEMAPHORE_WAIT_BEGIN_TAG = 0xc9020000u;
|
||||
static constexpr uint32_t AGC_MEM_SEMAPHORE_END_TAG = 0xc9000000u;
|
||||
static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_DW = 4u;
|
||||
static constexpr uint32_t AGC_INTERNAL_DATA_PACKET_SIZE_DW = 3u;
|
||||
|
||||
@@ -3077,6 +3080,42 @@ uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
|
||||
poll_cycles);
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbMemSemaphore(CommandBuffer* buf, const volatile void* address,
|
||||
uint8_t mailbox, uint8_t signal_type, uint8_t op) {
|
||||
EXIT_NOT_IMPLEMENTED(op != 6u && op != 7u);
|
||||
|
||||
auto* cmd =
|
||||
buf->AllocateDW(AGC_WAIT_USER_DATA_BEGIN_DW + AGC_INTERNAL_DATA_PACKET_SIZE_DW + 4u);
|
||||
if (cmd == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto address_value = reinterpret_cast<uint64_t>(address);
|
||||
const bool signal = (op == 6u);
|
||||
cmd[0] = KYTY_PM4(AGC_WAIT_USER_DATA_BEGIN_DW, Pm4::IT_SET_UCONFIG_REG, 1u);
|
||||
cmd[1] = AGC_INTERNAL_DATA_REGISTER;
|
||||
cmd[2] = (signal ? AGC_MEM_SEMAPHORE_SIGNAL_BEGIN_TAG : AGC_MEM_SEMAPHORE_WAIT_BEGIN_TAG) |
|
||||
static_cast<uint32_t>((address_value >> 32u) & 0xffffu);
|
||||
cmd[3] = static_cast<uint32_t>(address_value);
|
||||
|
||||
// A signal pads with a NOP header where a wait ends with its end marker.
|
||||
auto* semaphore = cmd + AGC_WAIT_USER_DATA_BEGIN_DW;
|
||||
if (signal) {
|
||||
semaphore[0] = KYTY_PM4(AGC_INTERNAL_DATA_PACKET_SIZE_DW, Pm4::IT_NOP, 0u);
|
||||
semaphore += AGC_INTERNAL_DATA_PACKET_SIZE_DW;
|
||||
}
|
||||
semaphore[0] = KYTY_PM4(4, Pm4::IT_MEM_SEMAPHORE, 0u);
|
||||
semaphore[1] = static_cast<uint32_t>(address_value) & ~0x7u;
|
||||
semaphore[2] = static_cast<uint32_t>(address_value >> 32u);
|
||||
semaphore[3] = (static_cast<uint32_t>(op) << 29u) | ((signal_type & 0x1u) << 20u) |
|
||||
((mailbox & 0x1u) << 16u);
|
||||
if (!signal) {
|
||||
write_agc_internal_data_packet(semaphore + 4, AGC_MEM_SEMAPHORE_END_TAG);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbWaitOnAddressGetSize(uint8_t size) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -3840,11 +3879,12 @@ bool AgcIsInternalDataPacket(uint32_t cmd_id, const uint32_t* payload) {
|
||||
const auto size_dw = KYTY_PM4_LEN(cmd_id);
|
||||
if (size_dw == AGC_WAIT_USER_DATA_BEGIN_DW) {
|
||||
const auto tag = payload[1] & 0xffff0000u;
|
||||
return tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG || tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG;
|
||||
return tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG || tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG ||
|
||||
tag == AGC_MEM_SEMAPHORE_SIGNAL_BEGIN_TAG || tag == AGC_MEM_SEMAPHORE_WAIT_BEGIN_TAG;
|
||||
}
|
||||
|
||||
return size_dw == AGC_INTERNAL_DATA_PACKET_SIZE_DW &&
|
||||
(payload[1] == AGC_WAIT_USER_DATA_END_TAG ||
|
||||
(payload[1] == AGC_WAIT_USER_DATA_END_TAG || payload[1] == AGC_MEM_SEMAPHORE_END_TAG ||
|
||||
payload[1] == AGC_DRAW_INDIRECT_MULTI_BEGIN_TAG ||
|
||||
payload[1] == AGC_DRAW_INDIRECT_MULTI_END_TAG);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,8 @@ uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
|
||||
uint8_t cache_policy, const volatile void* address,
|
||||
uint64_t reference, uint64_t mask, uint32_t poll_cycles);
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbWaitOnAddressGetSize(uint8_t size);
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbMemSemaphore(CommandBuffer* buf, const volatile void* address,
|
||||
uint8_t mailbox, uint8_t signal_type, uint8_t op);
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbDmaData(CommandBuffer* buf, uint8_t dst, uint8_t dst_cache_policy,
|
||||
uint64_t dst_address_or_offset, uint8_t src,
|
||||
uint8_t src_cache_policy,
|
||||
|
||||
@@ -78,6 +78,7 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("b-oySn+G2tE", Gen5::AgcAcbJumpGetSize);
|
||||
LIB_FUNC("DwICrVxerkY", Gen5::AgcAcbRewind);
|
||||
LIB_FUNC("htn36gPnBk4", Gen5::AgcAcbWaitRegMem);
|
||||
LIB_FUNC("q4VuU-QsLOE", Gen5::AgcAcbMemSemaphore);
|
||||
LIB_FUNC("idlaArvdXEs", Gen5::AgcAcbWaitOnAddressGetSize);
|
||||
LIB_FUNC("-RnpfpxIhec", Gen5::AgcAcbDmaData);
|
||||
LIB_FUNC("qzMN2XKGA4k", Gen5::AgcAcbCopyData);
|
||||
|
||||
@@ -51691,6 +51691,75 @@ void CheckPm4WaitResume(RenderContext &renderer) {
|
||||
std::printf("[host] %-32s ok\n", "Pm4WaitResume");
|
||||
}
|
||||
|
||||
void CheckPm4MemSemaphore(RenderContext &renderer) {
|
||||
constexpr const char *name = "Pm4MemSemaphore";
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::InitAgcDriver_1(&symbols);
|
||||
const auto *symbol = symbols.FindByNid("q4VuU-QsLOE", Loader::SymbolType::Func);
|
||||
Require(name, "async export",
|
||||
symbol != nullptr &&
|
||||
symbol->vaddr == reinterpret_cast<uint64_t>(&Gen5::AgcAcbMemSemaphore),
|
||||
"async semaphore import did not resolve to the emitter");
|
||||
|
||||
GraphicsInitJmpTables();
|
||||
CommandProcessor processor(renderer, 0x20);
|
||||
const auto execute = [&](std::span<const uint32_t> commands) {
|
||||
Pm4Execution execution;
|
||||
return processor.Process(execution, commands) == Pm4ProcessResult::Complete;
|
||||
};
|
||||
|
||||
alignas(8) uint64_t counter = 0;
|
||||
const auto address = reinterpret_cast<uint64_t>(&counter);
|
||||
const auto low = static_cast<uint32_t>(address);
|
||||
const auto high = static_cast<uint32_t>(address >> 32u);
|
||||
std::array<uint32_t, 11> packet{};
|
||||
auto *const end = packet.data() + packet.size();
|
||||
CommandBufferLayout acb{packet.data(), end, packet.data(), end, nullptr, nullptr, 0};
|
||||
// A packet header in unwritten DWORDs shows the CP skips the signal's pad by its length.
|
||||
constexpr uint32_t poison = 0xc0023900u;
|
||||
const auto emit = [&](uint8_t signal_type, uint8_t op) {
|
||||
packet.fill(poison);
|
||||
acb.cursor_up = packet.data();
|
||||
return Gen5::AgcAcbMemSemaphore(reinterpret_cast<Gen5::CommandBuffer *>(&acb), &counter, 1,
|
||||
signal_type, op) == packet.data() &&
|
||||
acb.cursor_up == end;
|
||||
};
|
||||
|
||||
Require(name, "wait stream",
|
||||
emit(0, 7) &&
|
||||
packet == decltype(packet){0xc0027904u, 0x342u, 0xc9020000u | (high & 0xffffu), low,
|
||||
0xc0023900u, low & ~0x7u, high, 0xe0010000u,
|
||||
0xc0017904u, 0x342u, 0xc9000000u} &&
|
||||
Gen5::AgcIsInternalDataPacket(packet[0], packet.data() + 1) &&
|
||||
Gen5::AgcIsInternalDataPacket(packet[8], packet.data() + 9),
|
||||
"semaphore wait differs from the native stream");
|
||||
const auto wait = packet;
|
||||
Pm4Execution waiting;
|
||||
Require(name, "empty wait",
|
||||
processor.Process(waiting, wait) == Pm4ProcessResult::Blocked &&
|
||||
processor.Process(waiting, wait) == Pm4ProcessResult::Blocked &&
|
||||
!waiting.MadeProgress() && counter == 0,
|
||||
"semaphore wait passed or advanced on a zero counter");
|
||||
|
||||
Require(name, "increment stream",
|
||||
emit(0, 6) &&
|
||||
packet == decltype(packet){0xc0027904u, 0x342u, 0xc9010000u | (high & 0xffffu), low,
|
||||
0xc0011000u, poison, poison,
|
||||
0xc0023900u, low & ~0x7u, high, 0xc0010000u} &&
|
||||
Gen5::AgcIsInternalDataPacket(packet[0], packet.data() + 1) &&
|
||||
execute(packet) && counter == 1 && execute(packet) && counter == 2,
|
||||
"semaphore increment differs from the native stream or count");
|
||||
Require(name, "resume",
|
||||
processor.Process(waiting, wait) == Pm4ProcessResult::Complete && counter == 1,
|
||||
"semaphore wait did not consume one count after a signal");
|
||||
|
||||
counter = 5;
|
||||
Require(name, "write",
|
||||
emit(1, 6) && packet[10] == 0xc0110000u && execute(packet) && counter == 1,
|
||||
"semaphore write signal did not set the counter to one");
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckPm4RewindResume(RenderContext &renderer) {
|
||||
constexpr const char *name = "Pm4RewindResume";
|
||||
Loader::SymbolDatabase symbols;
|
||||
@@ -52681,6 +52750,7 @@ int main(int argc, char **argv) {
|
||||
CheckPm4AcquireMemPackets(vulkan.RuntimeRenderer());
|
||||
CheckPm4WaitPackets(vulkan.RuntimeRenderer());
|
||||
CheckPm4WaitResume(vulkan.RuntimeRenderer());
|
||||
CheckPm4MemSemaphore(vulkan.RuntimeRenderer());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--pm4-draw-multi-only") == 0) {
|
||||
@@ -52989,6 +53059,7 @@ int main(int argc, char **argv) {
|
||||
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
|
||||
CheckPm4IndirectControlFlow(vulkan.RuntimeRenderer());
|
||||
CheckPm4WaitResume(vulkan.RuntimeRenderer());
|
||||
CheckPm4MemSemaphore(vulkan.RuntimeRenderer());
|
||||
CheckPm4RewindResume(vulkan.RuntimeRenderer());
|
||||
CheckEmbeddedFetchVertexOffset();
|
||||
CheckEmbeddedFetchLaneSpill();
|
||||
|
||||
Reference in New Issue
Block a user