diff options
| author | 2018-04-20 22:29:39 -0400 | |
|---|---|---|
| committer | 2018-04-20 22:29:39 -0400 | |
| commit | 8ac3a3f45e2102da5cc3b56e4be9b5e766f72358 (patch) | |
| tree | c74be88544f6465fe3afad898654dced8d51f006 /src | |
| parent | Merge pull request #374 from lioncash/noexcept (diff) | |
| parent | ShaderGen: Implemented the KIL instruction, which is equivalent to 'discard'. (diff) | |
| download | yuzu-8ac3a3f45e2102da5cc3b56e4be9b5e766f72358.tar.gz yuzu-8ac3a3f45e2102da5cc3b56e4be9b5e766f72358.tar.xz yuzu-8ac3a3f45e2102da5cc3b56e4be9b5e766f72358.zip | |
Merge pull request #369 from Subv/shader_instr2
ShaderGen: Implemented fsetp/kil and predicated instruction execution.
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/engines/shader_bytecode.h | 52 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 131 |
2 files changed, 179 insertions, 4 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 7cd125f05..e6c2fd367 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -13,6 +13,9 @@ namespace Tegra { | |||
| 13 | namespace Shader { | 13 | namespace Shader { |
| 14 | 14 | ||
| 15 | struct Register { | 15 | struct Register { |
| 16 | // Register 255 is special cased to always be 0 | ||
| 17 | static constexpr size_t ZeroIndex = 255; | ||
| 18 | |||
| 16 | constexpr Register() = default; | 19 | constexpr Register() = default; |
| 17 | 20 | ||
| 18 | constexpr Register(u64 value) : value(value) {} | 21 | constexpr Register(u64 value) : value(value) {} |
| @@ -106,6 +109,8 @@ union OpCode { | |||
| 106 | 109 | ||
| 107 | FSETP_R = 0x5BB, | 110 | FSETP_R = 0x5BB, |
| 108 | FSETP_C = 0x4BB, | 111 | FSETP_C = 0x4BB, |
| 112 | FSETP_IMM = 0x36B, | ||
| 113 | FSETP_NEG_IMM = 0x37B, | ||
| 109 | EXIT = 0xE30, | 114 | EXIT = 0xE30, |
| 110 | KIL = 0xE33, | 115 | KIL = 0xE33, |
| 111 | 116 | ||
| @@ -121,6 +126,7 @@ union OpCode { | |||
| 121 | Ffma, | 126 | Ffma, |
| 122 | Flow, | 127 | Flow, |
| 123 | Memory, | 128 | Memory, |
| 129 | FloatPredicate, | ||
| 124 | Unknown, | 130 | Unknown, |
| 125 | }; | 131 | }; |
| 126 | 132 | ||
| @@ -161,6 +167,9 @@ union OpCode { | |||
| 161 | case Id::FSETP_C: | 167 | case Id::FSETP_C: |
| 162 | case Id::KIL: | 168 | case Id::KIL: |
| 163 | return op4; | 169 | return op4; |
| 170 | case Id::FSETP_IMM: | ||
| 171 | case Id::FSETP_NEG_IMM: | ||
| 172 | return Id::FSETP_IMM; | ||
| 164 | } | 173 | } |
| 165 | 174 | ||
| 166 | switch (op5) { | 175 | switch (op5) { |
| @@ -238,8 +247,9 @@ union OpCode { | |||
| 238 | info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"}; | 247 | info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"}; |
| 239 | info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"}; | 248 | info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"}; |
| 240 | info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"}; | 249 | info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"}; |
| 241 | info_table[Id::FSETP_C] = {Type::Arithmetic, "fsetp_c"}; | 250 | info_table[Id::FSETP_C] = {Type::FloatPredicate, "fsetp_c"}; |
| 242 | info_table[Id::FSETP_R] = {Type::Arithmetic, "fsetp_r"}; | 251 | info_table[Id::FSETP_R] = {Type::FloatPredicate, "fsetp_r"}; |
| 252 | info_table[Id::FSETP_IMM] = {Type::FloatPredicate, "fsetp_imm"}; | ||
| 243 | info_table[Id::EXIT] = {Type::Trivial, "exit"}; | 253 | info_table[Id::EXIT] = {Type::Trivial, "exit"}; |
| 244 | info_table[Id::IPA] = {Type::Trivial, "ipa"}; | 254 | info_table[Id::IPA] = {Type::Trivial, "ipa"}; |
| 245 | info_table[Id::KIL] = {Type::Flow, "kil"}; | 255 | info_table[Id::KIL] = {Type::Flow, "kil"}; |
| @@ -283,7 +293,23 @@ namespace Shader { | |||
| 283 | 293 | ||
| 284 | enum class Pred : u64 { | 294 | enum class Pred : u64 { |
| 285 | UnusedIndex = 0x7, | 295 | UnusedIndex = 0x7, |
| 286 | NeverExecute = 0xf, | 296 | NeverExecute = 0xF, |
| 297 | }; | ||
| 298 | |||
| 299 | enum class PredCondition : u64 { | ||
| 300 | LessThan = 1, | ||
| 301 | Equal = 2, | ||
| 302 | LessEqual = 3, | ||
| 303 | GreaterThan = 4, | ||
| 304 | NotEqual = 5, | ||
| 305 | GreaterEqual = 6, | ||
| 306 | // TODO(Subv): Other condition types | ||
| 307 | }; | ||
| 308 | |||
| 309 | enum class PredOperation : u64 { | ||
| 310 | And = 0, | ||
| 311 | Or = 1, | ||
| 312 | Xor = 2, | ||
| 287 | }; | 313 | }; |
| 288 | 314 | ||
| 289 | enum class SubOp : u64 { | 315 | enum class SubOp : u64 { |
| @@ -305,7 +331,11 @@ union Instruction { | |||
| 305 | OpCode opcode; | 331 | OpCode opcode; |
| 306 | BitField<0, 8, Register> gpr0; | 332 | BitField<0, 8, Register> gpr0; |
| 307 | BitField<8, 8, Register> gpr8; | 333 | BitField<8, 8, Register> gpr8; |
| 308 | BitField<16, 4, Pred> pred; | 334 | union { |
| 335 | BitField<16, 4, Pred> full_pred; | ||
| 336 | BitField<16, 3, u64> pred_index; | ||
| 337 | } pred; | ||
| 338 | BitField<19, 1, u64> negate_pred; | ||
| 309 | BitField<20, 8, Register> gpr20; | 339 | BitField<20, 8, Register> gpr20; |
| 310 | BitField<20, 7, SubOp> sub_op; | 340 | BitField<20, 7, SubOp> sub_op; |
| 311 | BitField<28, 8, Register> gpr28; | 341 | BitField<28, 8, Register> gpr28; |
| @@ -343,6 +373,20 @@ union Instruction { | |||
| 343 | BitField<49, 1, u64> negate_c; | 373 | BitField<49, 1, u64> negate_c; |
| 344 | } ffma; | 374 | } ffma; |
| 345 | 375 | ||
| 376 | union { | ||
| 377 | BitField<0, 3, u64> pred0; | ||
| 378 | BitField<3, 3, u64> pred3; | ||
| 379 | BitField<7, 1, u64> abs_a; | ||
| 380 | BitField<39, 3, u64> pred39; | ||
| 381 | BitField<42, 1, u64> neg_pred; | ||
| 382 | BitField<43, 1, u64> neg_a; | ||
| 383 | BitField<44, 1, u64> abs_b; | ||
| 384 | BitField<45, 2, PredOperation> op; | ||
| 385 | BitField<47, 1, u64> ftz; | ||
| 386 | BitField<48, 4, PredCondition> cond; | ||
| 387 | BitField<56, 1, u64> neg_b; | ||
| 388 | } fsetp; | ||
| 389 | |||
| 346 | BitField<61, 1, u64> is_b_imm; | 390 | BitField<61, 1, u64> is_b_imm; |
| 347 | BitField<60, 1, u64> is_b_gpr; | 391 | BitField<60, 1, u64> is_b_gpr; |
| 348 | BitField<59, 1, u64> is_c_gpr; | 392 | BitField<59, 1, u64> is_c_gpr; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index de137558d..2395945c3 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -220,6 +220,8 @@ private: | |||
| 220 | 220 | ||
| 221 | /// Generates code representing a temporary (GPR) register. | 221 | /// Generates code representing a temporary (GPR) register. |
| 222 | std::string GetRegister(const Register& reg, unsigned elem = 0) { | 222 | std::string GetRegister(const Register& reg, unsigned elem = 0) { |
| 223 | if (reg == Register::ZeroIndex) | ||
| 224 | return "0"; | ||
| 223 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) { | 225 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) { |
| 224 | // GPRs 0-3 are output color for the fragment shader | 226 | // GPRs 0-3 are output color for the fragment shader |
| 225 | return std::string{"color."} + "rgba"[(reg + elem) & 3]; | 227 | return std::string{"color."} + "rgba"[(reg + elem) & 3]; |
| @@ -276,6 +278,52 @@ private: | |||
| 276 | shader.AddLine(dest + " = " + src + ";"); | 278 | shader.AddLine(dest + " = " + src + ";"); |
| 277 | } | 279 | } |
| 278 | 280 | ||
| 281 | /* | ||
| 282 | * Writes code that assigns a predicate boolean variable. | ||
| 283 | * @param pred The id of the predicate to write to. | ||
| 284 | * @param value The expression value to assign to the predicate. | ||
| 285 | */ | ||
| 286 | void SetPredicate(u64 pred, const std::string& value) { | ||
| 287 | using Tegra::Shader::Pred; | ||
| 288 | // Can't assign to the constant predicate. | ||
| 289 | ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); | ||
| 290 | |||
| 291 | std::string variable = 'p' + std::to_string(pred); | ||
| 292 | shader.AddLine(variable + " = " + value + ';'); | ||
| 293 | declr_predicates.insert(std::move(variable)); | ||
| 294 | } | ||
| 295 | |||
| 296 | /* | ||
| 297 | * Returns the condition to use in the 'if' for a predicated instruction. | ||
| 298 | * @param instr Instruction to generate the if condition for. | ||
| 299 | * @returns string containing the predicate condition. | ||
| 300 | */ | ||
| 301 | std::string GetPredicateCondition(Instruction instr) const { | ||
| 302 | using Tegra::Shader::Pred; | ||
| 303 | ASSERT(instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)); | ||
| 304 | |||
| 305 | std::string variable = | ||
| 306 | 'p' + std::to_string(static_cast<u64>(instr.pred.pred_index.Value())); | ||
| 307 | |||
| 308 | if (instr.negate_pred) { | ||
| 309 | return "!(" + variable + ')'; | ||
| 310 | } | ||
| 311 | |||
| 312 | return variable; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* | ||
| 316 | * Returns whether the instruction at the specified offset is a 'sched' instruction. | ||
| 317 | * Sched instructions always appear before a sequence of 3 instructions. | ||
| 318 | */ | ||
| 319 | bool IsSchedInstruction(u32 offset) const { | ||
| 320 | // sched instructions appear once every 4 instructions. | ||
| 321 | static constexpr size_t SchedPeriod = 4; | ||
| 322 | u32 absolute_offset = offset - main_offset; | ||
| 323 | |||
| 324 | return (absolute_offset % SchedPeriod) == 0; | ||
| 325 | } | ||
| 326 | |||
| 279 | /** | 327 | /** |
| 280 | * Compiles a single instruction from Tegra to GLSL. | 328 | * Compiles a single instruction from Tegra to GLSL. |
| 281 | * @param offset the offset of the Tegra shader instruction. | 329 | * @param offset the offset of the Tegra shader instruction. |
| @@ -283,10 +331,24 @@ private: | |||
| 283 | * + 1. If the current instruction always terminates the program, returns PROGRAM_END. | 331 | * + 1. If the current instruction always terminates the program, returns PROGRAM_END. |
| 284 | */ | 332 | */ |
| 285 | u32 CompileInstr(u32 offset) { | 333 | u32 CompileInstr(u32 offset) { |
| 334 | // Ignore sched instructions when generating code. | ||
| 335 | if (IsSchedInstruction(offset)) | ||
| 336 | return offset + 1; | ||
| 337 | |||
| 286 | const Instruction instr = {program_code[offset]}; | 338 | const Instruction instr = {program_code[offset]}; |
| 287 | 339 | ||
| 288 | shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); | 340 | shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); |
| 289 | 341 | ||
| 342 | using Tegra::Shader::Pred; | ||
| 343 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, | ||
| 344 | "NeverExecute predicate not implemented"); | ||
| 345 | |||
| 346 | if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 347 | shader.AddLine("if (" + GetPredicateCondition(instr) + ')'); | ||
| 348 | shader.AddLine('{'); | ||
| 349 | ++shader.scope; | ||
| 350 | } | ||
| 351 | |||
| 290 | switch (OpCode::GetInfo(instr.opcode).type) { | 352 | switch (OpCode::GetInfo(instr.opcode).type) { |
| 291 | case OpCode::Type::Arithmetic: { | 353 | case OpCode::Type::Arithmetic: { |
| 292 | std::string dest = GetRegister(instr.gpr0); | 354 | std::string dest = GetRegister(instr.gpr0); |
| @@ -450,14 +512,70 @@ private: | |||
| 450 | } | 512 | } |
| 451 | break; | 513 | break; |
| 452 | } | 514 | } |
| 515 | case OpCode::Type::FloatPredicate: { | ||
| 516 | std::string op_a = instr.fsetp.neg_a ? "-" : ""; | ||
| 517 | op_a += GetRegister(instr.gpr8); | ||
| 518 | |||
| 519 | if (instr.fsetp.abs_a) { | ||
| 520 | op_a = "abs(" + op_a + ')'; | ||
| 521 | } | ||
| 522 | |||
| 523 | std::string op_b{}; | ||
| 524 | |||
| 525 | if (instr.is_b_imm) { | ||
| 526 | if (instr.fsetp.neg_b) { | ||
| 527 | // Only the immediate version of fsetp has a neg_b bit. | ||
| 528 | op_b += '-'; | ||
| 529 | } | ||
| 530 | op_b += '(' + GetImmediate19(instr) + ')'; | ||
| 531 | } else { | ||
| 532 | if (instr.is_b_gpr) { | ||
| 533 | op_b += GetRegister(instr.gpr20); | ||
| 534 | } else { | ||
| 535 | op_b += GetUniform(instr.uniform); | ||
| 536 | } | ||
| 537 | } | ||
| 453 | 538 | ||
| 539 | if (instr.fsetp.abs_b) { | ||
| 540 | op_b = "abs(" + op_b + ')'; | ||
| 541 | } | ||
| 542 | |||
| 543 | using Tegra::Shader::Pred; | ||
| 544 | ASSERT_MSG(instr.fsetp.pred0 == static_cast<u64>(Pred::UnusedIndex) && | ||
| 545 | instr.fsetp.pred39 == static_cast<u64>(Pred::UnusedIndex), | ||
| 546 | "Compound predicates are not implemented"); | ||
| 547 | |||
| 548 | // We can't use the constant predicate as destination. | ||
| 549 | ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | ||
| 550 | |||
| 551 | using Tegra::Shader::PredCondition; | ||
| 552 | switch (instr.fsetp.cond) { | ||
| 553 | case PredCondition::LessThan: | ||
| 554 | SetPredicate(instr.fsetp.pred3, '(' + op_a + ") < (" + op_b + ')'); | ||
| 555 | break; | ||
| 556 | case PredCondition::Equal: | ||
| 557 | SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')'); | ||
| 558 | break; | ||
| 559 | default: | ||
| 560 | NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})", | ||
| 561 | static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b); | ||
| 562 | UNREACHABLE(); | ||
| 563 | } | ||
| 564 | break; | ||
| 565 | } | ||
| 454 | default: { | 566 | default: { |
| 455 | switch (instr.opcode.EffectiveOpCode()) { | 567 | switch (instr.opcode.EffectiveOpCode()) { |
| 456 | case OpCode::Id::EXIT: { | 568 | case OpCode::Id::EXIT: { |
| 569 | ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), | ||
| 570 | "Predicated exits not implemented"); | ||
| 457 | shader.AddLine("return true;"); | 571 | shader.AddLine("return true;"); |
| 458 | offset = PROGRAM_END - 1; | 572 | offset = PROGRAM_END - 1; |
| 459 | break; | 573 | break; |
| 460 | } | 574 | } |
| 575 | case OpCode::Id::KIL: { | ||
| 576 | shader.AddLine("discard;"); | ||
| 577 | break; | ||
| 578 | } | ||
| 461 | case OpCode::Id::IPA: { | 579 | case OpCode::Id::IPA: { |
| 462 | const auto& attribute = instr.attribute.fmt28; | 580 | const auto& attribute = instr.attribute.fmt28; |
| 463 | std::string dest = GetRegister(instr.gpr0); | 581 | std::string dest = GetRegister(instr.gpr0); |
| @@ -476,6 +594,12 @@ private: | |||
| 476 | } | 594 | } |
| 477 | } | 595 | } |
| 478 | 596 | ||
| 597 | // Close the predicate condition scope. | ||
| 598 | if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 599 | --shader.scope; | ||
| 600 | shader.AddLine('}'); | ||
| 601 | } | ||
| 602 | |||
| 479 | return offset + 1; | 603 | return offset + 1; |
| 480 | } | 604 | } |
| 481 | 605 | ||
| @@ -605,6 +729,12 @@ private: | |||
| 605 | declarations.AddNewLine(); | 729 | declarations.AddNewLine(); |
| 606 | ++const_buffer_layout; | 730 | ++const_buffer_layout; |
| 607 | } | 731 | } |
| 732 | |||
| 733 | declarations.AddNewLine(); | ||
| 734 | for (const auto& pred : declr_predicates) { | ||
| 735 | declarations.AddLine("bool " + pred + " = false;"); | ||
| 736 | } | ||
| 737 | declarations.AddNewLine(); | ||
| 608 | } | 738 | } |
| 609 | 739 | ||
| 610 | private: | 740 | private: |
| @@ -618,6 +748,7 @@ private: | |||
| 618 | 748 | ||
| 619 | // Declarations | 749 | // Declarations |
| 620 | std::set<std::string> declr_register; | 750 | std::set<std::string> declr_register; |
| 751 | std::set<std::string> declr_predicates; | ||
| 621 | std::set<Attribute::Index> declr_input_attribute; | 752 | std::set<Attribute::Index> declr_input_attribute; |
| 622 | std::set<Attribute::Index> declr_output_attribute; | 753 | std::set<Attribute::Index> declr_output_attribute; |
| 623 | std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; | 754 | std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; |