mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 05:46:56 +01:00
shader: fix shared-terminal selection merges
This commit is contained in:
@@ -1138,20 +1138,6 @@ bool IsLoopControlGateway(const Graph& graph, const NaturalLoop& loop, uint32_t
|
|||||||
is_control_target(block->terminator.false_block);
|
is_control_target(block->terminator.false_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasLinearPathTo(const Graph& graph, uint32_t start, uint32_t target) {
|
|
||||||
std::vector<bool> visited(graph.blocks.size(), false);
|
|
||||||
for (auto block_id = start; block_id != target;) {
|
|
||||||
const auto* block = graph.FindBlock(block_id);
|
|
||||||
if (block == nullptr || block_id >= visited.size() || visited[block_id] ||
|
|
||||||
block->successors.size() != 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
visited[block_id] = true;
|
|
||||||
block_id = block->successors.front();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasLinearPathToTerminal(const Graph& graph, uint32_t start) {
|
bool HasLinearPathToTerminal(const Graph& graph, uint32_t start) {
|
||||||
std::vector<bool> visited(graph.blocks.size(), false);
|
std::vector<bool> visited(graph.blocks.size(), false);
|
||||||
for (auto block_id = start;;) {
|
for (auto block_id = start;;) {
|
||||||
@@ -1213,13 +1199,6 @@ uint32_t FindSelectionMerge(const Graph& graph, const BasicBlock& block) {
|
|||||||
const auto true_target = block.terminator.true_block;
|
const auto true_target = block.terminator.true_block;
|
||||||
const auto false_target = block.terminator.false_block;
|
const auto false_target = block.terminator.false_block;
|
||||||
if (global_block != nullptr && global_block->successors.empty()) {
|
if (global_block != nullptr && global_block->successors.empty()) {
|
||||||
// A terminal common post-dominator can hide the local continuation of a nested
|
|
||||||
// early-exit selection. Prefer that continuation as the structured merge.
|
|
||||||
const bool true_exits = HasLinearPathTo(graph, true_target, global_merge);
|
|
||||||
const bool false_exits = HasLinearPathTo(graph, false_target, global_merge);
|
|
||||||
if (true_exits != false_exits) {
|
|
||||||
return true_exits ? false_target : true_target;
|
|
||||||
}
|
|
||||||
const bool false_reaches_true =
|
const bool false_reaches_true =
|
||||||
CanReachBefore(graph, false_target, true_target, global_merge);
|
CanReachBefore(graph, false_target, true_target, global_merge);
|
||||||
const bool true_reaches_false =
|
const bool true_reaches_false =
|
||||||
@@ -1511,7 +1490,11 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
|||||||
}
|
}
|
||||||
const auto region = SelectionRegion(graph, *block, merge);
|
const auto region = SelectionRegion(graph, *block, merge);
|
||||||
const auto external = std::find_if(region.begin(), region.end(), [&](uint32_t member) {
|
const auto external = std::find_if(region.begin(), region.end(), [&](uint32_t member) {
|
||||||
return !graph.Dominates(block_id, member);
|
const auto* member_block = graph.FindBlock(member);
|
||||||
|
return member_block != nullptr &&
|
||||||
|
std::ranges::any_of(member_block->predecessors, [&](uint32_t predecessor) {
|
||||||
|
return predecessor != block_id && !Contains(region, predecessor);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
if (external != region.end()) {
|
if (external != region.end()) {
|
||||||
SetFailure(
|
SetFailure(
|
||||||
|
|||||||
@@ -7881,6 +7881,50 @@ void TestNewShaderRecompilerCfgNestedEarlyExitSharedTerminal() {
|
|||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestNewShaderRecompilerCfgSharedTerminalEarlyExit() {
|
||||||
|
const uint32_t shader[] = {
|
||||||
|
EncodeSopc(0x06, 0, 0), // outer early-exit condition
|
||||||
|
EncodeSopp(0x04, 8), // outer -> early exit or nested body
|
||||||
|
EncodeSopc(0x06, 1, 1), // nested body condition
|
||||||
|
EncodeSopp(0x04, 1), // nested body -> right or left
|
||||||
|
EncodeSopp(0x02, 4), // left -> body continuation
|
||||||
|
EncodeSopc(0x06, 2, 2), // right condition
|
||||||
|
EncodeSopp(0x04, 1), // right -> join or work
|
||||||
|
EncodeSopp(0x02, 0), // work -> join
|
||||||
|
EncodeSopp(0x02, 0), // join -> body continuation
|
||||||
|
EncodeSopp(0x02, 1), // body continuation -> shared terminal
|
||||||
|
EncodeSopp(0x02, 0), // early exit -> shared terminal
|
||||||
|
0xbf810000u,
|
||||||
|
};
|
||||||
|
|
||||||
|
ShaderRecompiler::Decoder::Program decoded;
|
||||||
|
std::string error;
|
||||||
|
Check(ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded,
|
||||||
|
&error),
|
||||||
|
error.c_str());
|
||||||
|
ShaderRecompiler::CFG::Graph graph;
|
||||||
|
Check(ShaderRecompiler::CFG::BuildGraph(decoded, graph, &error),
|
||||||
|
error.c_str());
|
||||||
|
const auto original_coverage =
|
||||||
|
CfgInstructionCoverage(graph, decoded.instructions.size());
|
||||||
|
Check(ShaderRecompiler::CFG::Structurize(graph, &error), error.c_str());
|
||||||
|
Check(CfgInstructionCoverage(graph, decoded.instructions.size()) ==
|
||||||
|
original_coverage,
|
||||||
|
"shared-terminal structurization changed semantic instruction coverage");
|
||||||
|
|
||||||
|
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||||
|
options.dump_ir = true;
|
||||||
|
ShaderRecompiler::CompileResult result;
|
||||||
|
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||||
|
error.c_str());
|
||||||
|
Check(!result.program.dispatcher_fallback &&
|
||||||
|
Common::ContainsStr(result.ir_dump, "mode=structured"),
|
||||||
|
"shared-terminal early exit selected dispatcher control flow");
|
||||||
|
Check(SpirvInstructionOpcodeCount(result.spirv, 251) == 0u,
|
||||||
|
"shared-terminal early exit unexpectedly used dispatcher OpSwitch");
|
||||||
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry() {
|
void TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry() {
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
EncodeSopp(0x02, 1), // entry -> header, skipping unreachable entry
|
EncodeSopp(0x02, 1), // entry -> header, skipping unreachable entry
|
||||||
@@ -11513,6 +11557,7 @@ int main() {
|
|||||||
TestNewShaderRecompilerCfgLoopSharedRegion();
|
TestNewShaderRecompilerCfgLoopSharedRegion();
|
||||||
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
||||||
TestNewShaderRecompilerCfgNestedEarlyExitSharedTerminal();
|
TestNewShaderRecompilerCfgNestedEarlyExitSharedTerminal();
|
||||||
|
TestNewShaderRecompilerCfgSharedTerminalEarlyExit();
|
||||||
TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry();
|
TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry();
|
||||||
TestNewShaderRecompilerCfgIrreducibleDispatcher();
|
TestNewShaderRecompilerCfgIrreducibleDispatcher();
|
||||||
TestNewShaderRecompilerDispatcherSpillsU32x3();
|
TestNewShaderRecompilerDispatcherSpillsU32x3();
|
||||||
|
|||||||
Reference in New Issue
Block a user