diff options
| author | 2018-04-07 23:48:38 -0400 | |
|---|---|---|
| committer | 2018-04-13 23:48:28 -0400 | |
| commit | 85d77a3d24f17040791fe66cc1278713cfb487ae (patch) | |
| tree | a2237d550d391e4d313fe64f5209048d3b685f6d /src | |
| parent | gl_shader_manager: Cleanup and consolidate uniform handling. (diff) | |
| download | yuzu-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.cpp | 315 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.h | 12 |
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 | ||
| 13 | namespace Tegra { | 13 | namespace GLShader { |
| 14 | namespace Shader { | ||
| 15 | namespace Decompiler { | 14 | namespace Decompiler { |
| 16 | 15 | ||
| 16 | using Tegra::Shader::Attribute; | ||
| 17 | using Tegra::Shader::Instruction; | ||
| 18 | using Tegra::Shader::OpCode; | ||
| 19 | using Tegra::Shader::Register; | ||
| 20 | using Tegra::Shader::Uniform; | ||
| 21 | |||
| 17 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; | 22 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
| 18 | 23 | ||
| 19 | class DecompileFail : public std::runtime_error { | 24 | class 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 | |||
| 141 | private: | ||
| 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 | ||
| 136 | private: | 428 | private: |
| @@ -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 | |||
| 442 | std::string GetCommonDeclarations() { | ||
| 443 | return "bool exec_shader();"; | ||
| 444 | } | ||
| 145 | 445 | ||
| 146 | boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset) { | 446 | boost::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 | ||
| 11 | namespace Tegra { | 12 | namespace GLShader { |
| 12 | namespace Shader { | ||
| 13 | namespace Decompiler { | 13 | namespace Decompiler { |
| 14 | 14 | ||
| 15 | constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100}; | 15 | std::string GetCommonDeclarations(); |
| 16 | constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100}; | ||
| 17 | |||
| 18 | using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>; | ||
| 19 | 16 | ||
| 20 | boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset); | 17 | boost::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 | ||