mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 20:06:54 +02:00
Handle chained PM4 buffers without recursion
This commit is contained in:
@@ -32,11 +32,12 @@ private:
|
|||||||
|
|
||||||
struct BufferCursor {
|
struct BufferCursor {
|
||||||
std::span<const uint32_t> commands;
|
std::span<const uint32_t> commands;
|
||||||
uint32_t offset_dw = 0;
|
uint32_t offset_dw = 0;
|
||||||
uint32_t deferred_advance_dw = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<BufferCursor> m_buffer_stack;
|
std::vector<BufferCursor> m_buffer_stack;
|
||||||
|
std::span<const uint32_t> m_next_buffer;
|
||||||
|
bool m_chain = false;
|
||||||
bool m_suspended = false;
|
bool m_suspended = false;
|
||||||
bool m_made_progress = false;
|
bool m_made_progress = false;
|
||||||
};
|
};
|
||||||
@@ -129,7 +130,7 @@ public:
|
|||||||
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
||||||
|
|
||||||
Pm4ProcessResult Process(Pm4Execution& execution, std::span<const uint32_t> commands);
|
Pm4ProcessResult Process(Pm4Execution& execution, std::span<const uint32_t> commands);
|
||||||
void ProcessIndirectBuffer(std::span<const uint32_t> commands);
|
void ProcessIndirectBuffer(std::span<const uint32_t> commands, bool chain);
|
||||||
|
|
||||||
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
||||||
|
|
||||||
@@ -143,7 +144,7 @@ private:
|
|||||||
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
||||||
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
||||||
uint32_t interrupt_context_id);
|
uint32_t interrupt_context_id);
|
||||||
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
void ProcessPm4(Pm4Execution& execution);
|
||||||
void SuspendPm4();
|
void SuspendPm4();
|
||||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||||
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||||
|
|||||||
@@ -1495,10 +1495,10 @@ KYTY_CP_OP_PARSER(CpOpBranch) {
|
|||||||
reinterpret_cast<uint64_t>(else_buffer), else_num_dw);
|
reinterpret_cast<uint64_t>(else_buffer), else_num_dw);
|
||||||
|
|
||||||
if (take_then) {
|
if (take_then) {
|
||||||
cp.ProcessIndirectBuffer({then_buffer, then_num_dw});
|
cp.ProcessIndirectBuffer({then_buffer, then_num_dw}, true);
|
||||||
} else if (mode == 2 && else_num_dw != 0) {
|
} else if (mode == 2 && else_num_dw != 0) {
|
||||||
EXIT_NOT_IMPLEMENTED(else_buffer == nullptr);
|
EXIT_NOT_IMPLEMENTED(else_buffer == nullptr);
|
||||||
cp.ProcessIndirectBuffer({else_buffer, else_num_dw});
|
cp.ProcessIndirectBuffer({else_buffer, else_num_dw}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload_dw;
|
return payload_dw;
|
||||||
@@ -1959,7 +1959,7 @@ KYTY_CP_OP_PARSER(CpOpIndirectBuffer) {
|
|||||||
|
|
||||||
GraphicsDbgDumpDcb("ci", indirect_num_dw, indirect_buffer);
|
GraphicsDbgDumpDcb("ci", indirect_num_dw, indirect_buffer);
|
||||||
|
|
||||||
cp.ProcessIndirectBuffer({indirect_buffer, indirect_num_dw});
|
cp.ProcessIndirectBuffer({indirect_buffer, indirect_num_dw}, (control & (1u << 20u)) != 0);
|
||||||
|
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -672,20 +672,16 @@ Pm4ProcessResult CommandProcessor::Process(Pm4Execution& execution,
|
|||||||
Pm4Execution* previous_execution;
|
Pm4Execution* previous_execution;
|
||||||
} execution_scope(*this, execution);
|
} execution_scope(*this, execution);
|
||||||
|
|
||||||
ProcessPm4(execution, 0);
|
ProcessPm4(execution);
|
||||||
return execution.m_buffer_stack.empty() ? Pm4ProcessResult::Complete
|
return execution.m_buffer_stack.empty() ? Pm4ProcessResult::Complete
|
||||||
: Pm4ProcessResult::Blocked;
|
: Pm4ProcessResult::Blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::ProcessIndirectBuffer(std::span<const uint32_t> commands) {
|
void CommandProcessor::ProcessIndirectBuffer(std::span<const uint32_t> commands, bool chain) {
|
||||||
EXIT_IF(g_current_execution == nullptr);
|
EXIT_IF(g_current_execution == nullptr);
|
||||||
if (commands.empty()) {
|
EXIT_IF(!g_current_execution->m_next_buffer.empty());
|
||||||
return;
|
g_current_execution->m_next_buffer = commands;
|
||||||
}
|
g_current_execution->m_chain = chain;
|
||||||
auto& execution = *g_current_execution;
|
|
||||||
const auto stop_depth = execution.m_buffer_stack.size();
|
|
||||||
execution.m_buffer_stack.push_back({commands});
|
|
||||||
ProcessPm4(execution, stop_depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::SuspendPm4() {
|
void CommandProcessor::SuspendPm4() {
|
||||||
@@ -693,21 +689,13 @@ void CommandProcessor::SuspendPm4() {
|
|||||||
g_current_execution->m_suspended = true;
|
g_current_execution->m_suspended = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
void CommandProcessor::ProcessPm4(Pm4Execution& execution) {
|
||||||
while (execution.m_buffer_stack.size() > stop_depth) {
|
while (!execution.m_buffer_stack.empty()) {
|
||||||
if (g_gpu_state != nullptr) {
|
if (g_gpu_state != nullptr) {
|
||||||
g_gpu_state->ProcessCommands();
|
g_gpu_state->ProcessCommands();
|
||||||
}
|
}
|
||||||
const auto buffer_index = execution.m_buffer_stack.size() - 1;
|
auto& cursor = execution.m_buffer_stack.back();
|
||||||
auto& cursor = execution.m_buffer_stack[buffer_index];
|
|
||||||
EXIT_IF(cursor.offset_dw > cursor.commands.size());
|
EXIT_IF(cursor.offset_dw > cursor.commands.size());
|
||||||
if (cursor.deferred_advance_dw != 0) {
|
|
||||||
EXIT_IF(cursor.deferred_advance_dw > cursor.commands.size() - cursor.offset_dw);
|
|
||||||
cursor.offset_dw += cursor.deferred_advance_dw;
|
|
||||||
cursor.deferred_advance_dw = 0;
|
|
||||||
execution.m_made_progress = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cursor.offset_dw == cursor.commands.size()) {
|
if (cursor.offset_dw == cursor.commands.size()) {
|
||||||
execution.m_buffer_stack.pop_back();
|
execution.m_buffer_stack.pop_back();
|
||||||
continue;
|
continue;
|
||||||
@@ -783,14 +771,19 @@ void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
|||||||
handler(*this, packet_header & ~1u, packet + 1, remaining_dw, total_dw) + 1;
|
handler(*this, packet_header & ~1u, packet + 1, remaining_dw, total_dw) + 1;
|
||||||
EXIT_IF(packet_dw > remaining_dw);
|
EXIT_IF(packet_dw > remaining_dw);
|
||||||
if (execution.m_suspended) {
|
if (execution.m_suspended) {
|
||||||
if (execution.m_buffer_stack.size() > buffer_index + 1) {
|
|
||||||
execution.m_buffer_stack[buffer_index].deferred_advance_dw = packet_dw;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EXIT_IF(execution.m_buffer_stack.size() != buffer_index + 1);
|
cursor.offset_dw += packet_dw;
|
||||||
execution.m_buffer_stack[buffer_index].offset_dw += packet_dw;
|
|
||||||
execution.m_made_progress = true;
|
execution.m_made_progress = true;
|
||||||
|
if (!execution.m_next_buffer.empty()) {
|
||||||
|
// Chains and taken branches reuse the fetcher; only calls retain a return cursor.
|
||||||
|
if (execution.m_chain) {
|
||||||
|
cursor = {execution.m_next_buffer};
|
||||||
|
} else {
|
||||||
|
execution.m_buffer_stack.push_back({execution.m_next_buffer});
|
||||||
|
}
|
||||||
|
execution.m_next_buffer = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30295,6 +30295,89 @@ void CheckPm4ContextStateOperations(RenderContext &renderer) {
|
|||||||
std::printf("[host] %-32s ok\n", "Pm4ContextState");
|
std::printf("[host] %-32s ok\n", "Pm4ContextState");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckPm4IndirectControlFlow(RenderContext &renderer) {
|
||||||
|
constexpr const char *name = "Pm4IndirectControlFlow";
|
||||||
|
GraphicsInitJmpTables();
|
||||||
|
CommandProcessor processor(renderer, 0);
|
||||||
|
uint32_t selected = 0;
|
||||||
|
uint32_t returned = 0;
|
||||||
|
const auto address = [](const void *value) {
|
||||||
|
return reinterpret_cast<uint64_t>(value);
|
||||||
|
};
|
||||||
|
const auto write = [&](uint32_t *destination, uint32_t value) {
|
||||||
|
return std::array<uint32_t, 5>{
|
||||||
|
KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0), 0,
|
||||||
|
static_cast<uint32_t>(address(destination)),
|
||||||
|
static_cast<uint32_t>(address(destination) >> 32u), value};
|
||||||
|
};
|
||||||
|
const auto indirect = [&](std::span<const uint32_t> target, bool chain) {
|
||||||
|
uint32_t control = 0x0f200000u | static_cast<uint32_t>(target.size());
|
||||||
|
if (chain) {
|
||||||
|
control |= 1u << 20u;
|
||||||
|
}
|
||||||
|
return std::array<uint32_t, 4>{
|
||||||
|
KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0),
|
||||||
|
static_cast<uint32_t>(address(target.data())),
|
||||||
|
static_cast<uint32_t>(address(target.data()) >> 32u), control};
|
||||||
|
};
|
||||||
|
const auto then_commands = write(&selected, 11);
|
||||||
|
const auto else_commands = write(&selected, 33);
|
||||||
|
const auto branch_suffix = write(&selected, 44);
|
||||||
|
const auto caller_suffix = write(&returned, 22);
|
||||||
|
alignas(8) uint64_t condition = 0;
|
||||||
|
std::array<uint32_t, 19> branch{
|
||||||
|
KYTY_PM4(14, Pm4::IT_INDIRECT_BUFFER, 0), 0,
|
||||||
|
static_cast<uint32_t>(address(&condition)),
|
||||||
|
static_cast<uint32_t>(address(&condition) >> 32u), UINT32_MAX,
|
||||||
|
UINT32_MAX, 1, 0,
|
||||||
|
static_cast<uint32_t>(address(then_commands.data())),
|
||||||
|
static_cast<uint32_t>(address(then_commands.data()) >> 32u),
|
||||||
|
static_cast<uint32_t>(then_commands.size()),
|
||||||
|
static_cast<uint32_t>(address(else_commands.data())),
|
||||||
|
static_cast<uint32_t>(address(else_commands.data()) >> 32u),
|
||||||
|
static_cast<uint32_t>(else_commands.size())};
|
||||||
|
std::copy(branch_suffix.begin(), branch_suffix.end(), branch.begin() + 14);
|
||||||
|
std::array<uint32_t, 9> caller{};
|
||||||
|
const auto call_branch = indirect(branch, false);
|
||||||
|
std::copy(call_branch.begin(), call_branch.end(), caller.begin());
|
||||||
|
std::copy(caller_suffix.begin(), caller_suffix.end(), caller.begin() + 4);
|
||||||
|
for (uint32_t mode : {1u, 2u}) {
|
||||||
|
branch[1] = mode | (3u << 8u);
|
||||||
|
for (uint64_t value : {0ull, 1ull}) {
|
||||||
|
condition = value;
|
||||||
|
selected = returned = 0;
|
||||||
|
uint32_t expected = 44;
|
||||||
|
if (value == 1) {
|
||||||
|
expected = 11;
|
||||||
|
} else if (mode == 2) {
|
||||||
|
expected = 33;
|
||||||
|
}
|
||||||
|
Pm4Execution execution;
|
||||||
|
Require(name, "conditional fetcher replacement",
|
||||||
|
processor.Process(execution, caller) == Pm4ProcessResult::Complete &&
|
||||||
|
selected == expected && returned == 22,
|
||||||
|
"taken branch resumed its discarded suffix or lost the caller's return");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This stream exceeds native recursion capacity, while chains need one fetcher cursor.
|
||||||
|
std::vector<std::array<uint32_t, 4>> links(65536);
|
||||||
|
std::span<const uint32_t> target = then_commands;
|
||||||
|
for (auto &link : links) {
|
||||||
|
link = indirect(target, true);
|
||||||
|
target = link;
|
||||||
|
}
|
||||||
|
const auto call_chain = indirect(target, false);
|
||||||
|
std::copy(call_chain.begin(), call_chain.end(), caller.begin());
|
||||||
|
selected = returned = 0;
|
||||||
|
Pm4Execution execution;
|
||||||
|
Require(name, "long chain and call return",
|
||||||
|
processor.Process(execution, caller) == Pm4ProcessResult::Complete &&
|
||||||
|
selected == 11 && returned == 22,
|
||||||
|
"long chain did not complete and return to the original caller");
|
||||||
|
std::printf("[host] %-32s ok\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckPm4WaitResume(RenderContext &renderer) {
|
void CheckPm4WaitResume(RenderContext &renderer) {
|
||||||
GraphicsInitJmpTables();
|
GraphicsInitJmpTables();
|
||||||
CommandProcessor processor(renderer, 0);
|
CommandProcessor processor(renderer, 0);
|
||||||
@@ -30324,7 +30407,7 @@ void CheckPm4WaitResume(RenderContext &renderer) {
|
|||||||
nested[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0);
|
nested[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0);
|
||||||
nested[1] = static_cast<uint32_t>(address(child.data()));
|
nested[1] = static_cast<uint32_t>(address(child.data()));
|
||||||
nested[2] = static_cast<uint32_t>(address(child.data()) >> 32u);
|
nested[2] = static_cast<uint32_t>(address(child.data()) >> 32u);
|
||||||
nested[3] = 0x0f200000u | static_cast<uint32_t>(child.size());
|
nested[3] = 0x0f300000u | static_cast<uint32_t>(child.size());
|
||||||
|
|
||||||
std::array<uint32_t, 14> commands{};
|
std::array<uint32_t, 14> commands{};
|
||||||
commands[0] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
commands[0] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||||
@@ -30347,6 +30430,10 @@ void CheckPm4WaitResume(RenderContext &renderer) {
|
|||||||
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
||||||
prefix == 11 && child_observation == 0 && suffix == 0,
|
prefix == 11 && child_observation == 0 && suffix == 0,
|
||||||
"blocked indirect wait did not preserve its command position");
|
"blocked indirect wait did not preserve its command position");
|
||||||
|
Require("Pm4WaitResume", "still blocked",
|
||||||
|
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
||||||
|
!execution.MadeProgress() && suffix == 0,
|
||||||
|
"blocked wait advanced or replayed its caller");
|
||||||
|
|
||||||
label = 1;
|
label = 1;
|
||||||
child[4] = 1;
|
child[4] = 1;
|
||||||
@@ -30609,6 +30696,10 @@ int main(int argc, char **argv) {
|
|||||||
if (argc == 2 && std::strcmp(argv[1], "--gpu-command-lane-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--gpu-command-lane-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
vulkan.CheckGpuCommandLane();
|
vulkan.CheckGpuCommandLane();
|
||||||
|
CheckPm4IndirectControlFlow(vulkan.RuntimeRenderer());
|
||||||
|
CheckPm4WaitResume(vulkan.RuntimeRenderer());
|
||||||
|
CheckPm4RewindResume(vulkan.RuntimeRenderer());
|
||||||
|
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--alignbyte-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--alignbyte-only") == 0) {
|
||||||
@@ -30878,6 +30969,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
|
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
|
||||||
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());
|
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());
|
||||||
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
|
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
|
||||||
|
CheckPm4IndirectControlFlow(vulkan.RuntimeRenderer());
|
||||||
CheckPm4WaitResume(vulkan.RuntimeRenderer());
|
CheckPm4WaitResume(vulkan.RuntimeRenderer());
|
||||||
CheckPm4RewindResume(vulkan.RuntimeRenderer());
|
CheckPm4RewindResume(vulkan.RuntimeRenderer());
|
||||||
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||||
|
|||||||
Reference in New Issue
Block a user