summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp581
1 files changed, 441 insertions, 140 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index f886e49ca..65fed77ef 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -16,11 +16,11 @@ namespace Decompiler {
using Tegra::Shader::Attribute;
using Tegra::Shader::Instruction;
+using Tegra::Shader::LogicOperation;
using Tegra::Shader::OpCode;
using Tegra::Shader::Register;
using Tegra::Shader::Sampler;
using Tegra::Shader::SubOp;
-using Tegra::Shader::Uniform;
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
@@ -267,6 +267,27 @@ public:
}
/**
+ * Returns code that does an integer size conversion for the specified size.
+ * @param value Value to perform integer size conversion on.
+ * @param size Register size to use for conversion instructions.
+ * @returns GLSL string corresponding to the value converted to the specified size.
+ */
+ static std::string ConvertIntegerSize(const std::string& value, Register::Size size) {
+ switch (size) {
+ case Register::Size::Byte:
+ return "((" + value + " << 24) >> 24)";
+ case Register::Size::Short:
+ return "((" + value + " << 16) >> 16)";
+ case Register::Size::Word:
+ // Default - do nothing
+ return value;
+ default:
+ NGLOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size));
+ UNREACHABLE();
+ }
+ }
+
+ /**
* Gets a register as an float.
* @param reg The register to get.
* @param elem The element to use for the operation.
@@ -282,15 +303,18 @@ public:
* @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.
+ * @param size Register size to use for conversion instructions.
* @returns GLSL string corresponding to the register as an integer.
*/
- std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0,
- bool is_signed = true) {
+ std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, bool is_signed = true,
+ Register::Size size = Register::Size::Word) {
const std::string func = GetGLSLConversionFunc(
GLSLRegister::Type::Float,
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger);
- return func + '(' + GetRegister(reg, elem) + ')';
+ std::string value = func + '(' + GetRegister(reg, elem) + ')';
+
+ return ConvertIntegerSize(value, size);
}
/**
@@ -300,13 +324,15 @@ public:
* @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 is_saturated Optional, when True, saturates the provided value.
* @param dest_elem Optional, the destination element to use for the operation.
*/
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) {
- SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem);
+ u64 dest_num_components, u64 value_num_components,
+ bool is_saturated = false, u64 dest_elem = 0) {
+
+ SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
+ dest_num_components, value_num_components, dest_elem);
}
/**
@@ -316,18 +342,22 @@ public:
* @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 is_saturated Optional, when True, saturates the provided value.
* @param dest_elem Optional, the destination element to use for the operation.
+ * @param size Register size to use for conversion instructions.
*/
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) {
+ u64 value_num_components, bool is_saturated = false,
+ u64 dest_elem = 0, Register::Size size = Register::Size::Word) {
+ ASSERT_MSG(!is_saturated, "Unimplemented");
+
const std::string func = GetGLSLConversionFunc(
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger,
GLSLRegister::Type::Float);
- SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components,
- is_abs, dest_elem);
+ SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
+ dest_num_components, value_num_components, dest_elem);
}
/**
@@ -365,11 +395,9 @@ public:
}
/// Generates code representing a uniform (C buffer) register, interpreted as the input type.
- std::string GetUniform(const Uniform& uniform, GLSLRegister::Type type) {
- declr_const_buffers[uniform.index].MarkAsUsed(static_cast<unsigned>(uniform.index),
- static_cast<unsigned>(uniform.offset), stage);
- std::string value =
- 'c' + std::to_string(uniform.index) + '[' + std::to_string(uniform.offset) + ']';
+ std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) {
+ declr_const_buffers[index].MarkAsUsed(index, offset, stage);
+ std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset) + ']';
if (type == GLSLRegister::Type::Float) {
return value;
@@ -380,10 +408,19 @@ public:
}
}
- /// Generates code representing a uniform (C buffer) register, interpreted as the type of the
- /// destination register.
- std::string GetUniform(const Uniform& uniform, const Register& dest_reg) {
- return GetUniform(uniform, regs[dest_reg].GetActiveType());
+ std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg,
+ GLSLRegister::Type type) {
+ declr_const_buffers[index].MarkAsUsedIndirect(index, stage);
+ std::string value = 'c' + std::to_string(index) + "[(floatBitsToInt(" +
+ GetRegister(index_reg, 0) + ") + " + std::to_string(offset) + ") / 4]";
+
+ if (type == GLSLRegister::Type::Float) {
+ return value;
+ } else if (type == GLSLRegister::Type::Integer) {
+ return "floatBitsToInt(" + value + ')';
+ } else {
+ UNREACHABLE();
+ }
}
/// Add declarations for registers
@@ -425,6 +462,14 @@ public:
++const_buffer_layout;
}
declarations.AddNewLine();
+
+ // Append the sampler2D array for the used textures.
+ size_t num_samplers = GetSamplers().size();
+ if (num_samplers > 0) {
+ declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' +
+ std::to_string(num_samplers) + "];");
+ declarations.AddNewLine();
+ }
}
/// Returns a list of constant buffer declarations
@@ -435,6 +480,32 @@ public:
return result;
}
+ /// Returns a list of samplers used in the shader
+ std::vector<SamplerEntry> GetSamplers() const {
+ return used_samplers;
+ }
+
+ /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
+ /// necessary.
+ std::string AccessSampler(const Sampler& sampler) {
+ size_t offset = static_cast<size_t>(sampler.index.Value());
+
+ // If this sampler has already been used, return the existing mapping.
+ auto itr =
+ std::find_if(used_samplers.begin(), used_samplers.end(),
+ [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
+
+ if (itr != used_samplers.end()) {
+ return itr->GetName();
+ }
+
+ // Otherwise create a new mapping for this sampler
+ size_t next_index = used_samplers.size();
+ SamplerEntry entry{stage, offset, next_index};
+ used_samplers.emplace_back(entry);
+ return entry.GetName();
+ }
+
private:
/// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc.
const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const {
@@ -460,13 +531,11 @@ private:
* @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);
+ u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
+ std::string dest = GetRegister(reg, static_cast<u32>(dest_elem));
if (dest_num_components > 1) {
dest += GetSwizzle(elem);
}
@@ -476,8 +545,6 @@ private:
src += GetSwizzle(elem);
}
- src = is_abs ? "abs(" + src + ')' : src;
-
shader.AddLine(dest + " = " + src + ';');
}
@@ -498,7 +565,7 @@ private:
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
- return "vec4(0, 0, gl_InstanceID, gl_VertexID)";
+ return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))";
default:
const u32 index{static_cast<u32>(attribute) -
static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -544,6 +611,7 @@ private:
std::set<Attribute::Index> declr_input_attribute;
std::set<Attribute::Index> declr_output_attribute;
std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
+ std::vector<SamplerEntry> used_samplers;
const Maxwell3D::Regs::ShaderStage& stage;
};
@@ -563,7 +631,7 @@ public:
/// Returns entries in the shader that are useful for external functions
ShaderEntries GetEntries() const {
- return {regs.GetConstBuffersDeclarations()};
+ return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()};
}
private:
@@ -585,12 +653,8 @@ private:
}
/// Generates code representing a texture sampler.
- std::string GetSampler(const Sampler& sampler) const {
- // TODO(Subv): Support more than just texture sampler 0
- ASSERT_MSG(sampler.index == Sampler::Index::Sampler_0, "unsupported");
- const unsigned index{static_cast<unsigned>(sampler.index.Value()) -
- static_cast<unsigned>(Sampler::Index::Sampler_0)};
- return "tex[" + std::to_string(index) + ']';
+ std::string GetSampler(const Sampler& sampler) {
+ return regs.AccessSampler(sampler);
}
/**
@@ -696,6 +760,31 @@ private:
return (absolute_offset % SchedPeriod) == 0;
}
+ void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
+ const std::string& op_b) {
+ switch (logic_op) {
+ case LogicOperation::And: {
+ regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " & " + op_b + ')', 1, 1);
+ break;
+ }
+ case LogicOperation::Or: {
+ regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " | " + op_b + ')', 1, 1);
+ break;
+ }
+ case LogicOperation::Xor: {
+ regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " ^ " + op_b + ')', 1, 1);
+ break;
+ }
+ case LogicOperation::PassB: {
+ regs.SetRegisterToInteger(dest, true, 0, op_b, 1, 1);
+ break;
+ }
+ default:
+ NGLOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op));
+ UNREACHABLE();
+ }
+ }
+
/**
* Compiles a single instruction from Tegra to GLSL.
* @param offset the offset of the Tegra shader instruction.
@@ -733,21 +822,25 @@ private:
switch (opcode->GetType()) {
case OpCode::Type::Arithmetic: {
- std::string op_a = instr.alu.negate_a ? "-" : "";
- op_a += regs.GetRegisterAsFloat(instr.gpr8);
+ std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
if (instr.alu.abs_a) {
op_a = "abs(" + op_a + ')';
}
- std::string op_b = instr.alu.negate_b ? "-" : "";
+ if (instr.alu.negate_a) {
+ op_a = "-(" + op_a + ')';
+ }
+
+ std::string op_b;
if (instr.is_b_imm) {
- op_b += GetImmediate19(instr);
+ op_b = GetImmediate19(instr);
} else {
if (instr.is_b_gpr) {
- op_b += regs.GetRegisterAsFloat(instr.gpr20);
+ op_b = regs.GetRegisterAsFloat(instr.gpr20);
} else {
- op_b += regs.GetUniform(instr.uniform, instr.gpr0);
+ op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
}
}
@@ -755,6 +848,10 @@ private:
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: {
@@ -762,58 +859,49 @@ private:
break;
}
- 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: {
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.abs_d);
- break;
- }
- case OpCode::Id::FMUL32_IMM: {
- // fmul32i doesn't have abs or neg bits.
- regs.SetRegisterToFloat(
- instr.gpr0, 0,
- regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
+ instr.alu.saturate_d);
break;
}
case OpCode::Id::FADD_C:
case OpCode::Id::FADD_R:
case OpCode::Id::FADD_IMM: {
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.abs_d);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
+ instr.alu.saturate_d);
break;
}
case OpCode::Id::MUFU: {
switch (instr.sub_op) {
case SubOp::Cos:
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
case SubOp::Sin:
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
case SubOp::Ex2:
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
case SubOp::Lg2:
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
case SubOp::Rcp:
- regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, instr.alu.abs_d);
+ regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
+ instr.alu.saturate_d);
break;
case SubOp::Rsq:
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
case SubOp::Min:
regs.SetRegisterToFloat(instr.gpr0, 0, "min(" + op_a + "," + op_b + ')', 1, 1,
- instr.alu.abs_d);
+ instr.alu.saturate_d);
break;
default:
NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
@@ -850,52 +938,49 @@ private:
}
break;
}
- case OpCode::Type::Logic: {
- std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
-
- if (instr.alu.lop.invert_a)
- op_a = "~(" + op_a + ')';
-
+ case OpCode::Type::ArithmeticImmediate: {
switch (opcode->GetId()) {
- case OpCode::Id::LOP32I: {
- u32 imm = static_cast<u32>(instr.alu.imm20_32.Value());
+ case OpCode::Id::MOV32_IMM: {
+ regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
+ break;
+ }
+ case OpCode::Id::FMUL32_IMM: {
+ regs.SetRegisterToFloat(
+ instr.gpr0, 0,
+ regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
+ break;
+ }
+ }
+ break;
+ }
+ case OpCode::Type::Bfe: {
+ ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented");
- if (instr.alu.lop.invert_b)
- imm = ~imm;
+ std::string op_a = instr.bfe.negate_a ? "-" : "";
+ op_a += regs.GetRegisterAsInteger(instr.gpr8);
- switch (instr.alu.lop.operation) {
- case Tegra::Shader::LogicOperation::And: {
- regs.SetRegisterToInteger(instr.gpr0, false, 0,
- '(' + op_a + " & " + std::to_string(imm) + ')', 1, 1);
- break;
- }
- case Tegra::Shader::LogicOperation::Or: {
- regs.SetRegisterToInteger(instr.gpr0, false, 0,
- '(' + op_a + " | " + std::to_string(imm) + ')', 1, 1);
- break;
- }
- case Tegra::Shader::LogicOperation::Xor: {
- regs.SetRegisterToInteger(instr.gpr0, false, 0,
- '(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1);
- break;
- }
- default:
- NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}",
- static_cast<u32>(instr.alu.lop.operation.Value()));
- UNREACHABLE();
- }
+ switch (opcode->GetId()) {
+ case OpCode::Id::BFE_IMM: {
+ std::string inner_shift =
+ '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
+ std::string outer_shift =
+ '(' + inner_shift + " >> " +
+ std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
break;
}
default: {
- NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName());
+ NGLOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName());
UNREACHABLE();
}
}
+
break;
}
case OpCode::Type::Shift: {
- std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
+ std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
std::string op_b;
if (instr.is_b_imm) {
@@ -904,11 +989,25 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsInteger(instr.gpr20);
} else {
- op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Integer);
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Integer);
}
}
switch (opcode->GetId()) {
+ case OpCode::Id::SHR_C:
+ case OpCode::Id::SHR_R:
+ case OpCode::Id::SHR_IMM: {
+ if (!instr.shift.is_signed) {
+ // Logical shift right
+ op_a = "uint(" + op_a + ')';
+ }
+
+ // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
+ 1, 1);
+ break;
+ }
case OpCode::Id::SHL_C:
case OpCode::Id::SHL_R:
case OpCode::Id::SHL_IMM:
@@ -922,28 +1021,101 @@ private:
break;
}
- case OpCode::Type::ScaledAdd: {
+ case OpCode::Type::ArithmeticIntegerImmediate: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
+ std::string op_b = std::to_string(instr.alu.imm20_32.Value());
- if (instr.iscadd.negate_a)
- op_a = '-' + op_a;
+ switch (opcode->GetId()) {
+ case OpCode::Id::IADD32I:
+ if (instr.iadd32i.negate_a)
+ op_a = "-(" + op_a + ')';
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
+ instr.iadd32i.saturate != 0);
+ break;
+ case OpCode::Id::LOP32I: {
+ if (instr.alu.lop32i.invert_a)
+ op_a = "~(" + op_a + ')';
- std::string op_b = instr.iscadd.negate_b ? "-" : "";
+ if (instr.alu.lop32i.invert_b)
+ op_b = "~(" + op_b + ')';
+ WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b);
+ break;
+ }
+ default: {
+ NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}",
+ opcode->GetName());
+ UNREACHABLE();
+ }
+ }
+ break;
+ }
+ case OpCode::Type::ArithmeticInteger: {
+ std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
+ 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);
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Integer);
}
}
- std::string shift = std::to_string(instr.iscadd.shift_amount.Value());
+ switch (opcode->GetId()) {
+ case OpCode::Id::IADD_C:
+ case OpCode::Id::IADD_R:
+ case OpCode::Id::IADD_IMM: {
+ if (instr.alu_integer.negate_a)
+ op_a = "-(" + op_a + ')';
+
+ if (instr.alu_integer.negate_b)
+ op_b = "-(" + op_b + ')';
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
+ instr.alu.saturate_d);
+ break;
+ }
+ case OpCode::Id::ISCADD_C:
+ case OpCode::Id::ISCADD_R:
+ case OpCode::Id::ISCADD_IMM: {
+ if (instr.alu_integer.negate_a)
+ op_a = "-(" + op_a + ')';
+
+ if (instr.alu_integer.negate_b)
+ op_b = "-(" + op_b + ')';
+
+ std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0,
+ "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
+ break;
+ }
+ case OpCode::Id::LOP_C:
+ case OpCode::Id::LOP_R:
+ case OpCode::Id::LOP_IMM: {
+ ASSERT_MSG(!instr.alu.lop.unk44, "Unimplemented");
+ ASSERT_MSG(instr.alu.lop.pred48 == Pred::UnusedIndex, "Unimplemented");
+
+ if (instr.alu.lop.invert_a)
+ op_a = "~(" + op_a + ')';
+
+ if (instr.alu.lop.invert_b)
+ op_b = "~(" + op_b + ')';
+
+ WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b);
+ break;
+ }
+ default: {
+ NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
+ opcode->GetName());
+ UNREACHABLE();
+ }
+ }
- regs.SetRegisterToInteger(instr.gpr0, true, 0,
- "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
break;
}
case OpCode::Type::Ffma: {
@@ -953,7 +1125,8 @@ private:
switch (opcode->GetId()) {
case OpCode::Id::FFMA_CR: {
- op_b += regs.GetUniform(instr.uniform, instr.gpr0);
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
op_c += regs.GetRegisterAsFloat(instr.gpr39);
break;
}
@@ -964,7 +1137,8 @@ private:
}
case OpCode::Id::FFMA_RC: {
op_b += regs.GetRegisterAsFloat(instr.gpr39);
- op_c += regs.GetUniform(instr.uniform, instr.gpr0);
+ op_c += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
break;
}
case OpCode::Id::FFMA_IMM: {
@@ -978,31 +1152,33 @@ private:
}
}
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1,
+ instr.alu.saturate_d);
break;
}
case OpCode::Type::Conversion: {
- ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented");
ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
- ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented");
switch (opcode->GetId()) {
case OpCode::Id::I2I_R: {
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
- std::string op_a =
- regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
+ std::string op_a = regs.GetRegisterAsInteger(
+ instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
}
- regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
+ 1, instr.alu.saturate_d, 0, instr.conversion.dest_size);
break;
}
case OpCode::Id::I2F_R: {
- std::string op_a =
- regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
+ ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
+ ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
+ std::string op_a = regs.GetRegisterAsInteger(
+ instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
@@ -1012,13 +1188,71 @@ private:
break;
}
case OpCode::Id::F2F_R: {
+ ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
+ ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
+ switch (instr.conversion.f2f.rounding) {
+ case Tegra::Shader::F2fRoundingOp::None:
+ break;
+ case Tegra::Shader::F2fRoundingOp::Floor:
+ op_a = "floor(" + op_a + ')';
+ break;
+ case Tegra::Shader::F2fRoundingOp::Ceil:
+ op_a = "ceil(" + op_a + ')';
+ break;
+ case Tegra::Shader::F2fRoundingOp::Trunc:
+ op_a = "trunc(" + op_a + ')';
+ break;
+ default:
+ NGLOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}",
+ static_cast<u32>(instr.conversion.f2f.rounding.Value()));
+ UNREACHABLE();
+ break;
+ }
+
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
}
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
+ break;
+ }
+ case OpCode::Id::F2I_R: {
+ ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
+ std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
+
+ if (instr.conversion.abs_a) {
+ op_a = "abs(" + op_a + ')';
+ }
+
+ switch (instr.conversion.f2i.rounding) {
+ case Tegra::Shader::F2iRoundingOp::None:
+ break;
+ case Tegra::Shader::F2iRoundingOp::Floor:
+ op_a = "floor(" + op_a + ')';
+ break;
+ case Tegra::Shader::F2iRoundingOp::Ceil:
+ op_a = "ceil(" + op_a + ')';
+ break;
+ case Tegra::Shader::F2iRoundingOp::Trunc:
+ op_a = "trunc(" + op_a + ')';
+ break;
+ default:
+ NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}",
+ static_cast<u32>(instr.conversion.f2i.rounding.Value()));
+ UNREACHABLE();
+ break;
+ }
+
+ if (instr.conversion.is_output_signed) {
+ op_a = "int(" + op_a + ')';
+ } else {
+ op_a = "uint(" + op_a + ')';
+ }
+
+ regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
+ 1, false, 0, instr.conversion.dest_size);
break;
}
default: {
@@ -1029,36 +1263,60 @@ private:
break;
}
case OpCode::Type::Memory: {
- const Attribute::Index attribute = instr.attribute.fmt20.index;
-
switch (opcode->GetId()) {
case OpCode::Id::LD_A: {
ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");
regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element,
- attribute);
+ instr.attribute.fmt20.index);
+ break;
+ }
+ case OpCode::Id::LD_C: {
+ ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented");
+
+ std::string op_a =
+ regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, instr.gpr8,
+ GLSLRegister::Type::Float);
+ std::string op_b =
+ regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, instr.gpr8,
+ GLSLRegister::Type::Float);
+
+ switch (instr.ld_c.type.Value()) {
+ case Tegra::Shader::UniformType::Single:
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
+ break;
+
+ case Tegra::Shader::UniformType::Double:
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0.Value() + 1, 0, op_b, 1, 1);
+ break;
+
+ default:
+ NGLOG_CRITICAL(HW_GPU, "Unhandled type: {}",
+ static_cast<unsigned>(instr.ld_c.type.Value()));
+ UNREACHABLE();
+ }
break;
}
case OpCode::Id::ST_A: {
ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");
- regs.SetOutputAttributeToRegister(attribute, instr.attribute.fmt20.element,
- instr.gpr0);
+ regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index,
+ instr.attribute.fmt20.element, instr.gpr0);
break;
}
case OpCode::Id::TEX: {
- ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested");
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
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
- // them in case they are used as outputs of the texs instruction.
+ // Add an extra scope and declare the texture coords inside to prevent
+ // overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
++shader.scope;
shader.AddLine(coord);
const std::string texture = "texture(" + sampler + ", coords)";
size_t dest_elem{};
- for (size_t elem = 0; elem < instr.attribute.fmt20.size; ++elem) {
+ for (size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
@@ -1071,7 +1329,6 @@ private:
break;
}
case OpCode::Id::TEXS: {
- ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested");
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);
@@ -1083,8 +1340,8 @@ private:
shader.AddLine(coord);
const std::string texture = "texture(" + sampler + ", coords)";
- // TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA goes
- // into gpr28+0 and gpr28+1
+ // TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
+ // goes into gpr28+0 and gpr28+1
size_t offset{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
@@ -1134,7 +1391,8 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
} else {
- op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Float);
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
}
}
@@ -1167,15 +1425,17 @@ private:
}
case OpCode::Type::IntegerSetPredicate: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed);
+ std::string op_b;
- std::string op_b{};
-
- ASSERT_MSG(!instr.is_b_imm, "ISETP_IMM not implemented");
-
- if (instr.is_b_gpr) {
- op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.isetp.is_signed);
+ if (instr.is_b_imm) {
+ op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
} else {
- op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Integer);
+ if (instr.is_b_gpr) {
+ op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.isetp.is_signed);
+ } else {
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Integer);
+ }
}
using Tegra::Shader::Pred;
@@ -1221,7 +1481,8 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
} else {
- op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Float);
+ op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
}
}
@@ -1229,8 +1490,8 @@ private:
op_b = "abs(" + op_b + ')';
}
- // The fset instruction sets a register to 1.0 if the condition is true, and to 0
- // otherwise.
+ // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
+ // condition is true, and to 0 otherwise.
std::string second_pred =
GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
@@ -1248,6 +1509,41 @@ private:
}
break;
}
+ case OpCode::Type::IntegerSet: {
+ std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed);
+
+ 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, 0, instr.iset.is_signed);
+ } else {
+ op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Integer);
+ }
+ }
+
+ // The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
+ // condition is true, and to 0 otherwise.
+ std::string second_pred =
+ GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0);
+
+ std::string comparator = GetPredicateComparison(instr.iset.cond);
+ std::string combiner = GetPredicateCombiner(instr.iset.op);
+
+ std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " +
+ combiner + " (" + second_pred + "))";
+
+ if (instr.iset.bf) {
+ regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
+ } else {
+ regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
+ 1);
+ }
+ break;
+ }
default: {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
@@ -1261,8 +1557,8 @@ private:
shader.AddLine("return true;");
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
+ // 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;
}
@@ -1284,6 +1580,11 @@ private:
regs.SetRegisterToInputAttibute(instr.gpr0, attribute.element, attribute.index);
break;
}
+ case OpCode::Id::SSY: {
+ // The SSY opcode tells the GPU where to re-converge divergent execution paths, we
+ // can ignore this when generating GLSL code.
+ break;
+ }
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
UNREACHABLE();