diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp index a1f5a1f8c3..53cfe73ba6 100644 --- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp @@ -1178,11 +1178,7 @@ void ARBDecompiler::VisitAST(const ASTNode& node) { if (ast_return->kills) { AddLine("KIL TR;"); } else { - if (context_func->IsMain()) { - Exit(); - } else { - AddLine("RET;"); - } + Exit(); } if (!is_true) { AddLine("ENDIF;"); @@ -1487,7 +1483,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) { } void ARBDecompiler::Exit() { - if (stage != ShaderType::Fragment) { + if (!context_func->IsMain() || stage != ShaderType::Fragment) { AddLine("RET;"); return; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 7d9d627a42..5404a388ea 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -435,6 +435,25 @@ public: DeclareCustomVariables(); DeclarePhysicalAttributeReader(); + const auto& subfunctions = ir.GetSubFunctions(); + auto it = subfunctions.rbegin(); + while (it != subfunctions.rend()) { + context_func = *it; + code.AddLine("void func_{}() {{", context_func->GetId()); + ++code.scope; + + if (context_func->IsDecompiled()) { + DecompileAST(); + } else { + DecompileBranchMode(); + } + + --code.scope; + code.AddLine("}}"); + + it++; + } + context_func = ir.GetMainFunction(); code.AddLine("void main() {{"); @@ -1133,6 +1152,11 @@ private: return {}; } + if (const auto func_call = std::get_if(&*node)) { + code.AddLine("func_{}();", func_call->GetFuncId()); + return {}; + } + if (const auto comment = std::get_if(&*node)) { code.AddLine("// " + comment->GetText()); return {}; @@ -2269,7 +2293,9 @@ private: } Expression Exit(Operation operation) { - PreExit(); + if (context_func->IsMain()) { + PreExit(); + } code.AddLine("return;"); return {}; } diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 4ead6c5f8b..7e8a228e3d 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -2177,7 +2177,9 @@ private: } Expression Exit(Operation operation) { - PreExit(); + if (context_func->IsMain()) { + PreExit(); + } inside_branch = true; if (conditional_branch_set) { OpReturn(); @@ -3066,7 +3068,9 @@ public: if (ast.kills) { decomp.OpKill(); } else { - decomp.PreExit(); + if (decomp.context_func->IsMain()) { + decomp.PreExit(); + } decomp.OpReturn(); } decomp.AddLabel(decomp.OpLabel()); diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 33ecb7cf39..5ef2251b95 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -112,12 +113,10 @@ struct ShaderFunction { struct ShaderProgram { ShaderFunction main; - std::unordered_map subfunctions; + std::map subfunctions; }; std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 start_address, - const CompilerSettings& settings, - Registry& registry); - + const CompilerSettings& settings, Registry& registry); } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index eaa8b46bb0..4667c37317 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -216,10 +216,22 @@ void ShaderIR::Decode() { decompiled = false; auto info = ScanFlow(program_code, main_offset, settings, registry); + u32 id_start = 1; + for (auto& pair : info->subfunctions) { + func_map.emplace(pair.first, id_start); + id_start++; + } coverage_begin = info->main.start; coverage_end = 0; decode_function(info->main); main_function = gen_function(info->main, 0); + subfunctions.resize(info->subfunctions.size()); + for (auto& pair : info->subfunctions) { + auto& func_info = pair.second; + decode_function(func_info); + u32 id = func_map[pair.first]; + subfunctions[id - 1] = gen_function(func_info, id); + } } NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 5f88537bc4..2bc596512a 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -33,6 +33,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { // With the previous preconditions, this instruction is a no-operation. break; } + case OpCode::Id::RET: case OpCode::Id::EXIT: { const ConditionCode cc = instr.flow_condition_code; UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc); @@ -312,6 +313,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed"); break; } + case OpCode::Id::CAL: { + const u32 target = pc + instr.bra.GetBranchTarget(); + const auto it = func_map.find(target); + if (it == func_map.end()) { + UNREACHABLE(); + break; + } + bb.push_back(FunctionCall(it->second)); + break; + } default: UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index b54d33763d..a58e7c65e4 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -267,10 +267,11 @@ class PatchNode; class SmemNode; class GmemNode; class CommentNode; +class FunctionCallNode; using NodeData = std::variant; + LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>; using Node = std::shared_ptr; using Node4 = std::array; using NodeBlock = std::vector; @@ -494,6 +495,18 @@ private: std::vector code; ///< Code to execute }; +class FunctionCallNode final : public AmendNode { +public: + explicit FunctionCallNode(u32 func_id_) : func_id{func_id_} {} + + [[nodiscard]] u32 GetFuncId() const { + return func_id; + } + +private: + u32 func_id; ///< Id of the function to call +}; + /// A general purpose register class GprNode final { public: diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp index 6a5b6940d1..cef9c26bc3 100644 --- a/src/video_core/shader/node_helper.cpp +++ b/src/video_core/shader/node_helper.cpp @@ -19,6 +19,11 @@ Node Comment(std::string text) { return MakeNode(std::move(text)); } +/// Creates a function call +Node FunctionCall(u32 func_id) { + return MakeNode(func_id); +} + Node Immediate(u32 value) { return MakeNode(value); } diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h index 1e0886185d..3f882cd25d 100644 --- a/src/video_core/shader/node_helper.h +++ b/src/video_core/shader/node_helper.h @@ -27,6 +27,9 @@ Node Conditional(Node condition, std::vector code); /// Creates a commentary node Node Comment(std::string text); +/// Creates a function call +Node FunctionCall(u32 func_id); + /// Creates an u32 immediate Node Immediate(u32 value); diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index b7a33d0ae8..56983c9479 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -69,7 +69,7 @@ public: explicit ShaderFunctionIR(std::map&& basic_blocks_, bool disable_flow_stack_, u32 id_, u32 coverage_begin_, u32 coverage_end_) : basic_blocks{std::move(basic_blocks_)}, decompiled{false}, - disable_flow_stack{disable_flow_stack}, id{id_}, coverage_begin{coverage_begin_}, + disable_flow_stack{disable_flow_stack_}, id{id_}, coverage_begin{coverage_begin_}, coverage_end{coverage_end_} {} explicit ShaderFunctionIR(ASTManager&& program_manager_, u32 id_, u32 coverage_begin_, u32 coverage_end_) @@ -80,11 +80,11 @@ public: return basic_blocks; } - bool IsFlowStackDisabled() const { + [[nodiscard]] bool IsFlowStackDisabled() const { return disable_flow_stack; } - bool IsDecompiled() const { + [[nodiscard]] bool IsDecompiled() const { return decompiled; } @@ -100,11 +100,11 @@ public: return program_manager.GetVariables(); } - bool IsMain() const { + [[nodiscard]] bool IsMain() const { return id == 0; } - u32 GetId() const { + [[nodiscard]] u32 GetId() const { return id; } @@ -226,6 +226,10 @@ public: return main_function; } + const std::vector>& GetSubFunctions() const { + return subfunctions; + } + private: friend class ASTDecoder; @@ -491,6 +495,7 @@ private: std::shared_ptr main_function; std::vector> subfunctions; + std::unordered_map func_map; std::set used_registers; std::set used_predicates;