aboutsummaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 05:02:43 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 11:29:29 -0500
commit310dd596d0f6d48de3517cfb1bdc90a366817405 (patch)
tree751386685eefaa7b86037893e80f76077b87883b /src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
parenteb02d1dd53a03d0bb6ce541694243579a4c10c6f (diff)
shader_recompiler: translate Switch 2 task and mesh programs
Mario Kart World draws some of its geometry with NVN task and mesh programs (the task program culls and writes the mesh workgroup count and a payload to its stage buffer; each mesh workgroup reads it back and writes vertices and primitives). Add Task and Mesh stages and translate them for VK_EXT_mesh_shader: - the ISBE pseudo instruction from the lowering becomes reads and writes of an emulated stage buffer: the task's output region is the task payload, the mesh output regions live in workgroup memory - every EXIT of these stages jumps to one final return, so all invocations reach the epilogue: a barrier, then OpEmitMeshTasksEXT, or OpSetMeshOutputsEXT and a copy of the 32-element vertex and primitive groups (lines named by the mesh header's and the pass-through geometry header's output maps) to arrayed and per-primitive outputs - Sirit lacks the two mesh instructions: they are emitted as OpStore markers and replaced after assembly - fragment inputs a mesh program writes per primitive get PerPrimitiveEXT The captured task and mesh programs of the title translate to SPIR-V that spirv-val accepts.
Diffstat (limited to 'src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp')
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp29
1 files changed, 25 insertions, 4 deletions
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 329d44443e..50cff52f88 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -314,7 +314,8 @@ bool NeedsLift(Node goto_stmt, Node label_stmt) noexcept {
class GotoPass {
public:
- explicit GotoPass(Flow::CFG& cfg, ObjectPool<Statement>& stmt_pool) : pool{stmt_pool} {
+ explicit GotoPass(Flow::CFG& cfg, ObjectPool<Statement>& stmt_pool, bool converge_exits_)
+ : pool{stmt_pool}, converge_exits{converge_exits_} {
std::vector gotos{BuildTree(cfg)};
const auto end{gotos.rend()};
for (auto goto_stmt = gotos.rbegin(); goto_stmt != end; ++goto_stmt) {
@@ -381,7 +382,17 @@ private:
u32 label_id{0};
std::vector<Node> gotos;
Flow::Function& first_function{cfg.Functions().front()};
- BuildTree(cfg, first_function, label_id, gotos, root_stmt.children.end(), std::nullopt);
+ Node insert_point{root_stmt.children.end()};
+ if (converge_exits) {
+ // Every EXIT jumps to one return at the end, so all invocations reach the same
+ // epilogue (task and mesh programs emit their outputs there, after a barrier).
+ Tree& root{root_stmt.children};
+ exit_label = root.insert(root.end(), *pool.Create(Label{}, label_id, &root_stmt));
+ ++label_id;
+ root.insert(root.end(), *pool.Create(Return{}, &root_stmt));
+ insert_point = *exit_label;
+ }
+ BuildTree(cfg, first_function, label_id, gotos, insert_point, std::nullopt);
return gotos;
}
@@ -454,7 +465,14 @@ private:
break;
}
case Flow::EndClass::Exit:
- root.insert(ip, *pool.Create(Return{}, &root_stmt));
+ if (exit_label) {
+ Statement* const always_cond{
+ pool.Create(Identity{}, IR::Condition{true}, &root_stmt)};
+ gotos.push_back(root.insert(
+ ip, *pool.Create(Goto{}, always_cond, *exit_label, &root_stmt)));
+ } else {
+ root.insert(ip, *pool.Create(Return{}, &root_stmt));
+ }
break;
case Flow::EndClass::Return: {
Statement* const always_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
@@ -618,6 +636,8 @@ private:
ObjectPool<Statement>& pool;
Statement root_stmt{FunctionTag{}};
+ bool converge_exits{};
+ std::optional<Node> exit_label;
};
[[nodiscard]] Statement* TryFindForwardBlock(Statement& stmt) {
@@ -987,7 +1007,8 @@ IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::
Environment& env, Flow::CFG& cfg,
const HostTranslateInfo& host_info) {
ObjectPool<Statement> stmt_pool{64};
- GotoPass goto_pass{cfg, stmt_pool};
+ const Stage stage{env.ShaderStage()};
+ GotoPass goto_pass{cfg, stmt_pool, stage == Stage::Task || stage == Stage::Mesh};
Statement& root{goto_pass.RootStatement()};
IR::AbstractSyntaxList syntax_list;
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};