aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorameerj <52414509+ameerj@users.noreply.github.com>2021-08-23 19:52:07 -0400
committerameerj <52414509+ameerj@users.noreply.github.com>2021-08-28 11:35:25 -0400
commit862dc2b2b31b8353449f61dcc68d69255721f6c6 (patch)
treee37b2597909d3990ccd563d5403eff16835b6562
parentc20ea8939032a7141f22607ad16b8aad5e7a3251 (diff)
structured_control_flow: Add DemoteCombinationPass
Some drivers misread data when demotes are interleaved in the program. This moves demote branches to be checked at the end of the program. Fixes "wireframe" issue in Pokemon SwSh on some drivers
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp108
1 files changed, 107 insertions, 1 deletions
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 8b3e0a15c7..f124dc8c07 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -660,6 +660,9 @@ public:
IR::Block& first_block{*syntax_list.front().data.block};
IR::IREmitter ir(first_block, first_block.begin());
ir.Prologue();
+ if (uses_demote_to_helper) {
+ DemoteCombinationPass();
+ }
}
private:
@@ -809,7 +812,14 @@ private:
}
case StatementType::Return: {
ensure_block();
- IR::IREmitter{*current_block}.Epilogue();
+ IR::Block* return_block{block_pool.Create(inst_pool)};
+ IR::IREmitter{*return_block}.Epilogue();
+ current_block->AddBranch(return_block);
+
+ auto& merge{syntax_list.emplace_back()};
+ merge.type = IR::AbstractSyntaxNode::Type::Block;
+ merge.data.block = return_block;
+
current_block = nullptr;
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
break;
@@ -824,6 +834,7 @@ private:
auto& merge{syntax_list.emplace_back()};
merge.type = IR::AbstractSyntaxNode::Type::Block;
merge.data.block = demote_block;
+ uses_demote_to_helper = true;
break;
}
case StatementType::Unreachable: {
@@ -855,11 +866,106 @@ private:
return block_pool.Create(inst_pool);
}
+ void DemoteCombinationPass() {
+ using Type = IR::AbstractSyntaxNode::Type;
+ std::vector<IR::Block*> demote_blocks;
+ std::vector<IR::U1> demote_conds;
+ u32 num_epilogues{};
+ for (const IR::AbstractSyntaxNode& node : syntax_list) {
+ if (node.type != Type::Block) {
+ continue;
+ }
+ for (const IR::Inst& inst : node.data.block->Instructions()) {
+ const IR::Opcode op{inst.GetOpcode()};
+ if (op == IR::Opcode::DemoteToHelperInvocation) {
+ demote_blocks.push_back(node.data.block);
+ break;
+ }
+ if (op == IR::Opcode::Epilogue) {
+ ++num_epilogues;
+ }
+ }
+ }
+ if (demote_blocks.size() == 0) {
+ return;
+ }
+ if (num_epilogues > 1) {
+ LOG_DEBUG(Shader, "Combining demotes with more than one return is not implemented.");
+ return;
+ }
+ s64 last_iterator_offset{};
+ auto& asl{syntax_list};
+ for (const IR::Block* demote_block : demote_blocks) {
+ const auto start_it{asl.begin() + last_iterator_offset};
+ auto asl_it{std::find_if(start_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::If && asn.data.if_node.body == demote_block;
+ })};
+ if (asl_it == asl.end()) {
+ // Demote without a conditional branch.
+ // No need to proceed since all fragment instances will be demoted regardless.
+ return;
+ }
+ const IR::Block* const end_if = asl_it->data.if_node.merge;
+ demote_conds.push_back(asl_it->data.if_node.cond);
+ last_iterator_offset = std::distance(asl.begin(), asl_it);
+
+ asl_it = asl.erase(asl_it);
+ asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::Block && asn.data.block == demote_block;
+ });
+
+ asl_it = asl.erase(asl_it);
+ asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::EndIf && asn.data.end_if.merge == end_if;
+ });
+ asl_it = asl.erase(asl_it);
+ }
+ const auto epilogue_func{[](const IR::AbstractSyntaxNode& asn) {
+ if (asn.type != Type::Block) {
+ return false;
+ }
+ for (const auto& inst : asn.data.block->Instructions()) {
+ if (inst.GetOpcode() == IR::Opcode::Epilogue) {
+ return true;
+ }
+ }
+ return false;
+ }};
+ const auto reverse_it{std::find_if(asl.rbegin(), asl.rend(), epilogue_func)};
+ const auto return_block_it{(reverse_it + 1).base()};
+
+ IR::IREmitter ir{*(return_block_it - 1)->data.block};
+ IR::U1 cond(IR::Value(false));
+ for (const auto& demote_cond : demote_conds) {
+ cond = ir.LogicalOr(cond, demote_cond);
+ }
+ cond.Inst()->DestructiveAddUsage(1);
+
+ IR::AbstractSyntaxNode demote_if_node{};
+ demote_if_node.type = Type::If;
+ demote_if_node.data.if_node.cond = cond;
+ demote_if_node.data.if_node.body = demote_blocks[0];
+ demote_if_node.data.if_node.merge = return_block_it->data.block;
+
+ IR::AbstractSyntaxNode demote_node{};
+ demote_node.type = Type::Block;
+ demote_node.data.block = demote_blocks[0];
+
+ IR::AbstractSyntaxNode demote_endif_node{};
+ demote_endif_node.type = Type::EndIf;
+ demote_endif_node.data.end_if.merge = return_block_it->data.block;
+
+ asl.insert(return_block_it, demote_endif_node);
+ asl.insert(return_block_it, demote_node);
+ asl.insert(return_block_it, demote_if_node);
+ }
+
ObjectPool<Statement>& stmt_pool;
ObjectPool<IR::Inst>& inst_pool;
ObjectPool<IR::Block>& block_pool;
Environment& env;
IR::AbstractSyntaxList& syntax_list;
+ bool uses_demote_to_helper{};
// TODO: C++20 Remove this when all compilers support constexpr std::vector
#if __cpp_lib_constexpr_vector >= 201907