summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-04-04 21:44:35 -0400
committerGravatar bunnei2018-04-13 23:48:20 -0400
commited7e597b4494f770f4907560af0aa778d7762226 (patch)
treee52e78746d267dc85545338de95789d3a54150a9 /src
parentshader_bytecode: Add initial module for shader decoding. (diff)
downloadyuzu-ed7e597b4494f770f4907560af0aa778d7762226.tar.gz
yuzu-ed7e597b4494f770f4907560af0aa778d7762226.tar.xz
yuzu-ed7e597b4494f770f4907560af0aa778d7762226.zip
gl_shader_decompiler: Add skeleton code from Citra for shader analysis.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp167
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h19
2 files changed, 142 insertions, 44 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 564ea8f9e..3fc420649 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -2,57 +2,158 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <set>
5#include <string> 7#include <string>
6#include <queue>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/engines/shader_bytecode.h"
9#include "video_core/renderer_opengl/gl_shader_decompiler.h" 11#include "video_core/renderer_opengl/gl_shader_decompiler.h"
10 12
11namespace Maxwell3D { 13namespace Tegra {
12namespace Shader { 14namespace Shader {
13namespace Decompiler { 15namespace Decompiler {
14 16
15constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 17constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
16 18
17class Impl { 19class DecompileFail : public std::runtime_error {
18public: 20public:
19 Impl(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, 21 using std::runtime_error::runtime_error;
20 const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, u32 main_offset, 22};
21 const std::function<std::string(u32)>& inputreg_getter, 23
22 const std::function<std::string(u32)>& outputreg_getter, bool sanitize_mul, 24/// Describes the behaviour of code path of a given entry point and a return point.
23 const std::string& emit_cb, const std::string& setemit_cb) 25enum class ExitMethod {
24 : program_code(program_code), swizzle_data(swizzle_data), main_offset(main_offset), 26 Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
25 inputreg_getter(inputreg_getter), outputreg_getter(outputreg_getter), 27 AlwaysReturn, ///< All code paths reach the return point.
26 sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {} 28 Conditional, ///< Code path reaches the return point or an END instruction conditionally.
29 AlwaysEnd, ///< All code paths reach a END instruction.
30};
31
32/// A subroutine is a range of code refereced by a CALL, IF or LOOP instruction.
33struct Subroutine {
34 /// Generates a name suitable for GLSL source code.
35 std::string GetName() const {
36 return "sub_" + std::to_string(begin) + "_" + std::to_string(end);
37 }
38
39 u32 begin; ///< Entry point of the subroutine.
40 u32 end; ///< Return point of the subroutine.
41 ExitMethod exit_method; ///< Exit method of the subroutine.
42 std::set<u32> labels; ///< Addresses refereced by JMP instructions.
43
44 bool operator<(const Subroutine& rhs) const {
45 return std::tie(begin, end) < std::tie(rhs.begin, rhs.end);
46 }
47};
48
49/// Analyzes shader code and produces a set of subroutines.
50class ControlFlowAnalyzer {
51public:
52 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset)
53 : program_code(program_code) {
54
55 // Recursively finds all subroutines.
56 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END);
57 if (program_main.exit_method != ExitMethod::AlwaysEnd)
58 throw DecompileFail("Program does not always end");
59 }
60
61 std::set<Subroutine> GetSubroutines() {
62 return std::move(subroutines);
63 }
64
65private:
66 const ProgramCode& program_code;
67 std::set<Subroutine> subroutines;
68 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
69
70 /// Adds and analyzes a new subroutine if it is not added yet.
71 const Subroutine& AddSubroutine(u32 begin, u32 end) {
72 auto iter = subroutines.find(Subroutine{begin, end});
73 if (iter != subroutines.end())
74 return *iter;
75
76 Subroutine subroutine{begin, end};
77 subroutine.exit_method = Scan(begin, end, subroutine.labels);
78 if (subroutine.exit_method == ExitMethod::Undetermined)
79 throw DecompileFail("Recursive function detected");
80 return *subroutines.insert(std::move(subroutine)).first;
81 }
27 82
28 std::string Decompile() { 83 /// Scans a range of code for labels and determines the exit method.
29 UNREACHABLE(); 84 ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) {
30 return {}; 85 auto [iter, inserted] =
86 exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined);
87 ExitMethod& exit_method = iter->second;
88 if (!inserted)
89 return exit_method;
90
91 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
92 const Instruction instr = {program_code[offset]};
93 switch (instr.opcode.Value().EffectiveOpCode()) {
94 case OpCode::Id::EXIT: {
95 return exit_method = ExitMethod::AlwaysEnd;
96 }
97 }
98 }
99 return exit_method = ExitMethod::AlwaysReturn;
100 }
101};
102
103class ShaderWriter {
104public:
105 void AddLine(const std::string& text) {
106 DEBUG_ASSERT(scope >= 0);
107 if (!text.empty()) {
108 shader_source += std::string(static_cast<size_t>(scope) * 4, ' ');
109 }
110 shader_source += text + '\n';
111 }
112
113 std::string GetResult() {
114 return std::move(shader_source);
115 }
116
117 int scope = 0;
118
119private:
120 std::string shader_source;
121};
122
123class GLSLGenerator {
124public:
125 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
126 u32 main_offset)
127 : subroutines(subroutines), program_code(program_code), main_offset(main_offset) {
128
129 Generate();
130 }
131
132 std::string GetShaderCode() {
133 return shader.GetResult();
31 } 134 }
32 135
33private: 136private:
34 const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code; 137 const std::set<Subroutine>& subroutines;
35 const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data; 138 const ProgramCode& program_code;
36 u32 main_offset; 139 const u32 main_offset;
37 const std::function<std::string(u32)>& inputreg_getter; 140
38 const std::function<std::string(u32)>& outputreg_getter; 141 ShaderWriter shader;
39 bool sanitize_mul; 142
40 const std::string& emit_cb; 143 void Generate() {}
41 const std::string& setemit_cb;
42}; 144};
43 145
44std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, 146boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset) {
45 const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, 147 try {
46 u32 main_offset, 148 auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
47 const std::function<std::string(u32)>& inputreg_getter, 149 GLSLGenerator generator(subroutines, program_code, main_offset);
48 const std::function<std::string(u32)>& outputreg_getter, 150 return generator.GetShaderCode();
49 bool sanitize_mul, const std::string& emit_cb, 151 } catch (const DecompileFail& exception) {
50 const std::string& setemit_cb) { 152 LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what());
51 Impl impl(program_code, swizzle_data, main_offset, inputreg_getter, outputreg_getter, 153 }
52 sanitize_mul, emit_cb, setemit_cb); 154 return boost::none;
53 return impl.Decompile();
54} 155}
55 156
56} // namespace Decompiler 157} // namespace Decompiler
57} // namespace Shader 158} // namespace Shader
58} // namespace Maxwell3D 159} // namespace Tegra
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 02ebfcbe8..628f02c93 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -5,23 +5,20 @@
5#include <array> 5#include <array>
6#include <functional> 6#include <functional>
7#include <string> 7#include <string>
8#include <boost/optional.hpp>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
10namespace Maxwell3D { 11namespace Tegra {
11namespace Shader { 12namespace Shader {
12namespace Decompiler { 13namespace Decompiler {
13 14
14constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100000}; 15constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100};
15constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100000}; 16constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100};
16 17
17std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, 18using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>;
18 const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, 19
19 u32 main_offset, 20boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset);
20 const std::function<std::string(u32)>& inputreg_getter,
21 const std::function<std::string(u32)>& outputreg_getter,
22 bool sanitize_mul, const std::string& emit_cb = "",
23 const std::string& setemit_cb = "");
24 21
25} // namespace Decompiler 22} // namespace Decompiler
26} // namespace Shader 23} // namespace Shader
27} // namespace Maxwell3D 24} // namespace Tegra