From 2e95ba2e9c67d16456fb9f700cfe3da837e16a73 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 16 Aug 2018 09:05:16 -0500 Subject: [PATCH] Shaders: Corrected the 'abs' and 'neg' bit usage in the float arithmetic instructions. We should definitely audit our shader generator for more errors like this. --- src/video_core/engines/shader_bytecode.h | 4 ++ .../renderer_opengl/gl_shader_decompiler.cpp | 50 +++++++++++++------ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 2526ebf28..f438fa809 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -282,6 +282,10 @@ union Instruction { } } alu; + union { + BitField<48, 1, u64> negate_b; + } fmul; + union { BitField<48, 1, u64> is_signed; } shift; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index e0dfdbb9f..e899237e5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -741,6 +741,30 @@ private: return op->second; } + /** + * Transforms the input string GLSL operand into one that applies the abs() function and negates + * the output if necessary. When both abs and neg are true, the negation will be applied after + * taking the absolute value. + * @param operand The input operand to take the abs() of, negate, or both. + * @param abs Whether to apply the abs() function to the input operand. + * @param neg Whether to negate the input operand. + * @returns String corresponding to the operand after being transformed by the abs() and + * negation operations. + */ + static std::string GetOperandAbsNeg(const std::string& operand, bool abs, bool neg) { + std::string result = operand; + + if (abs) { + result = "abs(" + result + ')'; + } + + if (neg) { + result = "-(" + result + ')'; + } + + return result; + } + /* * Returns whether the instruction at the specified offset is a 'sched' instruction. * Sched instructions always appear before a sequence of 3 instructions. @@ -857,13 +881,6 @@ private: switch (opcode->GetType()) { case OpCode::Type::Arithmetic: { std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); - if (instr.alu.abs_a) { - op_a = "abs(" + op_a + ')'; - } - - if (instr.alu.negate_a) { - op_a = "-(" + op_a + ')'; - } std::string op_b; @@ -878,17 +895,10 @@ private: } } - if (instr.alu.abs_b) { - op_b = "abs(" + op_b + ')'; - } - - if (instr.alu.negate_b) { - op_b = "-(" + op_b + ')'; - } - switch (opcode->GetId()) { case OpCode::Id::MOV_C: case OpCode::Id::MOV_R: { + // MOV does not have neither 'abs' nor 'neg' bits. regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); break; } @@ -896,6 +906,8 @@ private: case OpCode::Id::FMUL_C: case OpCode::Id::FMUL_R: case OpCode::Id::FMUL_IMM: { + // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. + op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.saturate_d); break; @@ -903,11 +915,14 @@ private: case OpCode::Id::FADD_C: case OpCode::Id::FADD_R: case OpCode::Id::FADD_IMM: { + op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); + op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.saturate_d); break; } case OpCode::Id::MUFU: { + op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); switch (instr.sub_op) { case SubOp::Cos: regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, @@ -947,6 +962,9 @@ private: case OpCode::Id::FMNMX_C: case OpCode::Id::FMNMX_R: case OpCode::Id::FMNMX_IMM: { + op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); + op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); + std::string condition = GetPredicateCondition(instr.alu.fmnmx.pred, instr.alu.fmnmx.negate_pred != 0); std::string parameters = op_a + ',' + op_b; @@ -960,7 +978,7 @@ private: case OpCode::Id::RRO_R: case OpCode::Id::RRO_IMM: { // Currently RRO is only implemented as a register move. - // Usage of `abs_b` and `negate_b` here should also be correct. + op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); LOG_WARNING(HW_GPU, "RRO instruction is incomplete"); break;