diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index f3ca30cfa..f20c2cd41 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -25,6 +25,13 @@ struct Register { /// Register 255 is special cased to always be 0 static constexpr size_t ZeroIndex = 255; + enum class Size : u64 { + Byte = 0, + Short = 1, + Word = 2, + Long = 3, + }; + constexpr Register() = default; constexpr Register(u64 value) : value(value) {} @@ -236,6 +243,15 @@ union Instruction { BitField<56, 1, u64> neg_imm; } fset; + union { + BitField<10, 2, Register::Size> size; + BitField<13, 1, u64> is_signed; + BitField<41, 2, u64> selector; + BitField<45, 1, u64> negate_a; + BitField<49, 1, u64> abs_a; + BitField<50, 1, u64> saturate_a; + } conversion; + BitField<61, 1, u64> is_b_imm; BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; @@ -290,7 +306,7 @@ public: MOV_C, MOV_R, MOV_IMM, - MOV32I, + MOV32_IMM, SHR_C, SHR_R, SHR_IMM, @@ -314,6 +330,7 @@ public: FloatSet, FloatSetPredicate, IntegerSetPredicate, + Conversion, Unknown, }; @@ -435,20 +452,20 @@ private: INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), - INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"), - INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"), - INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"), - INST("0100110011100---", Id::I2I_C, Type::Arithmetic, "I2I_C"), - INST("0101110011100---", Id::I2I_R, Type::Arithmetic, "I2I_R"), - INST("01110001-1000---", Id::I2I_IMM, Type::Arithmetic, "I2I_IMM"), INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"), INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), - INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"), + INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"), INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"), INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"), INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"), + INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), + INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), + INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), + INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"), + INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"), + INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"), INST("01011000--------", Id::FSET_R, Type::FloatSet, "FSET_R"), INST("0100100---------", Id::FSET_C, Type::FloatSet, "FSET_C"), INST("0011000---------", Id::FSET_IMM, Type::FloatSet, "FSET_IMM"), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8b235e252..27190cc45 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -153,85 +153,61 @@ private: */ class GLSLRegister { public: - GLSLRegister(size_t index, ShaderWriter& shader) - : index{index}, shader{shader}, float_str{"freg_" + std::to_string(index)}, - integer_str{"ireg_" + std::to_string(index)} {} + enum class Type { + Float, + Integer, + UnsignedInteger, + }; - /// Returns a GLSL string representing the current state of the register - const std::string& GetActiveString() { - declr_type.insert(active_type); + GLSLRegister(size_t index, ShaderWriter& shader) : index{index}, shader{shader} {} - switch (active_type) { + /// Gets the GLSL type string for a register + static std::string GetTypeString(Type type) { + switch (type) { case Type::Float: - return float_str; + return "float"; case Type::Integer: - return integer_str; + return "int"; + case Type::UnsignedInteger: + return "uint"; } UNREACHABLE(); - return float_str; + return {}; } - /// Returns a GLSL string representing the register as a float - const std::string& GetFloatString() const { - ASSERT(IsFloatUsed()); - return float_str; + /// Gets the GLSL register prefix string, used for declarations and referencing + static std::string GetPrefixString(Type type) { + return "reg_" + GetTypeString(type) + '_'; } - /// Returns a GLSL string representing the register as an integer - const std::string& GetIntegerString() const { - ASSERT(IsIntegerUsed()); - return integer_str; + /// Returns a GLSL string representing the current state of the register + const std::string GetActiveString() { + declr_type.insert(active_type); + return GetPrefixString(active_type) + std::to_string(index); } - /// Convert the current register state from float to integer - void FloatToInteger() { - ASSERT(active_type == Type::Float); - - const std::string src = GetActiveString(); - active_type = Type::Integer; - const std::string dest = GetActiveString(); - - shader.AddLine(dest + " = floatBitsToInt(" + src + ");"); - } - - /// Convert the current register state from integer to float - void IntegerToFloat() { - ASSERT(active_type == Type::Integer); - - const std::string src = GetActiveString(); - active_type = Type::Float; - const std::string dest = GetActiveString(); - - shader.AddLine(dest + " = intBitsToFloat(" + src + ");"); - } - - /// Returns true if the register was ever used as a float, used for register declarations - bool IsFloatUsed() const { - return declr_type.find(Type::Float) != declr_type.end(); - } - - /// Returns true if the register was ever used as an integer, used for register declarations - bool IsIntegerUsed() const { - return declr_type.find(Type::Integer) != declr_type.end(); - } - - /// Returns true if the active type is float + /// Returns true if the active type is a float bool IsFloat() const { return active_type == Type::Float; } - /// Returns true if the active type is integer + /// Returns true if the active type is an integer bool IsInteger() const { return active_type == Type::Integer; } -private: - enum class Type { - Float, - Integer, - }; + /// Returns the index of the register + size_t GetIndex() const { + return index; + } + /// Returns a set of the declared types of the register + const std::set& DeclaredTypes() const { + return declr_type; + } + +private: const size_t index; const std::string float_str; const std::string integer_str; @@ -254,18 +230,35 @@ public: BuildRegisterList(); } - /// Generates code representing a temporary (GPR) register. - std::string GetRegister(const Register& reg, unsigned elem = 0) { - if (reg == Register::ZeroIndex) { - return "0"; - } - - return regs[reg.GetSwizzledIndex(elem)].GetActiveString(); + /** + * Gets a register as an float. + * @param reg The register to get. + * @param elem The element to use for the operation. + * @returns GLSL string corresponding to the register as a float. + */ + std::string GetRegisterAsFloat(const Register& reg, unsigned elem = 0) { + ASSERT(regs[reg].IsFloat()); + return GetRegister(reg, elem); } /** - * Writes code that does a register assignment to float value operation. Should only be used - * with shader instructions that deal with floats. + * Gets a register as an integer. + * @param reg The register to get. + * @param elem The element to use for the operation. + * @param is_signed Whether to get the register as a signed (or unsigned) integer. + * @returns GLSL string corresponding to the register as an integer. + */ + std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, + bool is_signed = true) { + const std::string func = GetGLSLConversionFunc( + GLSLRegister::Type::Float, + is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger); + + return func + '(' + GetRegister(reg, elem) + ')'; + } + + /** + * Writes code that does a register assignment to float value operation. * @param reg The destination register to use. * @param elem The element to use for the operation. * @param value The code representing the value to assign. @@ -277,21 +270,28 @@ public: void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) { - ASSERT(regs[reg].IsFloat()); + SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem); + } - std::string dest = GetRegister(reg, dest_elem); - if (dest_num_components > 1) { - dest += GetSwizzle(elem); - } + /** + * Writes code that does a register assignment to integer value operation. + * @param reg The destination register to use. + * @param elem The element to use for the operation. + * @param value The code representing the value to assign. + * @param dest_num_components Number of components in the destination. + * @param value_num_components Number of components in the value. + * @param is_abs Optional, when True, applies absolute value to output. + * @param dest_elem Optional, the destination element to use for the operation. + */ + void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, + const std::string& value, u64 dest_num_components, + u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) { + const std::string func = GetGLSLConversionFunc( + is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger, + GLSLRegister::Type::Float); - std::string src = '(' + value + ')'; - if (value_num_components > 1) { - src += GetSwizzle(elem); - } - - src = is_abs ? "abs(" + src + ')' : src; - - shader.AddLine(dest + " = " + src + ';'); + SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components, + is_abs, dest_elem); } /** @@ -302,7 +302,7 @@ public: * @param attribute The input attibute to use as the source value. */ void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute) { - std::string dest = GetRegister(reg); + std::string dest = GetRegisterAsFloat(reg); std::string src = GetInputAttribute(attribute) + GetSwizzle(elem); if (regs[reg].IsFloat()) { @@ -323,7 +323,7 @@ public: */ void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { std::string dest = GetOutputAttribute(attribute) + GetSwizzle(elem); - std::string src = GetRegister(reg); + std::string src = GetRegisterAsFloat(reg); ASSERT_MSG(regs[reg].IsFloat(), "Output attributes must be set to a float"); shader.AddLine(dest + " = " + src + ';'); } @@ -347,11 +347,10 @@ public: /// Add declarations for registers void GenerateDeclarations() { for (const auto& reg : regs) { - if (reg.IsFloatUsed()) { - declarations.AddLine("float " + reg.GetFloatString() + " = 0.0;"); - } - if (reg.IsIntegerUsed()) { - declarations.AddLine("int " + reg.GetIntegerString() + " = 0;"); + for (const auto& type : reg.DeclaredTypes()) { + declarations.AddLine(GLSLRegister::GetTypeString(type) + ' ' + + GLSLRegister::GetPrefixString(type) + + std::to_string(reg.GetIndex()) + " = 0;"); } } declarations.AddNewLine(); @@ -395,6 +394,51 @@ public: } private: + /// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc. + const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const { + const std::string src_type = GLSLRegister::GetTypeString(src); + std::string dest_type = GLSLRegister::GetTypeString(dest); + dest_type[0] = toupper(dest_type[0]); + return src_type + "BitsTo" + dest_type; + } + + /// Generates code representing a temporary (GPR) register. + std::string GetRegister(const Register& reg, unsigned elem) { + if (reg == Register::ZeroIndex) { + return "0"; + } + + return regs[reg.GetSwizzledIndex(elem)].GetActiveString(); + } + + /** + * Writes code that does a register assignment to value operation. + * @param reg The destination register to use. + * @param elem The element to use for the operation. + * @param value The code representing the value to assign. + * @param dest_num_components Number of components in the destination. + * @param value_num_components Number of components in the value. + * @param is_abs Optional, when True, applies absolute value to output. + * @param dest_elem Optional, the destination element to use for the operation. + */ + void SetRegister(const Register& reg, u64 elem, const std::string& value, + u64 dest_num_components, u64 value_num_components, bool is_abs, + u64 dest_elem) { + std::string dest = GetRegister(reg, dest_elem); + if (dest_num_components > 1) { + dest += GetSwizzle(elem); + } + + std::string src = '(' + value + ')'; + if (value_num_components > 1) { + src += GetSwizzle(elem); + } + + src = is_abs ? "abs(" + src + ')' : src; + + shader.AddLine(dest + " = " + src + ';'); + } + /// Build the GLSL register list. void BuildRegisterList() { for (size_t index = 0; index < Register::NumRegisters; ++index) { @@ -598,7 +642,7 @@ private: switch (opcode->GetType()) { case OpCode::Type::Arithmetic: { std::string op_a = instr.alu.negate_a ? "-" : ""; - op_a += regs.GetRegister(instr.gpr8); + op_a += regs.GetRegisterAsFloat(instr.gpr8); if (instr.alu.abs_a) { op_a = "abs(" + op_a + ')'; } @@ -609,7 +653,7 @@ private: op_b += GetImmediate19(instr); } else { if (instr.is_b_gpr) { - op_b += regs.GetRegister(instr.gpr20); + op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { op_b += regs.GetUniform(instr.uniform, instr.gpr0); } @@ -620,6 +664,11 @@ private: } switch (opcode->GetId()) { + case OpCode::Id::MOV32_IMM: { + // mov32i doesn't have abs or neg bits. + regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); + break; + } case OpCode::Id::FMUL_C: case OpCode::Id::FMUL_R: case OpCode::Id::FMUL_IMM: { @@ -629,8 +678,8 @@ private: case OpCode::Id::FMUL32_IMM: { // fmul32i doesn't have abs or neg bits. regs.SetRegisterToFloat( - instr.gpr0, 0, regs.GetRegister(instr.gpr8) + " * " + GetImmediate32(instr), 1, - 1); + instr.gpr0, 0, + regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); break; } case OpCode::Id::FADD_C: @@ -687,29 +736,29 @@ private: break; } case OpCode::Type::Ffma: { - std::string op_a = regs.GetRegister(instr.gpr8); + std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string op_b = instr.ffma.negate_b ? "-" : ""; std::string op_c = instr.ffma.negate_c ? "-" : ""; switch (opcode->GetId()) { case OpCode::Id::FFMA_CR: { op_b += regs.GetUniform(instr.uniform, instr.gpr0); - op_c += regs.GetRegister(instr.gpr39); + op_c += regs.GetRegisterAsFloat(instr.gpr39); break; } case OpCode::Id::FFMA_RR: { - op_b += regs.GetRegister(instr.gpr20); - op_c += regs.GetRegister(instr.gpr39); + op_b += regs.GetRegisterAsFloat(instr.gpr20); + op_c += regs.GetRegisterAsFloat(instr.gpr39); break; } case OpCode::Id::FFMA_RC: { - op_b += regs.GetRegister(instr.gpr39); + op_b += regs.GetRegisterAsFloat(instr.gpr39); op_c += regs.GetUniform(instr.uniform, instr.gpr0); break; } case OpCode::Id::FFMA_IMM: { op_b += GetImmediate19(instr); - op_c += regs.GetRegister(instr.gpr39); + op_c += regs.GetRegisterAsFloat(instr.gpr39); break; } default: { @@ -721,6 +770,32 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1); break; } + case OpCode::Type::Conversion: { + ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented"); + ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); + ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented"); + ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented"); + + switch (opcode->GetId()) { + case OpCode::Id::I2I_R: + case OpCode::Id::I2F_R: { + std::string op_a = + regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed); + + if (instr.conversion.abs_a) { + op_a = "abs(" + op_a + ')'; + } + + regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + break; + } case OpCode::Type::Memory: { const Attribute::Index attribute = instr.attribute.fmt20.index; @@ -739,8 +814,8 @@ private: } case OpCode::Id::TEXS: { ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested"); - const std::string op_a = regs.GetRegister(instr.gpr8); - const std::string op_b = regs.GetRegister(instr.gpr20); + const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); + const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); const std::string sampler = GetSampler(instr.sampler); const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; // Add an extra scope and declare the texture coords inside to prevent overwriting @@ -765,7 +840,7 @@ private: } case OpCode::Type::FloatSetPredicate: { std::string op_a = instr.fsetp.neg_a ? "-" : ""; - op_a += regs.GetRegister(instr.gpr8); + op_a += regs.GetRegisterAsFloat(instr.gpr8); if (instr.fsetp.abs_a) { op_a = "abs(" + op_a + ')'; @@ -781,7 +856,7 @@ private: op_b += '(' + GetImmediate19(instr) + ')'; } else { if (instr.is_b_gpr) { - op_b += regs.GetRegister(instr.gpr20); + op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { op_b += regs.GetUniform(instr.uniform, instr.gpr0); } @@ -816,7 +891,7 @@ private: } case OpCode::Type::FloatSet: { std::string op_a = instr.fset.neg_a ? "-" : ""; - op_a += regs.GetRegister(instr.gpr8); + op_a += regs.GetRegisterAsFloat(instr.gpr8); if (instr.fset.abs_a) { op_a = "abs(" + op_a + ')'; @@ -832,7 +907,7 @@ private: op_b += imm; } else { if (instr.is_b_gpr) { - op_b += regs.GetRegister(instr.gpr20); + op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { op_b += regs.GetUniform(instr.uniform, instr.gpr0); } @@ -877,10 +952,10 @@ private: // Final color output is currently hardcoded to GPR0-3 for fragment shaders if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { - shader.AddLine("color.r = " + regs.GetRegister(0) + ';'); - shader.AddLine("color.g = " + regs.GetRegister(1) + ';'); - shader.AddLine("color.b = " + regs.GetRegister(2) + ';'); - shader.AddLine("color.a = " + regs.GetRegister(3) + ';'); + shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';'); + shader.AddLine("color.g = " + regs.GetRegisterAsFloat(1) + ';'); + shader.AddLine("color.b = " + regs.GetRegisterAsFloat(2) + ';'); + shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';'); } shader.AddLine("return true;");