Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rsx: Vertex program analyser improvements #14804

Merged
merged 2 commits into from Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 33 additions & 16 deletions rpcs3/Emu/RSX/Program/ProgramStateCache.cpp
Expand Up @@ -50,7 +50,6 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
{
u32 current_instruction = start;
std::set<u32> conditional_targets;
bool has_printed_error = false;

while (true)
{
Expand All @@ -60,20 +59,13 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
{
if (!fast_exit)
{
if (!has_printed_error)
{
// This can be harmless if a dangling RET was encountered before
rsx_log.error("vp_analyser: Possible infinite loop detected");
has_printed_error = true;
}
current_instruction++;
continue;
}
else
{
// Block walk, looking for earliest exit
break;
// This can be harmless if a dangling RET was encountered before.
// This can also be legal in case of BRB...BRI loops since BRIs are conditional. Might just be a loop with exit cond.
rsx_log.warning("vp_analyser: Possible infinite loop detected");
}

// There is never any reason to continue scanning after self-intersecting on the control-flow tree.
break;
}

const auto instruction = v128::loadu(&data[current_instruction * 4]);
Expand Down Expand Up @@ -211,9 +203,23 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
}
}

if ((d3.end && (fast_exit || current_instruction >= instruction_range.second)) ||
(current_instruction + 1) == rsx::max_vertex_program_instructions)
// Check exit conditions...
if (d3.end)
{
// We have seen an end of instructions marker.
// Multiple exits may exist, usually skipped over by branching. Do not exit on end unless there is no branching.
if (!has_branch_instruction || fast_exit || current_instruction >= instruction_range.second)
{
// Conditions:
// 1. No branching so far. This will always be the exit.
// 2. Fast exit flag is set. This happens when walking through subroutines.
// 3. We've gone beyond the known instruction range. In this scenario, this is the furthest end marker seen so far. It has to be reached by some earlier branch.
break;
}
}
else if ((current_instruction + 1) == rsx::max_vertex_program_instructions)
{
// No more instructions to read.
break;
}

Expand Down Expand Up @@ -288,6 +294,17 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
}
}

// Typical ubershaders have the dispatch at the top with subroutines following. However...
// some games have the dispatch block at the end and the subroutines above them.
// We need to simulate a jump-to-entry in this situation
// Normally this condition is handled by the conditional_targets walk, but sometimes this doesn't work due to cyclic branches
if (instruction_range.first < dst_prog.entry)
{
// Is there a subroutine that jumps into the entry? If not, add to jump table
const auto target = dst_prog.entry - instruction_range.first;
dst_prog.jump_table.insert(target);
}

// Verification
for (const u32 target : dst_prog.jump_table)
{
Expand Down
8 changes: 4 additions & 4 deletions rpcs3/Emu/RSX/Program/VertexProgramDecompiler.cpp
Expand Up @@ -445,7 +445,7 @@ std::string VertexProgramDecompiler::Decompile()
const auto& data = m_prog.data;
m_instr_count = data.size() / 4;

bool is_has_BRA = false;
bool has_BRA = false;
bool program_end = false;
u32 i = 1;
u32 last_label_addr = 0;
Expand Down Expand Up @@ -527,7 +527,7 @@ std::string VertexProgramDecompiler::Decompile()
}
};

if (is_has_BRA || !m_prog.jump_table.empty())
if (has_BRA || !m_prog.jump_table.empty())
{
m_cur_instr = &m_instructions[0];

Expand Down Expand Up @@ -573,7 +573,7 @@ std::string VertexProgramDecompiler::Decompile()
{
//TODO: Subroutines can also have arbitrary jumps!
u32 jump_position = find_jump_lvl(i);
if (is_has_BRA || jump_position != umax)
if (has_BRA || jump_position != umax)
{
m_cur_instr->close_scopes++;
AddCode("}");
Expand Down Expand Up @@ -782,7 +782,7 @@ std::string VertexProgramDecompiler::Decompile()
}
}

if (is_has_BRA || !m_prog.jump_table.empty())
if (has_BRA || !m_prog.jump_table.empty())
{
m_cur_instr = &m_instructions[m_instr_count - 1];
m_cur_instr->close_scopes++;
Expand Down