summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp80
1 files changed, 72 insertions, 8 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4b14bb47e..4a41e7798 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -115,7 +115,16 @@ private:
if (const auto opcode = OpCode::Decode(instr)) {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
- return exit_method = ExitMethod::AlwaysEnd;
+ // The EXIT instruction can be predicated, which means that the shader can
+ // conditionally end on this instruction. We have to consider the case where the
+ // condition is not met and check the exit method of that other basic block.
+ using Tegra::Shader::Pred;
+ if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
+ return exit_method = ExitMethod::AlwaysEnd;
+ } else {
+ ExitMethod not_met = Scan(offset + 1, end, labels);
+ return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met);
+ }
}
case OpCode::Id::BRA: {
u32 target = offset + instr.bra.GetBranchTarget();
@@ -645,9 +654,9 @@ private:
std::string GetPredicateComparison(Tegra::Shader::PredCondition condition) const {
using Tegra::Shader::PredCondition;
static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
- {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
- {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
- {PredCondition::GreaterEqual, ">="},
+ {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
+ {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
+ {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
};
auto comparison = PredicateComparisonStrings.find(condition);
@@ -884,6 +893,59 @@ private:
}
break;
}
+
+ case OpCode::Type::Shift: {
+ std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
+ std::string op_b;
+
+ if (instr.is_b_imm) {
+ op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
+ } else {
+ if (instr.is_b_gpr) {
+ op_b += regs.GetRegisterAsInteger(instr.gpr20);
+ } else {
+ op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Integer);
+ }
+ }
+
+ switch (opcode->GetId()) {
+ case OpCode::Id::SHL_C:
+ case OpCode::Id::SHL_R:
+ case OpCode::Id::SHL_IMM:
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
+ break;
+ default: {
+ NGLOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName());
+ UNREACHABLE();
+ }
+ }
+ break;
+ }
+
+ case OpCode::Type::ScaledAdd: {
+ std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
+
+ if (instr.iscadd.negate_a)
+ op_a = '-' + op_a;
+
+ std::string op_b = instr.iscadd.negate_b ? "-" : "";
+
+ if (instr.is_b_imm) {
+ op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
+ } else {
+ if (instr.is_b_gpr) {
+ op_b += regs.GetRegisterAsInteger(instr.gpr20);
+ } else {
+ op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Integer);
+ }
+ }
+
+ std::string shift = std::to_string(instr.iscadd.shift_amount.Value());
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0,
+ "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
+ break;
+ }
case OpCode::Type::Ffma: {
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = instr.ffma.negate_b ? "-" : "";
@@ -1230,9 +1292,6 @@ private:
default: {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
- ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),
- "Predicated exits not implemented");
-
// Final color output is currently hardcoded to GPR0-3 for fragment shaders
if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';');
@@ -1242,7 +1301,12 @@ private:
}
shader.AddLine("return true;");
- offset = PROGRAM_END - 1;
+ if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
+ // If this is an unconditional exit then just end processing here, otherwise we
+ // have to account for the possibility of the condition not being met, so
+ // continue processing the next instruction.
+ offset = PROGRAM_END - 1;
+ }
break;
}
case OpCode::Id::KIL: {