summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-04-07 23:48:38 -0400
committerGravatar bunnei2018-04-13 23:48:28 -0400
commit85d77a3d24f17040791fe66cc1278713cfb487ae (patch)
treea2237d550d391e4d313fe64f5209048d3b685f6d /src
parentgl_shader_manager: Cleanup and consolidate uniform handling. (diff)
downloadyuzu-85d77a3d24f17040791fe66cc1278713cfb487ae.tar.gz
yuzu-85d77a3d24f17040791fe66cc1278713cfb487ae.tar.xz
yuzu-85d77a3d24f17040791fe66cc1278713cfb487ae.zip
gl_shader_decompiler: Basic impl. for very simple vertex shaders.
- Tested with Puyo Puyo Tetris and Cave Story+
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp315
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h12
2 files changed, 311 insertions, 16 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 3fc420649..60857c623 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -10,10 +10,15 @@
10#include "video_core/engines/shader_bytecode.h" 10#include "video_core/engines/shader_bytecode.h"
11#include "video_core/renderer_opengl/gl_shader_decompiler.h" 11#include "video_core/renderer_opengl/gl_shader_decompiler.h"
12 12
13namespace Tegra { 13namespace GLShader {
14namespace Shader {
15namespace Decompiler { 14namespace Decompiler {
16 15
16using Tegra::Shader::Attribute;
17using Tegra::Shader::Instruction;
18using Tegra::Shader::OpCode;
19using Tegra::Shader::Register;
20using Tegra::Shader::Uniform;
21
17constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 22constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
18 23
19class DecompileFail : public std::runtime_error { 24class DecompileFail : public std::runtime_error {
@@ -90,7 +95,7 @@ private:
90 95
91 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 96 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
92 const Instruction instr = {program_code[offset]}; 97 const Instruction instr = {program_code[offset]};
93 switch (instr.opcode.Value().EffectiveOpCode()) { 98 switch (instr.opcode.EffectiveOpCode()) {
94 case OpCode::Id::EXIT: { 99 case OpCode::Id::EXIT: {
95 return exit_method = ExitMethod::AlwaysEnd; 100 return exit_method = ExitMethod::AlwaysEnd;
96 } 101 }
@@ -130,7 +135,294 @@ public:
130 } 135 }
131 136
132 std::string GetShaderCode() { 137 std::string GetShaderCode() {
133 return shader.GetResult(); 138 return declarations.GetResult() + shader.GetResult();
139 }
140
141private:
142 /// Gets the Subroutine object corresponding to the specified address.
143 const Subroutine& GetSubroutine(u32 begin, u32 end) const {
144 auto iter = subroutines.find(Subroutine{begin, end});
145 ASSERT(iter != subroutines.end());
146 return *iter;
147 }
148
149 /// Generates code representing an input attribute register.
150 std::string GetInputAttribute(Attribute::Index attribute) {
151 declr_input_attribute.insert(attribute);
152
153 const u32 index{static_cast<u32>(attribute) -
154 static_cast<u32>(Attribute::Index::Attribute_0)};
155 if (attribute >= Attribute::Index::Attribute_0) {
156 return "input_attribute_" + std::to_string(index);
157 }
158
159 LOG_ERROR(HW_GPU, "Unhandled input attribute: 0x%02x", index);
160 UNREACHABLE();
161 }
162
163 /// Generates code representing an output attribute register.
164 std::string GetOutputAttribute(Attribute::Index attribute) {
165 switch (attribute) {
166 case Attribute::Index::Position:
167 return "gl_Position";
168 default:
169 const u32 index{static_cast<u32>(attribute) -
170 static_cast<u32>(Attribute::Index::Attribute_0)};
171 if (attribute >= Attribute::Index::Attribute_0) {
172 declr_output_attribute.insert(attribute);
173 return "output_attribute_" + std::to_string(index);
174 }
175
176 LOG_ERROR(HW_GPU, "Unhandled output attribute: 0x%02x", index);
177 UNREACHABLE();
178 }
179 }
180
181 /// Generates code representing a temporary (GPR) register.
182 std::string GetRegister(const Register& reg) {
183 return *declr_register.insert("register_" + std::to_string(reg)).first;
184 }
185
186 /// Generates code representing a uniform (C buffer) register.
187 std::string GetUniform(const Uniform& reg) const {
188 std::string index = std::to_string(reg.index);
189 return "uniform_" + index + "[" + std::to_string(reg.offset >> 2) + "][" +
190 std::to_string(reg.offset & 3) + "]";
191 }
192
193 /**
194 * Adds code that calls a subroutine.
195 * @param subroutine the subroutine to call.
196 */
197 void CallSubroutine(const Subroutine& subroutine) {
198 if (subroutine.exit_method == ExitMethod::AlwaysEnd) {
199 shader.AddLine(subroutine.GetName() + "();");
200 shader.AddLine("return true;");
201 } else if (subroutine.exit_method == ExitMethod::Conditional) {
202 shader.AddLine("if (" + subroutine.GetName() + "()) { return true; }");
203 } else {
204 shader.AddLine(subroutine.GetName() + "();");
205 }
206 }
207
208 /**
209 * Writes code that does an assignment operation.
210 * @param reg the destination register code.
211 * @param value the code representing the value to assign.
212 */
213 void SetDest(u64 elem, const std::string& reg, const std::string& value,
214 u64 dest_num_components, u64 value_num_components) {
215 std::string swizzle = ".";
216 swizzle += "xyzw"[elem];
217
218 std::string dest = reg + (dest_num_components != 1 ? swizzle : "");
219 std::string src = "(" + value + ")" + (value_num_components != 1 ? swizzle : "");
220
221 shader.AddLine(dest + " = " + src + ";");
222 }
223
224 /**
225 * Compiles a single instruction from Tegra to GLSL.
226 * @param offset the offset of the Tegra shader instruction.
227 * @return the offset of the next instruction to execute. Usually it is the current offset
228 * + 1. If the current instruction always terminates the program, returns PROGRAM_END.
229 */
230 u32 CompileInstr(u32 offset) {
231 const Instruction instr = {program_code[offset]};
232
233 shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name);
234
235 switch (OpCode::GetInfo(instr.opcode).type) {
236 case OpCode::Type::Arithmetic: {
237 ASSERT(!instr.nb);
238 ASSERT(!instr.aa);
239 ASSERT(!instr.na);
240 ASSERT(!instr.ab);
241 ASSERT(!instr.ad);
242
243 std::string gpr1 = GetRegister(instr.gpr1);
244 std::string gpr2 = GetRegister(instr.gpr2);
245 std::string uniform = GetUniform(instr.uniform);
246
247 switch (instr.opcode.EffectiveOpCode()) {
248 case OpCode::Id::FMUL_C: {
249 SetDest(0, gpr1, gpr2 + " * " + uniform, 1, 1);
250 break;
251 }
252 case OpCode::Id::FADD_C: {
253 SetDest(0, gpr1, gpr2 + " + " + uniform, 1, 1);
254 break;
255 }
256 case OpCode::Id::FFMA_CR: {
257 SetDest(0, gpr1, gpr2 + " * " + uniform + " + " + GetRegister(instr.gpr3), 1, 1);
258 break;
259 }
260 default: {
261 LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x",
262 (int)instr.opcode.EffectiveOpCode(), OpCode::GetInfo(instr.opcode).name,
263 instr.hex);
264 throw DecompileFail("Unhandled instruction");
265 break;
266 }
267 }
268 break;
269 }
270 case OpCode::Type::Memory: {
271 ASSERT(instr.attribute.size == 0);
272
273 std::string gpr1 = GetRegister(instr.gpr1);
274 const Attribute::Index attribute = instr.attribute.GetIndex();
275
276 switch (instr.opcode.EffectiveOpCode()) {
277 case OpCode::Id::LD_A: {
278 SetDest(instr.attribute.element, gpr1, GetInputAttribute(attribute), 1, 4);
279 break;
280 }
281 case OpCode::Id::ST_A: {
282 SetDest(instr.attribute.element, GetOutputAttribute(attribute), gpr1, 4, 1);
283 break;
284 }
285 default: {
286 LOG_ERROR(HW_GPU, "Unhandled memory instruction: 0x%02x (%s): 0x%08x",
287 (int)instr.opcode.EffectiveOpCode(), OpCode::GetInfo(instr.opcode).name,
288 instr.hex);
289 throw DecompileFail("Unhandled instruction");
290 break;
291 }
292 }
293 break;
294 }
295
296 default: {
297 switch (instr.opcode.EffectiveOpCode()) {
298 case OpCode::Id::EXIT: {
299 shader.AddLine("return true;");
300 offset = PROGRAM_END - 1;
301 break;
302 }
303
304 default: {
305 LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
306 (int)instr.opcode.EffectiveOpCode(),
307 OpCode::GetInfo(instr.opcode).name.c_str(), instr.hex);
308 // throw DecompileFail("Unhandled instruction");
309 break;
310 }
311 }
312
313 break;
314 }
315 }
316
317 return offset + 1;
318 }
319
320 /**
321 * Compiles a range of instructions from Tegra to GLSL.
322 * @param begin the offset of the starting instruction.
323 * @param end the offset where the compilation should stop (exclusive).
324 * @return the offset of the next instruction to compile. PROGRAM_END if the program
325 * terminates.
326 */
327 u32 CompileRange(u32 begin, u32 end) {
328 u32 program_counter;
329 for (program_counter = begin; program_counter < (begin > end ? PROGRAM_END : end);) {
330 program_counter = CompileInstr(program_counter);
331 }
332 return program_counter;
333 }
334
335 void Generate() {
336 // Add declarations for all subroutines
337 for (const auto& subroutine : subroutines) {
338 shader.AddLine("bool " + subroutine.GetName() + "();");
339 }
340 shader.AddLine("");
341
342 // Add the main entry point
343 shader.AddLine("bool exec_shader() {");
344 ++shader.scope;
345 CallSubroutine(GetSubroutine(main_offset, PROGRAM_END));
346 --shader.scope;
347 shader.AddLine("}\n");
348
349 // Add definitions for all subroutines
350 for (const auto& subroutine : subroutines) {
351 std::set<u32> labels = subroutine.labels;
352
353 shader.AddLine("bool " + subroutine.GetName() + "() {");
354 ++shader.scope;
355
356 if (labels.empty()) {
357 if (CompileRange(subroutine.begin, subroutine.end) != PROGRAM_END) {
358 shader.AddLine("return false;");
359 }
360 } else {
361 labels.insert(subroutine.begin);
362 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
363 shader.AddLine("while (true) {");
364 ++shader.scope;
365
366 shader.AddLine("switch (jmp_to) {");
367
368 for (auto label : labels) {
369 shader.AddLine("case " + std::to_string(label) + "u: {");
370 ++shader.scope;
371
372 auto next_it = labels.lower_bound(label + 1);
373 u32 next_label = next_it == labels.end() ? subroutine.end : *next_it;
374
375 u32 compile_end = CompileRange(label, next_label);
376 if (compile_end > next_label && compile_end != PROGRAM_END) {
377 // This happens only when there is a label inside a IF/LOOP block
378 shader.AddLine("{ jmp_to = " + std::to_string(compile_end) + "u; break; }");
379 labels.emplace(compile_end);
380 }
381
382 --shader.scope;
383 shader.AddLine("}");
384 }
385
386 shader.AddLine("default: return false;");
387 shader.AddLine("}");
388
389 --shader.scope;
390 shader.AddLine("}");
391
392 shader.AddLine("return false;");
393 }
394
395 --shader.scope;
396 shader.AddLine("}\n");
397
398 DEBUG_ASSERT(shader.scope == 0);
399 }
400
401 GenerateDeclarations();
402 }
403
404 /// Add declarations for registers
405 void GenerateDeclarations() {
406 for (const auto& reg : declr_register) {
407 declarations.AddLine("float " + reg + " = 0.0;");
408 }
409 declarations.AddLine("");
410
411 for (const auto& index : declr_input_attribute) {
412 // TODO(bunnei): Use proper number of elements for these
413 declarations.AddLine(
414 "layout(location = " + std::to_string(static_cast<u32>(index) - 8) + ") in vec4 " +
415 GetInputAttribute(index) + ";");
416 }
417 declarations.AddLine("");
418
419 for (const auto& index : declr_output_attribute) {
420 // TODO(bunnei): Use proper number of elements for these
421 declarations.AddLine(
422 "layout(location = " + std::to_string(static_cast<u32>(index) - 8) + ") out vec4 " +
423 GetOutputAttribute(index) + ";");
424 }
425 declarations.AddLine("");
134 } 426 }
135 427
136private: 428private:
@@ -139,9 +431,17 @@ private:
139 const u32 main_offset; 431 const u32 main_offset;
140 432
141 ShaderWriter shader; 433 ShaderWriter shader;
434 ShaderWriter declarations;
142 435
143 void Generate() {} 436 // Declarations
144}; 437 std::set<std::string> declr_register;
438 std::set<Attribute::Index> declr_input_attribute;
439 std::set<Attribute::Index> declr_output_attribute;
440}; // namespace Decompiler
441
442std::string GetCommonDeclarations() {
443 return "bool exec_shader();";
444}
145 445
146boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset) { 446boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset) {
147 try { 447 try {
@@ -155,5 +455,4 @@ boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u
155} 455}
156 456
157} // namespace Decompiler 457} // namespace Decompiler
158} // namespace Shader 458} // namespace GLShader
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 628f02c93..061dd6102 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -7,18 +7,14 @@
7#include <string> 7#include <string>
8#include <boost/optional.hpp> 8#include <boost/optional.hpp>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_opengl/gl_shader_gen.h"
10 11
11namespace Tegra { 12namespace GLShader {
12namespace Shader {
13namespace Decompiler { 13namespace Decompiler {
14 14
15constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100}; 15std::string GetCommonDeclarations();
16constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100};
17
18using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>;
19 16
20boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset); 17boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset);
21 18
22} // namespace Decompiler 19} // namespace Decompiler
23} // namespace Shader 20} // namespace GLShader
24} // namespace Tegra