mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 10:44:49 +01:00 
			
		
		
		
	Shader_Ir: Refactor Decompilation process and allow multiple decompilation modes.
This commit is contained in:
		
							parent
							
								
									38fc995f6c
								
							
						
					
					
						commit
						47e4f6a52c
					
				| @ -87,6 +87,8 @@ set(HASH_FILES | |||||||
|     "${VIDEO_CORE}/shader/ast.h" |     "${VIDEO_CORE}/shader/ast.h" | ||||||
|     "${VIDEO_CORE}/shader/control_flow.cpp" |     "${VIDEO_CORE}/shader/control_flow.cpp" | ||||||
|     "${VIDEO_CORE}/shader/control_flow.h" |     "${VIDEO_CORE}/shader/control_flow.h" | ||||||
|  |     "${VIDEO_CORE}/shader/compiler_settings.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/compiler_settings.h" | ||||||
|     "${VIDEO_CORE}/shader/decode.cpp" |     "${VIDEO_CORE}/shader/decode.cpp" | ||||||
|     "${VIDEO_CORE}/shader/expr.cpp" |     "${VIDEO_CORE}/shader/expr.cpp" | ||||||
|     "${VIDEO_CORE}/shader/expr.h" |     "${VIDEO_CORE}/shader/expr.h" | ||||||
|  | |||||||
| @ -64,6 +64,8 @@ add_custom_command(OUTPUT scm_rev.cpp | |||||||
|       "${VIDEO_CORE}/shader/ast.h" |       "${VIDEO_CORE}/shader/ast.h" | ||||||
|       "${VIDEO_CORE}/shader/control_flow.cpp" |       "${VIDEO_CORE}/shader/control_flow.cpp" | ||||||
|       "${VIDEO_CORE}/shader/control_flow.h" |       "${VIDEO_CORE}/shader/control_flow.h" | ||||||
|  |       "${VIDEO_CORE}/shader/compiler_settings.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/compiler_settings.h" | ||||||
|       "${VIDEO_CORE}/shader/decode.cpp" |       "${VIDEO_CORE}/shader/decode.cpp" | ||||||
|       "${VIDEO_CORE}/shader/expr.cpp" |       "${VIDEO_CORE}/shader/expr.cpp" | ||||||
|       "${VIDEO_CORE}/shader/expr.h" |       "${VIDEO_CORE}/shader/expr.h" | ||||||
|  | |||||||
| @ -109,6 +109,8 @@ add_library(video_core STATIC | |||||||
|     shader/ast.h |     shader/ast.h | ||||||
|     shader/control_flow.cpp |     shader/control_flow.cpp | ||||||
|     shader/control_flow.h |     shader/control_flow.h | ||||||
|  |     shader/compiler_settings.cpp | ||||||
|  |     shader/compiler_settings.h | ||||||
|     shader/decode.cpp |     shader/decode.cpp | ||||||
|     shader/expr.cpp |     shader/expr.cpp | ||||||
|     shader/expr.h |     shader/expr.h | ||||||
|  | |||||||
| @ -352,10 +352,12 @@ public: | |||||||
|         // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
 |         // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
 | ||||||
|         // unlikely that shaders will use 20 nested SSYs and PBKs.
 |         // unlikely that shaders will use 20 nested SSYs and PBKs.
 | ||||||
|         constexpr u32 FLOW_STACK_SIZE = 20; |         constexpr u32 FLOW_STACK_SIZE = 20; | ||||||
|  |         if (!ir.IsFlowStackDisabled()) { | ||||||
|             for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { |             for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { | ||||||
|                 code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); |                 code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); | ||||||
|                 code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); |                 code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         code.AddLine("while (true) {{"); |         code.AddLine("while (true) {{"); | ||||||
|         ++code.scope; |         ++code.scope; | ||||||
|  | |||||||
| @ -11,6 +11,8 @@ | |||||||
| namespace OpenGL::GLShader { | namespace OpenGL::GLShader { | ||||||
| 
 | 
 | ||||||
| using Tegra::Engines::Maxwell3D; | using Tegra::Engines::Maxwell3D; | ||||||
|  | using VideoCommon::Shader::CompileDepth; | ||||||
|  | using VideoCommon::Shader::CompilerSettings; | ||||||
| using VideoCommon::Shader::ProgramCode; | using VideoCommon::Shader::ProgramCode; | ||||||
| using VideoCommon::Shader::ShaderIR; | using VideoCommon::Shader::ShaderIR; | ||||||
| 
 | 
 | ||||||
| @ -31,13 +33,17 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { | |||||||
| 
 | 
 | ||||||
| )"; | )"; | ||||||
| 
 | 
 | ||||||
|     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); |     CompilerSettings settings; | ||||||
|  |     settings.depth = CompileDepth::NoFlowStack; | ||||||
|  | 
 | ||||||
|  |     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); | ||||||
|     const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; |     const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; | ||||||
|     ProgramResult program = Decompile(device, program_ir, stage, "vertex"); |     ProgramResult program = Decompile(device, program_ir, stage, "vertex"); | ||||||
|     out += program.first; |     out += program.first; | ||||||
| 
 | 
 | ||||||
|     if (setup.IsDualProgram()) { |     if (setup.IsDualProgram()) { | ||||||
|         const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b); |         const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b, | ||||||
|  |                                     settings); | ||||||
|         ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); |         ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); | ||||||
|         out += program_b.first; |         out += program_b.first; | ||||||
|     } |     } | ||||||
| @ -80,7 +86,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { | |||||||
| 
 | 
 | ||||||
| )"; | )"; | ||||||
| 
 | 
 | ||||||
|     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); |     CompilerSettings settings; | ||||||
|  |     settings.depth = CompileDepth::NoFlowStack; | ||||||
|  | 
 | ||||||
|  |     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); | ||||||
|     ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); |     ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); | ||||||
|     out += program.first; |     out += program.first; | ||||||
| 
 | 
 | ||||||
| @ -114,7 +123,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| )"; | )"; | ||||||
|     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); |     CompilerSettings settings; | ||||||
|  |     settings.depth = CompileDepth::NoFlowStack; | ||||||
|  | 
 | ||||||
|  |     const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); | ||||||
|     ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); |     ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); | ||||||
|     out += program.first; |     out += program.first; | ||||||
| 
 | 
 | ||||||
| @ -133,7 +145,10 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set | |||||||
|     std::string out = "// Shader Unique Id: CS" + id + "\n\n"; |     std::string out = "// Shader Unique Id: CS" + id + "\n\n"; | ||||||
|     out += GetCommonDeclarations(); |     out += GetCommonDeclarations(); | ||||||
| 
 | 
 | ||||||
|     const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a); |     CompilerSettings settings; | ||||||
|  |     settings.depth = CompileDepth::NoFlowStack; | ||||||
|  | 
 | ||||||
|  |     const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings); | ||||||
|     ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); |     ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); | ||||||
|     out += program.first; |     out += program.first; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -363,7 +363,7 @@ std::string ASTManager::Print() { | |||||||
|     return printer.GetResult(); |     return printer.GetResult(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ASTManager::ASTManager() = default; | ASTManager::ASTManager(bool full_decompile) : full_decompile{full_decompile} {}; | ||||||
| 
 | 
 | ||||||
| ASTManager::~ASTManager() { | ASTManager::~ASTManager() { | ||||||
|     Clear(); |     Clear(); | ||||||
| @ -383,6 +383,7 @@ ASTManager::ASTManager(ASTManager&& other) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ASTManager& ASTManager::operator=(ASTManager&& other) { | ASTManager& ASTManager::operator=(ASTManager&& other) { | ||||||
|  |     full_decompile = other.full_decompile; | ||||||
|     labels_map = std::move(other.labels_map); |     labels_map = std::move(other.labels_map); | ||||||
|     labels_count = other.labels_count; |     labels_count = other.labels_count; | ||||||
|     gotos = std::move(other.gotos); |     gotos = std::move(other.gotos); | ||||||
| @ -434,6 +435,13 @@ void ASTManager::Decompile() { | |||||||
|         ASTNode goto_node = *it; |         ASTNode goto_node = *it; | ||||||
|         u32 label_index = goto_node->GetGotoLabel(); |         u32 label_index = goto_node->GetGotoLabel(); | ||||||
|         ASTNode label = labels[label_index]; |         ASTNode label = labels[label_index]; | ||||||
|  |         if (!full_decompile) { | ||||||
|  |             // We only decompile backward jumps
 | ||||||
|  |             if (!IsBackwardsJump(goto_node, label)) { | ||||||
|  |                 it++; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         if (IndirectlyRelated(goto_node, label)) { |         if (IndirectlyRelated(goto_node, label)) { | ||||||
|             while (!DirectlyRelated(goto_node, label)) { |             while (!DirectlyRelated(goto_node, label)) { | ||||||
|                 MoveOutward(goto_node); |                 MoveOutward(goto_node); | ||||||
| @ -469,11 +477,91 @@ void ASTManager::Decompile() { | |||||||
|         } |         } | ||||||
|         it++; |         it++; | ||||||
|     } |     } | ||||||
|  |     if (full_decompile) { | ||||||
|         for (ASTNode label : labels) { |         for (ASTNode label : labels) { | ||||||
|             auto& manager = label->GetManager(); |             auto& manager = label->GetManager(); | ||||||
|             manager.Remove(label); |             manager.Remove(label); | ||||||
|         } |         } | ||||||
|         labels.clear(); |         labels.clear(); | ||||||
|  |     } else { | ||||||
|  |         auto it = labels.begin(); | ||||||
|  |         while (it != labels.end()) { | ||||||
|  |             bool can_remove = true; | ||||||
|  |             ASTNode label = *it; | ||||||
|  |             for (ASTNode goto_node : gotos) { | ||||||
|  |                 u32 label_index = goto_node->GetGotoLabel(); | ||||||
|  |                 ASTNode glabel = labels[label_index]; | ||||||
|  |                 if (glabel == label) { | ||||||
|  |                     can_remove = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (can_remove) { | ||||||
|  |                 auto& manager = label->GetManager(); | ||||||
|  |                 manager.Remove(label); | ||||||
|  |                 labels.erase(it); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { | ||||||
|  |     u32 goto_level = goto_node->GetLevel(); | ||||||
|  |     u32 label_level = label_node->GetLevel(); | ||||||
|  |     while (goto_level > label_level) { | ||||||
|  |         goto_level--; | ||||||
|  |         goto_node = goto_node->GetParent(); | ||||||
|  |     } | ||||||
|  |     while (label_level > goto_level) { | ||||||
|  |         label_level--; | ||||||
|  |         label_node = label_node->GetParent(); | ||||||
|  |     } | ||||||
|  |     while (goto_node->GetParent() != label_node->GetParent()) { | ||||||
|  |         goto_node = goto_node->GetParent(); | ||||||
|  |         label_node = label_node->GetParent(); | ||||||
|  |     } | ||||||
|  |     ASTNode current = goto_node->GetPrevious(); | ||||||
|  |     while (current) { | ||||||
|  |         if (current == label_node) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         current = current->GetPrevious(); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ASTNode CommonParent(ASTNode first, ASTNode second) { | ||||||
|  |     if (first->GetParent() == second->GetParent()) { | ||||||
|  |         return first->GetParent(); | ||||||
|  |     } | ||||||
|  |     u32 first_level = first->GetLevel(); | ||||||
|  |     u32 second_level = second->GetLevel(); | ||||||
|  |     u32 min_level; | ||||||
|  |     u32 max_level; | ||||||
|  |     ASTNode max; | ||||||
|  |     ASTNode min; | ||||||
|  |     if (first_level > second_level) { | ||||||
|  |         min_level = second_level; | ||||||
|  |         min = second; | ||||||
|  |         max_level = first_level; | ||||||
|  |         max = first; | ||||||
|  |     } else { | ||||||
|  |         min_level = first_level; | ||||||
|  |         min = first; | ||||||
|  |         max_level = second_level; | ||||||
|  |         max = second; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while (max_level > min_level) { | ||||||
|  |         max_level--; | ||||||
|  |         max = max->GetParent(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while (min->GetParent() != max->GetParent()) { | ||||||
|  |         min = min->GetParent(); | ||||||
|  |         max = max->GetParent(); | ||||||
|  |     } | ||||||
|  |     return min->GetParent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { | bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { | ||||||
|  | |||||||
| @ -274,7 +274,7 @@ private: | |||||||
| 
 | 
 | ||||||
| class ASTManager final { | class ASTManager final { | ||||||
| public: | public: | ||||||
|     ASTManager(); |     ASTManager(bool full_decompile); | ||||||
|     ~ASTManager(); |     ~ASTManager(); | ||||||
| 
 | 
 | ||||||
|     ASTManager(const ASTManager& o) = delete; |     ASTManager(const ASTManager& o) = delete; | ||||||
| @ -304,7 +304,18 @@ public: | |||||||
|     void SanityCheck(); |     void SanityCheck(); | ||||||
| 
 | 
 | ||||||
|     bool IsFullyDecompiled() const { |     bool IsFullyDecompiled() const { | ||||||
|  |         if (full_decompile) { | ||||||
|             return gotos.size() == 0; |             return gotos.size() == 0; | ||||||
|  |         } else { | ||||||
|  |             for (ASTNode goto_node : gotos) { | ||||||
|  |                 u32 label_index = goto_node->GetGotoLabel(); | ||||||
|  |                 ASTNode glabel = labels[label_index]; | ||||||
|  |                 if (IsBackwardsJump(goto_node, glabel)) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ASTNode GetProgram() const { |     ASTNode GetProgram() const { | ||||||
| @ -318,6 +329,10 @@ public: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; | ||||||
|  | 
 | ||||||
|  |     ASTNode CommonParent(ASTNode first, ASTNode second); | ||||||
|  | 
 | ||||||
|     bool IndirectlyRelated(ASTNode first, ASTNode second); |     bool IndirectlyRelated(ASTNode first, ASTNode second); | ||||||
| 
 | 
 | ||||||
|     bool DirectlyRelated(ASTNode first, ASTNode second); |     bool DirectlyRelated(ASTNode first, ASTNode second); | ||||||
| @ -334,6 +349,7 @@ private: | |||||||
|         return new_var; |         return new_var; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool full_decompile{}; | ||||||
|     std::unordered_map<u32, u32> labels_map{}; |     std::unordered_map<u32, u32> labels_map{}; | ||||||
|     u32 labels_count{}; |     u32 labels_count{}; | ||||||
|     std::vector<ASTNode> labels{}; |     std::vector<ASTNode> labels{}; | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								src/video_core/shader/compiler_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/video_core/shader/compiler_settings.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | // Copyright 2019 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "video_core/shader/compiler_settings.h" | ||||||
|  | 
 | ||||||
|  | namespace VideoCommon::Shader { | ||||||
|  | 
 | ||||||
|  | std::string CompileDepthAsString(const CompileDepth cd) { | ||||||
|  |     switch (cd) { | ||||||
|  |     case CompileDepth::BruteForce: | ||||||
|  |         return "Brute Force Compile"; | ||||||
|  |     case CompileDepth::FlowStack: | ||||||
|  |         return "Simple Flow Stack Mode"; | ||||||
|  |     case CompileDepth::NoFlowStack: | ||||||
|  |         return "Remove Flow Stack"; | ||||||
|  |     case CompileDepth::DecompileBackwards: | ||||||
|  |         return "Decompile Backward Jumps"; | ||||||
|  |     case CompileDepth::FullDecompile: | ||||||
|  |         return "Full Decompilation"; | ||||||
|  |     default: | ||||||
|  |         return "Unknown Compiler Process"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCommon::Shader
 | ||||||
							
								
								
									
										25
									
								
								src/video_core/shader/compiler_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/video_core/shader/compiler_settings.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | // Copyright 2019 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "video_core/engines/shader_bytecode.h" | ||||||
|  | 
 | ||||||
|  | namespace VideoCommon::Shader { | ||||||
|  | 
 | ||||||
|  | enum class CompileDepth : u32 { | ||||||
|  |     BruteForce = 0, | ||||||
|  |     FlowStack = 1, | ||||||
|  |     NoFlowStack = 2, | ||||||
|  |     DecompileBackwards = 3, | ||||||
|  |     FullDecompile = 4, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::string CompileDepthAsString(CompileDepth cd); | ||||||
|  | 
 | ||||||
|  | struct CompilerSettings { | ||||||
|  |     CompileDepth depth; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCommon::Shader
 | ||||||
| @ -57,8 +57,8 @@ struct BlockInfo { | |||||||
| 
 | 
 | ||||||
| struct CFGRebuildState { | struct CFGRebuildState { | ||||||
|     explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, |     explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, | ||||||
|                              const u32 start, ASTManager& manager) |                              const u32 start) | ||||||
|         : program_code{program_code}, program_size{program_size}, start{start}, manager{manager} {} |         : program_code{program_code}, program_size{program_size}, start{start} {} | ||||||
| 
 | 
 | ||||||
|     u32 start{}; |     u32 start{}; | ||||||
|     std::vector<BlockInfo> block_info{}; |     std::vector<BlockInfo> block_info{}; | ||||||
| @ -71,7 +71,7 @@ struct CFGRebuildState { | |||||||
|     std::unordered_map<u32, BlockStack> stacks{}; |     std::unordered_map<u32, BlockStack> stacks{}; | ||||||
|     const ProgramCode& program_code; |     const ProgramCode& program_code; | ||||||
|     const std::size_t program_size; |     const std::size_t program_size; | ||||||
|     ASTManager& manager; |     ASTManager* manager; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum class BlockCollision : u32 { None, Found, Inside }; | enum class BlockCollision : u32 { None, Found, Inside }; | ||||||
| @ -456,67 +456,91 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DecompileShader(CFGRebuildState& state) { | void DecompileShader(CFGRebuildState& state) { | ||||||
|     state.manager.Init(); |     state.manager->Init(); | ||||||
|     for (auto label : state.labels) { |     for (auto label : state.labels) { | ||||||
|         state.manager.DeclareLabel(label); |         state.manager->DeclareLabel(label); | ||||||
|     } |     } | ||||||
|     for (auto& block : state.block_info) { |     for (auto& block : state.block_info) { | ||||||
|         if (state.labels.count(block.start) != 0) { |         if (state.labels.count(block.start) != 0) { | ||||||
|             state.manager.InsertLabel(block.start); |             state.manager->InsertLabel(block.start); | ||||||
|         } |         } | ||||||
|         u32 end = block.branch.ignore ? block.end + 1 : block.end; |         u32 end = block.branch.ignore ? block.end + 1 : block.end; | ||||||
|         state.manager.InsertBlock(block.start, end); |         state.manager->InsertBlock(block.start, end); | ||||||
|         if (!block.branch.ignore) { |         if (!block.branch.ignore) { | ||||||
|             InsertBranch(state.manager, block.branch); |             InsertBranch(*state.manager, block.branch); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     // state.manager.ShowCurrentState("Before Decompiling");
 |     state.manager->Decompile(); | ||||||
|     state.manager.Decompile(); |  | ||||||
|     // state.manager.ShowCurrentState("After Decompiling");
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | ||||||
|                                                 u32 start_address, ASTManager& manager) { |                                                 u32 start_address, | ||||||
|     CFGRebuildState state{program_code, program_size, start_address, manager}; |                                                 const CompilerSettings& settings) { | ||||||
|  |     auto result_out = std::make_unique<ShaderCharacteristics>(); | ||||||
|  |     if (settings.depth == CompileDepth::BruteForce) { | ||||||
|  |         result_out->settings.depth = CompileDepth::BruteForce; | ||||||
|  |         return std::move(result_out); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CFGRebuildState state{program_code, program_size, start_address}; | ||||||
|     // Inspect Code and generate blocks
 |     // Inspect Code and generate blocks
 | ||||||
|     state.labels.clear(); |     state.labels.clear(); | ||||||
|     state.labels.emplace(start_address); |     state.labels.emplace(start_address); | ||||||
|     state.inspect_queries.push_back(state.start); |     state.inspect_queries.push_back(state.start); | ||||||
|     while (!state.inspect_queries.empty()) { |     while (!state.inspect_queries.empty()) { | ||||||
|         if (!TryInspectAddress(state)) { |         if (!TryInspectAddress(state)) { | ||||||
|             return {}; |             result_out->settings.depth = CompileDepth::BruteForce; | ||||||
|  |             return std::move(result_out); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool use_flow_stack = true; | ||||||
|  | 
 | ||||||
|  |     bool decompiled = false; | ||||||
|  | 
 | ||||||
|  |     if (settings.depth != CompileDepth::FlowStack) { | ||||||
|         // Decompile Stacks
 |         // Decompile Stacks
 | ||||||
|         state.queries.push_back(Query{state.start, {}, {}}); |         state.queries.push_back(Query{state.start, {}, {}}); | ||||||
|     bool decompiled = true; |         decompiled = true; | ||||||
|         while (!state.queries.empty()) { |         while (!state.queries.empty()) { | ||||||
|             if (!TryQuery(state)) { |             if (!TryQuery(state)) { | ||||||
|                 decompiled = false; |                 decompiled = false; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     use_flow_stack = !decompiled; | ||||||
| 
 | 
 | ||||||
|     // Sort and organize results
 |     // Sort and organize results
 | ||||||
|     std::sort(state.block_info.begin(), state.block_info.end(), |     std::sort(state.block_info.begin(), state.block_info.end(), | ||||||
|               [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); |               [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); | ||||||
|     if (decompiled) { |     if (decompiled && settings.depth != CompileDepth::NoFlowStack) { | ||||||
|  |         ASTManager manager{settings.depth != CompileDepth::DecompileBackwards}; | ||||||
|  |         state.manager = &manager; | ||||||
|         DecompileShader(state); |         DecompileShader(state); | ||||||
|         decompiled = state.manager.IsFullyDecompiled(); |         decompiled = state.manager->IsFullyDecompiled(); | ||||||
|         if (!decompiled) { |         if (!decompiled) { | ||||||
|  |             if (settings.depth == CompileDepth::FullDecompile) { | ||||||
|                 LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); |                 LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); | ||||||
|             state.manager.ShowCurrentState("Of Shader"); |             } else { | ||||||
|             state.manager.Clear(); |                 LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:"); | ||||||
|         } |  | ||||||
|             } |             } | ||||||
|  |             state.manager->ShowCurrentState("Of Shader"); | ||||||
|  |             state.manager->Clear(); | ||||||
|  |         } else { | ||||||
|             auto result_out = std::make_unique<ShaderCharacteristics>(); |             auto result_out = std::make_unique<ShaderCharacteristics>(); | ||||||
|     result_out->decompiled = decompiled; |  | ||||||
|             result_out->start = start_address; |             result_out->start = start_address; | ||||||
|     if (decompiled) { |             result_out->settings.depth = settings.depth; | ||||||
|  |             result_out->manager = std::move(manager); | ||||||
|             result_out->end = state.block_info.back().end + 1; |             result_out->end = state.block_info.back().end + 1; | ||||||
|             return std::move(result_out); |             return std::move(result_out); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |     result_out->start = start_address; | ||||||
|  |     result_out->settings.depth = | ||||||
|  |         use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack; | ||||||
|  |     result_out->blocks.clear(); | ||||||
|     for (auto& block : state.block_info) { |     for (auto& block : state.block_info) { | ||||||
|         ShaderBlock new_block{}; |         ShaderBlock new_block{}; | ||||||
|         new_block.start = block.start; |         new_block.start = block.start; | ||||||
| @ -530,6 +554,10 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, | |||||||
|         result_out->end = std::max(result_out->end, block.end); |         result_out->end = std::max(result_out->end, block.end); | ||||||
|         result_out->blocks.push_back(new_block); |         result_out->blocks.push_back(new_block); | ||||||
|     } |     } | ||||||
|  |     if (!use_flow_stack) { | ||||||
|  |         result_out->labels = std::move(state.labels); | ||||||
|  |         return std::move(result_out); | ||||||
|  |     } | ||||||
|     auto back = result_out->blocks.begin(); |     auto back = result_out->blocks.begin(); | ||||||
|     auto next = std::next(back); |     auto next = std::next(back); | ||||||
|     while (next != result_out->blocks.end()) { |     while (next != result_out->blocks.end()) { | ||||||
|  | |||||||
| @ -9,8 +9,9 @@ | |||||||
| #include <set> | #include <set> | ||||||
| 
 | 
 | ||||||
| #include "video_core/engines/shader_bytecode.h" | #include "video_core/engines/shader_bytecode.h" | ||||||
| #include "video_core/shader/shader_ir.h" |  | ||||||
| #include "video_core/shader/ast.h" | #include "video_core/shader/ast.h" | ||||||
|  | #include "video_core/shader/compiler_settings.h" | ||||||
|  | #include "video_core/shader/shader_ir.h" | ||||||
| 
 | 
 | ||||||
| namespace VideoCommon::Shader { | namespace VideoCommon::Shader { | ||||||
| 
 | 
 | ||||||
| @ -68,12 +69,15 @@ struct ShaderBlock { | |||||||
| 
 | 
 | ||||||
| struct ShaderCharacteristics { | struct ShaderCharacteristics { | ||||||
|     std::list<ShaderBlock> blocks{}; |     std::list<ShaderBlock> blocks{}; | ||||||
|     bool decompiled{}; |     std::set<u32> labels{}; | ||||||
|     u32 start{}; |     u32 start{}; | ||||||
|     u32 end{}; |     u32 end{}; | ||||||
|  |     ASTManager manager{true}; | ||||||
|  |     CompilerSettings settings{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, | ||||||
|                                                 u32 start_address, ASTManager& manager); |                                                 u32 start_address, | ||||||
|  |                                                 const CompilerSettings& settings); | ||||||
| 
 | 
 | ||||||
| } // namespace VideoCommon::Shader
 | } // namespace VideoCommon::Shader
 | ||||||
|  | |||||||
| @ -102,36 +102,72 @@ void ShaderIR::Decode() { | |||||||
|     std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); |     std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | ||||||
| 
 | 
 | ||||||
|     decompiled = false; |     decompiled = false; | ||||||
|     const auto info = |     auto info = ScanFlow(program_code, program_size, main_offset, settings); | ||||||
|         ScanFlow(program_code, program_size, main_offset, program_manager); |     auto& shader_info = *info; | ||||||
|     if (info) { |  | ||||||
|         const auto& shader_info = *info; |  | ||||||
|     coverage_begin = shader_info.start; |     coverage_begin = shader_info.start; | ||||||
|     coverage_end = shader_info.end; |     coverage_end = shader_info.end; | ||||||
|         if (shader_info.decompiled) { |     switch (shader_info.settings.depth) { | ||||||
|  |     case CompileDepth::FlowStack: { | ||||||
|  |         for (const auto& block : shader_info.blocks) { | ||||||
|  |             basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case CompileDepth::NoFlowStack: { | ||||||
|  |         disable_flow_stack = true; | ||||||
|  |         const auto insert_block = [this](NodeBlock& nodes, u32 label) { | ||||||
|  |             if (label == static_cast<u32>(exit_branch)) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             basic_blocks.insert({label, nodes}); | ||||||
|  |         }; | ||||||
|  |         const auto& blocks = shader_info.blocks; | ||||||
|  |         NodeBlock current_block; | ||||||
|  |         u32 current_label = static_cast<u32>(exit_branch); | ||||||
|  |         for (auto& block : blocks) { | ||||||
|  |             if (shader_info.labels.count(block.start) != 0) { | ||||||
|  |                 insert_block(current_block, current_label); | ||||||
|  |                 current_block.clear(); | ||||||
|  |                 current_label = block.start; | ||||||
|  |             } | ||||||
|  |             if (!block.ignore_branch) { | ||||||
|  |                 DecodeRangeInner(current_block, block.start, block.end); | ||||||
|  |                 InsertControlFlow(current_block, block); | ||||||
|  |             } else { | ||||||
|  |                 DecodeRangeInner(current_block, block.start, block.end + 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         insert_block(current_block, current_label); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case CompileDepth::DecompileBackwards: | ||||||
|  |     case CompileDepth::FullDecompile: { | ||||||
|  |         program_manager = std::move(shader_info.manager); | ||||||
|  |         disable_flow_stack = true; | ||||||
|         decompiled = true; |         decompiled = true; | ||||||
|         ASTDecoder decoder{*this}; |         ASTDecoder decoder{*this}; | ||||||
|         ASTNode program = GetASTProgram(); |         ASTNode program = GetASTProgram(); | ||||||
|         decoder.Visit(program); |         decoder.Visit(program); | ||||||
|             return; |         break; | ||||||
|     } |     } | ||||||
|         LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method"); |     default: | ||||||
|         // we can't decompile it, fallback to standard method
 |         LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); | ||||||
|         for (const auto& block : shader_info.blocks) { |         [[fallthrough]]; | ||||||
|             basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); |     case CompileDepth::BruteForce: { | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling"); |  | ||||||
| 
 |  | ||||||
|     // Now we need to deal with an undecompilable shader. We need to brute force
 |  | ||||||
|     // a shader that captures every position.
 |  | ||||||
|         coverage_begin = main_offset; |         coverage_begin = main_offset; | ||||||
|         const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); |         const u32 shader_end = static_cast<u32>(program_size / sizeof(u64)); | ||||||
|         coverage_end = shader_end; |         coverage_end = shader_end; | ||||||
|         for (u32 label = main_offset; label < shader_end; label++) { |         for (u32 label = main_offset; label < shader_end; label++) { | ||||||
|             basic_blocks.insert({label, DecodeRange(label, label + 1)}); |             basic_blocks.insert({label, DecodeRange(label, label + 1)}); | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     } | ||||||
|  |     if (settings.depth != shader_info.settings.depth) { | ||||||
|  |         LOG_WARNING( | ||||||
|  |             HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", | ||||||
|  |             CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth)); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { | NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { | ||||||
|  | |||||||
| @ -157,7 +157,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||||||
|         UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |         UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, | ||||||
|                              "Constant buffer flow is not supported"); |                              "Constant buffer flow is not supported"); | ||||||
| 
 | 
 | ||||||
|         if (decompiled) { |         if (disable_flow_stack) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -171,7 +171,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||||||
|         UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, |         UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, | ||||||
|                              "Constant buffer PBK is not supported"); |                              "Constant buffer PBK is not supported"); | ||||||
| 
 | 
 | ||||||
|         if (decompiled) { |         if (disable_flow_stack) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -186,7 +186,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||||||
|         UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", |         UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", | ||||||
|                              static_cast<u32>(cc)); |                              static_cast<u32>(cc)); | ||||||
| 
 | 
 | ||||||
|         if (decompiled) { |         if (disable_flow_stack) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -198,7 +198,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||||||
|         const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; |         const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | ||||||
|         UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", |         UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", | ||||||
|                              static_cast<u32>(cc)); |                              static_cast<u32>(cc)); | ||||||
|         if (decompiled) { |         if (disable_flow_stack) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,8 +22,10 @@ using Tegra::Shader::PredCondition; | |||||||
| using Tegra::Shader::PredOperation; | using Tegra::Shader::PredOperation; | ||||||
| using Tegra::Shader::Register; | using Tegra::Shader::Register; | ||||||
| 
 | 
 | ||||||
| ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size) | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size, | ||||||
|     : program_code{program_code}, main_offset{main_offset}, program_size{size}, program_manager{} { |                    CompilerSettings settings) | ||||||
|  |     : program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{}, | ||||||
|  |       program_manager{true}, settings{settings} { | ||||||
|     Decode(); |     Decode(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ | |||||||
| #include "video_core/engines/shader_bytecode.h" | #include "video_core/engines/shader_bytecode.h" | ||||||
| #include "video_core/engines/shader_header.h" | #include "video_core/engines/shader_header.h" | ||||||
| #include "video_core/shader/ast.h" | #include "video_core/shader/ast.h" | ||||||
|  | #include "video_core/shader/compiler_settings.h" | ||||||
| #include "video_core/shader/node.h" | #include "video_core/shader/node.h" | ||||||
| 
 | 
 | ||||||
| namespace VideoCommon::Shader { | namespace VideoCommon::Shader { | ||||||
| @ -65,7 +66,8 @@ struct GlobalMemoryUsage { | |||||||
| 
 | 
 | ||||||
| class ShaderIR final { | class ShaderIR final { | ||||||
| public: | public: | ||||||
|     explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size); |     explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size, | ||||||
|  |                       CompilerSettings settings); | ||||||
|     ~ShaderIR(); |     ~ShaderIR(); | ||||||
| 
 | 
 | ||||||
|     const std::map<u32, NodeBlock>& GetBasicBlocks() const { |     const std::map<u32, NodeBlock>& GetBasicBlocks() const { | ||||||
| @ -141,6 +143,10 @@ public: | |||||||
|         return header; |         return header; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool IsFlowStackDisabled() const { | ||||||
|  |         return disable_flow_stack; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     bool IsDecompiled() const { |     bool IsDecompiled() const { | ||||||
|         return decompiled; |         return decompiled; | ||||||
|     } |     } | ||||||
| @ -368,6 +374,7 @@ private: | |||||||
|     const u32 main_offset; |     const u32 main_offset; | ||||||
|     const std::size_t program_size; |     const std::size_t program_size; | ||||||
|     bool decompiled{}; |     bool decompiled{}; | ||||||
|  |     bool disable_flow_stack{}; | ||||||
| 
 | 
 | ||||||
|     u32 coverage_begin{}; |     u32 coverage_begin{}; | ||||||
|     u32 coverage_end{}; |     u32 coverage_end{}; | ||||||
| @ -375,6 +382,7 @@ private: | |||||||
|     std::map<u32, NodeBlock> basic_blocks; |     std::map<u32, NodeBlock> basic_blocks; | ||||||
|     NodeBlock global_code; |     NodeBlock global_code; | ||||||
|     ASTManager program_manager; |     ASTManager program_manager; | ||||||
|  |     CompilerSettings settings{}; | ||||||
| 
 | 
 | ||||||
|     std::set<u32> used_registers; |     std::set<u32> used_registers; | ||||||
|     std::set<Tegra::Shader::Pred> used_predicates; |     std::set<Tegra::Shader::Pred> used_predicates; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user