diff options
Diffstat (limited to 'src/video_core/renderer_opengl/gl_shader_decompiler.cpp')
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 1487 |
1 files changed, 742 insertions, 745 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 5fde22ad4..8d68156bf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -34,6 +34,17 @@ constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); constexpr u32 MAX_GEOMETRY_BUFFERS = 6; constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested +static const char* INTERNAL_FLAG_NAMES[] = {"zero_flag", "sign_flag", "carry_flag", + "overflow_flag"}; + +enum class InternalFlag : u64 { + ZeroFlag = 0, + SignFlag = 1, + CarryFlag = 2, + OverflowFlag = 3, + Amount +}; + class DecompileFail : public std::runtime_error { public: using std::runtime_error::runtime_error; @@ -49,8 +60,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { case Tegra::Shader::OutputTopology::TriangleStrip: return "triangle_strip"; default: - LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology)); return "points"; } } @@ -85,7 +95,8 @@ struct Subroutine { class ControlFlowAnalyzer { public: ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) - : program_code(program_code) { + : program_code(program_code), shader_coverage_begin(main_offset), + shader_coverage_end(main_offset + 1) { // Recursively finds all subroutines. const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); @@ -97,10 +108,16 @@ public: return std::move(subroutines); } + std::size_t GetShaderLength() const { + return shader_coverage_end * sizeof(u64); + } + private: const ProgramCode& program_code; std::set<Subroutine> subroutines; std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; + u32 shader_coverage_begin; + u32 shader_coverage_end; /// Adds and analyzes a new subroutine if it is not added yet. const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { @@ -142,6 +159,9 @@ private: return exit_method; for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { + shader_coverage_begin = std::min(shader_coverage_begin, offset); + shader_coverage_end = std::max(shader_coverage_end, offset + 1); + const Instruction instr = {program_code[offset]}; if (const auto opcode = OpCode::Decode(instr)) { switch (opcode->get().GetId()) { @@ -167,8 +187,8 @@ private: case OpCode::Id::SSY: case OpCode::Id::PBK: { // The SSY and PBK use a similar encoding as the BRA instruction. - ASSERT_MSG(instr.bra.constant_buffer == 0, - "Constant buffer branching is not supported"); + UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, + "Constant buffer branching is not supported"); const u32 target = offset + instr.bra.GetBranchTarget(); labels.insert(target); // Continue scanning for an exit method. @@ -181,14 +201,53 @@ private: } }; +template <typename T> +class ShaderScopedScope { +public: + explicit ShaderScopedScope(T& writer, std::string_view begin_expr, std::string end_expr) + : writer(writer), end_expr(std::move(end_expr)) { + + if (begin_expr.empty()) { + writer.AddLine('{'); + } else { + writer.AddExpression(begin_expr); + writer.AddLine(" {"); + } + ++writer.scope; + } + + ShaderScopedScope(const ShaderScopedScope&) = delete; + + ~ShaderScopedScope() { + --writer.scope; + if (end_expr.empty()) { + writer.AddLine('}'); + } else { + writer.AddExpression("} "); + writer.AddExpression(end_expr); + writer.AddLine(';'); + } + } + + ShaderScopedScope& operator=(const ShaderScopedScope&) = delete; + +private: + T& writer; + std::string end_expr; +}; + class ShaderWriter { public: - void AddLine(std::string_view text) { + void AddExpression(std::string_view text) { DEBUG_ASSERT(scope >= 0); if (!text.empty()) { AppendIndentation(); } shader_source += text; + } + + void AddLine(std::string_view text) { + AddExpression(text); AddNewLine(); } @@ -208,6 +267,11 @@ public: return std::move(shader_source); } + ShaderScopedScope<ShaderWriter> Scope(std::string_view begin_expr = {}, + std::string end_expr = {}) { + return ShaderScopedScope(*this, begin_expr, end_expr); + } + int scope = 0; private: @@ -258,14 +322,6 @@ private: const std::string& suffix; }; -enum class InternalFlag : u64 { - ZeroFlag = 0, - CarryFlag = 1, - OverflowFlag = 2, - NaNFlag = 3, - Amount -}; - /** * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state * of all registers (e.g. whether they are currently being used as Floats or Integers), and @@ -299,8 +355,7 @@ public: // Default - do nothing return value; default: - LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size)); - UNREACHABLE(); + UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size)); } } @@ -363,7 +418,7 @@ public: u64 value_num_components, bool is_saturated = false, u64 dest_elem = 0, Register::Size size = Register::Size::Word, bool sets_cc = false) { - ASSERT_MSG(!is_saturated, "Unimplemented"); + UNIMPLEMENTED_IF(is_saturated); const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; @@ -373,7 +428,7 @@ public: if (sets_cc) { const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); - LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); + LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); } } @@ -392,7 +447,7 @@ public: Tegra::Shader::HalfMerge merge, u64 dest_num_components, u64 value_num_components, bool is_saturated = false, u64 dest_elem = 0) { - ASSERT_MSG(!is_saturated, "Unimplemented"); + UNIMPLEMENTED_IF(is_saturated); const std::string result = [&]() { switch (merge) { @@ -456,24 +511,25 @@ public: shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); } - std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { + std::string GetConditionCode(const Tegra::Shader::ConditionCode cc) const { switch (cc) { - case Tegra::Shader::ControlCode::NEU: + case Tegra::Shader::ConditionCode::NEU: return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; default: - LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); return "false"; } } - std::string GetInternalFlag(const InternalFlag ii) const { - const u32 code = static_cast<u32>(ii); - return "internalFlag_" + std::to_string(code) + suffix; + std::string GetInternalFlag(const InternalFlag flag) const { + const auto index = static_cast<u32>(flag); + ASSERT(index < static_cast<u32>(InternalFlag::Amount)); + + return std::string(INTERNAL_FLAG_NAMES[index]) + '_' + suffix; } - void SetInternalFlag(const InternalFlag ii, const std::string& value) const { - shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); + void SetInternalFlag(const InternalFlag flag, const std::string& value) const { + shader.AddLine(GetInternalFlag(flag) + " = " + value + ';'); } /** @@ -488,27 +544,43 @@ public: const Register& buf_reg) { const std::string dest = GetOutputAttribute(attribute); const std::string src = GetRegisterAsFloat(val_reg); + if (dest.empty()) + return; - if (!dest.empty()) { - // Can happen with unknown/unimplemented output attributes, in which case we ignore the - // instruction for now. - if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { - // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry - // shader. These instructions use a dirty register as buffer index, to avoid some - // drivers from complaining about out of boundary writes, guard them. - const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " + - std::to_string(MAX_GEOMETRY_BUFFERS) + ')'}; - shader.AddLine("amem[" + buf_index + "][" + - std::to_string(static_cast<u32>(attribute)) + ']' + - GetSwizzle(elem) + " = " + src + ';'); - } else { - if (attribute == Attribute::Index::PointSize) { - fixed_pipeline_output_attributes_used.insert(attribute); - shader.AddLine(dest + " = " + src + ';'); - } else { - shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); - } - } + // Can happen with unknown/unimplemented output attributes, in which case we ignore the + // instruction for now. + if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { + // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry + // shader. These instructions use a dirty register as buffer index, to avoid some + // drivers from complaining about out of boundary writes, guard them. + const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " + + std::to_string(MAX_GEOMETRY_BUFFERS) + ')'}; + shader.AddLine("amem[" + buf_index + "][" + + std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) + + " = " + src + ';'); + return; + } + + switch (attribute) { + case Attribute::Index::ClipDistances0123: + case Attribute::Index::ClipDistances4567: { + const u64 index = (attribute == Attribute::Index::ClipDistances4567 ? 4 : 0) + elem; + UNIMPLEMENTED_IF_MSG( + ((header.vtg.clip_distances >> index) & 1) == 0, + "Shader is setting gl_ClipDistance{} without enabling it in the header", index); + + clip_distances[index] = true; + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + '[' + std::to_string(index) + "] = " + src + ';'); + break; + } + case Attribute::Index::PointSize: + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + " = " + src + ';'); + break; + default: + shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + break; } } @@ -575,6 +647,11 @@ public: return used_samplers; } + /// Returns an array of the used clip distances. + const std::array<bool, Maxwell::NumClipDistances>& GetClipDistances() const { + return clip_distances; + } + /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if /// necessary. std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, @@ -624,8 +701,8 @@ private: /// Generates declarations for internal flags. void GenerateInternalFlags() { - for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { - const InternalFlag code = static_cast<InternalFlag>(ii); + for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { + const InternalFlag code = static_cast<InternalFlag>(flag); declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); } declarations.AddNewLine(); @@ -728,12 +805,19 @@ private: void GenerateVertex() { if (stage != Maxwell3D::Regs::ShaderStage::Vertex) return; + bool clip_distances_declared = false; + declarations.AddLine("out gl_PerVertex {"); ++declarations.scope; declarations.AddLine("vec4 gl_Position;"); for (auto& o : fixed_pipeline_output_attributes_used) { if (o == Attribute::Index::PointSize) declarations.AddLine("float gl_PointSize;"); + if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 || + o == Attribute::Index::ClipDistances4567)) { + declarations.AddLine("float gl_ClipDistance[];"); + clip_distances_declared = true; + } } --declarations.scope; declarations.AddLine("};"); @@ -761,8 +845,7 @@ private: u64 dest_num_components, u64 value_num_components, u64 dest_elem, bool precise) { if (reg == Register::ZeroIndex) { - LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); - UNREACHABLE(); + // Setting RZ is a nop in hardware. return; } @@ -777,14 +860,12 @@ private: } if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + // This avoids optimizations of constant propagation and keeps the code as the original // Sadly using the precise keyword causes "linking" errors on fragment shaders. shader.AddLine("precise float tmp = " + src + ';'); shader.AddLine(dest + " = tmp;"); - --shader.scope; - shader.AddLine('}'); } else { shader.AddLine(dest + " = " + src + ';'); } @@ -834,7 +915,8 @@ 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, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))"; + // Config pack's first value is instance_id. + return "vec4(0, 0, uintBitsToFloat(config_pack[0]), uintBitsToFloat(gl_VertexID))"; case Attribute::Index::FrontFacing: // TODO(Subv): Find out what the values are for the other elements. ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); @@ -847,16 +929,13 @@ private: if (declr_input_attribute.count(attribute) == 0) { declr_input_attribute[attribute] = input_mode; } else { - if (declr_input_attribute[attribute] != input_mode) { - LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); - UNREACHABLE(); - } + UNIMPLEMENTED_IF_MSG(declr_input_attribute[attribute] != input_mode, + "Multiple input modes for the same attribute"); } return GeometryPass("input_attribute_" + std::to_string(index)); } - LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); } return "vec4(0, 0, 0, 0)"; @@ -882,24 +961,20 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode)); } } switch (sample_mode) { - case Tegra::Shader::IpaSampleMode::Centroid: { - // Note not implemented, it can be implemented with the "centroid " keyword in glsl; - LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); - UNREACHABLE(); + case Tegra::Shader::IpaSampleMode::Centroid: + // It can be implemented with the "centroid " keyword in glsl + UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid"); break; - } - case Tegra::Shader::IpaSampleMode::Default: { + case Tegra::Shader::IpaSampleMode::Default: // Default, n/a break; - } default: { - LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode)); + break; } } return out; @@ -912,6 +987,10 @@ private: return "gl_PointSize"; case Attribute::Index::Position: return "position"; + case Attribute::Index::ClipDistances0123: + case Attribute::Index::ClipDistances4567: { + return "gl_ClipDistance"; + } default: const u32 index{static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0)}; @@ -920,8 +999,7 @@ private: return "output_attribute_" + std::to_string(index); } - LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled output attribute={}", index); return {}; } } @@ -945,15 +1023,17 @@ private: const std::string& suffix; const Tegra::Shader::Header& header; std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used; + std::array<bool, Maxwell::NumClipDistances> clip_distances{}; u64 local_memory_size; }; class GLSLGenerator { public: GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, - u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) + u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix, + std::size_t shader_length) : subroutines(subroutines), program_code(program_code), main_offset(main_offset), - stage(stage), suffix(suffix) { + stage(stage), suffix(suffix), shader_length(shader_length) { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); local_memory_size = header.GetLocalMemorySize(); regs.SetLocalMemory(local_memory_size); @@ -966,7 +1046,8 @@ public: /// Returns entries in the shader that are useful for external functions ShaderEntries GetEntries() const { - return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; + return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), regs.GetClipDistances(), + shader_length}; } private: @@ -1071,19 +1152,26 @@ private: const std::string& op_a, const std::string& op_b) const { using Tegra::Shader::PredCondition; static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { - {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, - {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, - {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, - {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, - {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; + {PredCondition::LessThan, "<"}, + {PredCondition::Equal, "=="}, + {PredCondition::LessEqual, "<="}, + {PredCondition::GreaterThan, ">"}, + {PredCondition::NotEqual, "!="}, + {PredCondition::GreaterEqual, ">="}, + {PredCondition::LessThanWithNan, "<"}, + {PredCondition::NotEqualWithNan, "!="}, + {PredCondition::LessEqualWithNan, "<="}, + {PredCondition::GreaterThanWithNan, ">"}, + {PredCondition::GreaterEqualWithNan, ">="}}; const auto& comparison{PredicateComparisonStrings.find(condition)}; - ASSERT_MSG(comparison != PredicateComparisonStrings.end(), - "Unknown predicate comparison operation"); + UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(), + "Unknown predicate comparison operation"); std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; if (condition == PredCondition::LessThanWithNan || condition == PredCondition::NotEqualWithNan || + condition == PredCondition::LessEqualWithNan || condition == PredCondition::GreaterThanWithNan || condition == PredCondition::GreaterEqualWithNan) { predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; @@ -1107,7 +1195,7 @@ private: }; auto op = PredicateOperationStrings.find(operation); - ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation"); + UNIMPLEMENTED_IF_MSG(op == PredicateOperationStrings.end(), "Unknown predicate operation"); return op->second; } @@ -1205,8 +1293,7 @@ private: break; } default: - LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(logic_op)); } if (dest != Tegra::Shader::Register::ZeroIndex) { @@ -1224,9 +1311,8 @@ private: SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}", - static_cast<u32>(predicate_mode)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented predicate result mode: {}", + static_cast<u32>(predicate_mode)); } } @@ -1257,14 +1343,7 @@ private: regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); } - void WriteTexsInstruction(const Instruction& instr, const std::string& coord, - const std::string& texture) { - // 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); - + void WriteTexsInstruction(const Instruction& instr, const std::string& texture) { // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 @@ -1287,26 +1366,19 @@ private: ++written_components; } - - --shader.scope; - shader.AddLine('}'); } static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { switch (texture_type) { - case Tegra::Shader::TextureType::Texture1D: { + case Tegra::Shader::TextureType::Texture1D: return 1; - } - case Tegra::Shader::TextureType::Texture2D: { + case Tegra::Shader::TextureType::Texture2D: return 2; - } case Tegra::Shader::TextureType::Texture3D: - case Tegra::Shader::TextureType::TextureCube: { + case Tegra::Shader::TextureType::TextureCube: return 3; - } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type)); return 0; } } @@ -1316,12 +1388,10 @@ private: * top. */ void EmitPushToFlowStack(u32 target) { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;"); shader.AddLine("flow_stack_top++;"); - --shader.scope; - shader.AddLine('}'); } /* @@ -1329,20 +1399,18 @@ private: * popped address and decrementing the stack top. */ void EmitPopFromFlowStack() { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + shader.AddLine("flow_stack_top--;"); shader.AddLine("jmp_to = flow_stack[flow_stack_top];"); shader.AddLine("break;"); - --shader.scope; - shader.AddLine('}'); } /// Writes the output values from a fragment shader to the corresponding GLSL output variables. void EmitFragmentOutputsWrite() { ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); - ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); + UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Samplemask write is unimplemented"); shader.AddLine("if (alpha_test[0] != 0) {"); ++shader.scope; @@ -1408,7 +1476,7 @@ private: case Tegra::Shader::VideoType::Size32: // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when // this type is used (1 * 1 + 0 == 0x5b800000). Until a better - // explanation is found: assert. + // explanation is found: abort. UNIMPLEMENTED(); return zero; case Tegra::Shader::VideoType::Invalid: @@ -1447,6 +1515,161 @@ private: } } + std::pair<size_t, std::string> ValidateAndGetCoordinateElement( + const Tegra::Shader::TextureType texture_type, const bool depth_compare, + const bool is_array, const bool lod_bias_enabled, size_t max_coords, size_t max_inputs) { + const size_t coord_count = TextureCoordinates(texture_type); + + size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0); + const size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0); + if (total_coord_count > max_coords || total_reg_count > max_inputs) { + UNIMPLEMENTED_MSG("Unsupported Texture operation"); + total_coord_count = std::min(total_coord_count, max_coords); + } + // 1D.DC opengl is using a vec3 but 2nd component is ignored later. + total_coord_count += + (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) + ? 1 + : 0; + + constexpr std::array<const char*, 5> coord_container{ + {"", "float coord = (", "vec2 coord = vec2(", "vec3 coord = vec3(", + "vec4 coord = vec4("}}; + + return std::pair<size_t, std::string>(coord_count, coord_container[total_coord_count]); + } + + std::string GetTextureCode(const Tegra::Shader::Instruction& instr, + const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, + const bool depth_compare, const bool is_array, + const size_t bias_offset) { + + if ((texture_type == Tegra::Shader::TextureType::Texture3D && + (is_array || depth_compare)) || + (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && + depth_compare)) { + UNIMPLEMENTED_MSG("This method is not supported."); + } + + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, depth_compare); + + const bool lod_needed = process_mode == Tegra::Shader::TextureProcessMode::LZ || + process_mode == Tegra::Shader::TextureProcessMode::LL || + process_mode == Tegra::Shader::TextureProcessMode::LLA; + + const bool gl_lod_supported = !( + (texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) || + (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array && + depth_compare)); + + const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture("; + std::string texture = read_method + sampler + ", coord"; + + if (process_mode != Tegra::Shader::TextureProcessMode::None) { + if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { + if (gl_lod_supported) { + texture += ", 0"; + } else { + // Lod 0 is emulated by a big negative bias + // in scenarios that are not supported by glsl + texture += ", -1000"; + } + } else { + // If present, lod or bias are always stored in the register indexed by the + // gpr20 + // field with an offset depending on the usage of the other registers + texture += ',' + regs.GetRegisterAsFloat(instr.gpr20.Value() + bias_offset); + } + } + texture += ")"; + return texture; + } + + std::pair<std::string, std::string> GetTEXCode( + const Instruction& instr, const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, + const bool is_array) { + const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && + process_mode != Tegra::Shader::TextureProcessMode::LZ); + + const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( + texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used + const u64 coord_register = array_register + (is_array ? 1 : 0); + + std::string coord = coord_dcl; + for (size_t i = 0; i < coord_count;) { + coord += regs.GetRegisterAsFloat(coord_register + i); + ++i; + if (i != coord_count) { + coord += ','; + } + } + // 1D.DC in opengl the 2nd component is ignored. + if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) { + coord += ",0.0"; + } + if (depth_compare) { + // Depth is always stored in the register signaled by gpr20 + // or in the next register if lod or bias are used + const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); + coord += ',' + regs.GetRegisterAsFloat(depth_register); + } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + return std::make_pair( + coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0)); + } + + std::pair<std::string, std::string> GetTEXSCode( + const Instruction& instr, const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, + const bool is_array) { + const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && + process_mode != Tegra::Shader::TextureProcessMode::LZ); + + const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( + texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4); + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used + const u64 coord_register = array_register + (is_array ? 1 : 0); + const u64 last_coord_register = + (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2)) + ? static_cast<u64>(instr.gpr20.Value()) + : coord_register + 1; + + std::string coord = coord_dcl; + for (size_t i = 0; i < coord_count; ++i) { + const bool last = (i == (coord_count - 1)) && (coord_count > 1); + coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); + if (!last) { + coord += ','; + } + } + + if (depth_compare) { + // Depth is always stored in the register signaled by gpr20 + // or in the next register if lod or bias are used + const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); + coord += ',' + regs.GetRegisterAsFloat(depth_register); + } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + + return std::make_pair(coord, + GetTextureCode(instr, texture_type, process_mode, depth_compare, + is_array, (coord_count > 2 ? 1 : 0))); + } + /** * Compiles a single instruction from Tegra to GLSL. * @param offset the offset of the Tegra shader instruction. @@ -1464,8 +1687,7 @@ private: // Decoding failure if (!opcode) { - LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value); return offset + 1; } @@ -1473,8 +1695,8 @@ private: fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); using Tegra::Shader::Pred; - ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, - "NeverExecute predicate not implemented"); + UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute, + "NeverExecute predicate not implemented"); // Some instructions (like SSY) don't have a predicate field, they are always // unconditionally executed. @@ -1517,37 +1739,36 @@ private: 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. - ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented", - instr.fmul.tab5cb8_2.Value()); - ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", - instr.fmul.tab5c68_1.Value()); - ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", - instr.fmul.tab5c68_0 - .Value()); // SMO typical sends 1 here which seems to be the default - ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, + "FMUL tab5cb8_2({}) is not implemented", + instr.fmul.tab5cb8_2.Value()); + UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0, + "FMUL tab5cb8_1({}) is not implemented", + instr.fmul.tab5c68_1.Value()); + UNIMPLEMENTED_IF_MSG( + instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", + instr.fmul.tab5c68_0 + .Value()); // SMO typical sends 1 here which seems to be the default + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in FMUL is not implemented"); 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, 0, true); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::FADD_C: case OpCode::Id::FADD_R: case OpCode::Id::FADD_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in FADD is not implemented"); + 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, 0, true); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::MUFU: { @@ -1582,15 +1803,17 @@ private: instr.alu.saturate_d, 0, true); break; default: - LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", - static_cast<unsigned>(instr.sub_op.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}", + static_cast<unsigned>(instr.sub_op.Value())); } break; } case OpCode::Id::FMNMX_C: case OpCode::Id::FMNMX_R: case OpCode::Id::FMNMX_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in FMNMX is not implemented"); + 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); @@ -1601,10 +1824,6 @@ private: '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', 1, 1, false, 0, true); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::RRO_C: @@ -1617,9 +1836,7 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled arithmetic instruction: {}", opcode->get().GetName()); } } break; @@ -1631,17 +1848,19 @@ private: break; } case OpCode::Id::FMUL32_IMM: { + UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, + "Condition codes generation in FMUL32 is not implemented"); + regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1, instr.fmul32.saturate, 0, true); - if (instr.op_32.generates_cc) { - LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::FADD32I: { + UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, + "Condition codes generation in FADD32I is not implemented"); + std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string op_b = GetImmediate32(instr); @@ -1662,23 +1881,22 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); - if (instr.op_32.generates_cc) { - LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } } break; } case OpCode::Type::Bfe: { - ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); + UNIMPLEMENTED_IF(instr.bfe.negate_b); std::string op_a = instr.bfe.negate_a ? "-" : ""; op_a += regs.GetRegisterAsInteger(instr.gpr8); switch (opcode->get().GetId()) { case OpCode::Id::BFE_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in BFE is not implemented"); + std::string inner_shift = '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; std::string outer_shift = @@ -1686,20 +1904,35 @@ private: std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName()); } } break; } + case OpCode::Type::Bfi: { + UNIMPLEMENTED_IF(instr.generates_cc); + + const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> { + switch (opcode->get().GetId()) { + case OpCode::Id::BFI_IMM_R: + return {regs.GetRegisterAsInteger(instr.gpr39, 0, false), + std::to_string(instr.alu.GetSignedImm20_20())}; + default: + UNREACHABLE(); + } + }(); + const std::string offset = '(' + packed_shift + " & 0xff)"; + const std::string bits = "((" + packed_shift + " >> 8) & 0xff)"; + const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false); + regs.SetRegisterToInteger( + instr.gpr0, false, 0, + "bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1); + break; + } case OpCode::Type::Shift: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); std::string op_b; @@ -1719,6 +1952,9 @@ private: case OpCode::Id::SHR_C: case OpCode::Id::SHR_R: case OpCode::Id::SHR_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in SHR is not implemented"); + if (!instr.shift.is_signed) { // Logical shift right op_a = "uint(" + op_a + ')'; @@ -1727,24 +1963,17 @@ private: // 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); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::SHL_C: case OpCode::Id::SHL_R: case OpCode::Id::SHL_IMM: + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in SHL is not implemented"); regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code"); - UNREACHABLE(); - } break; default: { - LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName()); } } break; @@ -1755,17 +1984,19 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::IADD32I: + UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, + "Condition codes generation in IADD32I is not implemented"); + 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); - if (instr.op_32.generates_cc) { - LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code"); - UNREACHABLE(); - } break; case OpCode::Id::LOP32I: { + UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, + "Condition codes generation in LOP32I is not implemented"); + if (instr.alu.lop32i.invert_a) op_a = "~(" + op_a + ')'; @@ -1775,16 +2006,11 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, Tegra::Shader::PredicateResultMode::None, Tegra::Shader::Pred::UnusedIndex); - if (instr.op_32.generates_cc) { - LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled ArithmeticIntegerImmediate instruction: {}", + opcode->get().GetName()); } } break; @@ -1807,6 +2033,9 @@ private: case OpCode::Id::IADD_C: case OpCode::Id::IADD_R: case OpCode::Id::IADD_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in IADD is not implemented"); + if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -1815,15 +2044,14 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, instr.alu.saturate_d); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::IADD3_C: case OpCode::Id::IADD3_R: case OpCode::Id::IADD3_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in IADD3 is not implemented"); + std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); auto apply_height = [](auto height, auto& oprand) { @@ -1837,9 +2065,8 @@ private: oprand = "((" + oprand + ") >> 16)"; break; default: - LOG_CRITICAL(HW_GPU, "Unhandled IADD3 height: {}", - static_cast<u32>(height.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}", + static_cast<u32>(height.Value())); } }; @@ -1880,16 +2107,14 @@ private: } regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); - - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::ISCADD_C: case OpCode::Id::ISCADD_R: case OpCode::Id::ISCADD_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in ISCADD is not implemented"); + if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -1900,10 +2125,6 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::POPC_C: @@ -1927,6 +2148,9 @@ private: case OpCode::Id::LOP_C: case OpCode::Id::LOP_R: case OpCode::Id::LOP_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in LOP is not implemented"); + if (instr.alu.lop.invert_a) op_a = "~(" + op_a + ')'; @@ -1935,15 +2159,14 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::LOP3_C: case OpCode::Id::LOP3_R: case OpCode::Id::LOP3_IMM: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in LOP3 is not implemented"); + const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); std::string lut; @@ -1954,17 +2177,15 @@ private: } WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::IMNMX_C: case OpCode::Id::IMNMX_R: case OpCode::Id::IMNMX_IMM: { - ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, - "Unimplemented"); + UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in IMNMX is not implemented"); + const std::string condition = GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); const std::string parameters = op_a + ',' + op_b; @@ -1972,10 +2193,6 @@ private: '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', 1, 1); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::LEA_R2: @@ -2030,24 +2247,19 @@ private: op_b = regs.GetRegisterAsInteger(instr.gpr8); op_a = std::to_string(instr.lea.imm.entry_a); op_c = std::to_string(instr.lea.imm.entry_b); - LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName()); } } - if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) { - LOG_ERROR(HW_GPU, "Unhandled LEA Predicate"); - UNREACHABLE(); - } + UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex), + "Unhandled LEA Predicate"); const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}", + opcode->get().GetName()); } } @@ -2056,7 +2268,7 @@ private: case OpCode::Type::ArithmeticHalf: { if (opcode->get().GetId() == OpCode::Id::HADD2_C || opcode->get().GetId() == OpCode::Id::HADD2_R) { - ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); + UNIMPLEMENTED_IF(instr.alu_half.ftz != 0); } const bool negate_a = opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; @@ -2094,9 +2306,8 @@ private: case OpCode::Id::HMUL2_R: return '(' + op_a + " * " + op_b + ')'; default: - LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", + opcode->get().GetName()); return std::string("0"); } }(); @@ -2107,10 +2318,10 @@ private: } case OpCode::Type::ArithmeticHalfImmediate: { if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { - ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); + UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0); } else { - ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, - "Unimplemented"); + UNIMPLEMENTED_IF(instr.alu_half_imm.precision != + Tegra::Shader::HalfPrecision::None); } const std::string op_a = GetHalfFloat( @@ -2140,11 +2351,14 @@ private: std::string op_b = instr.ffma.negate_b ? "-" : ""; std::string op_c = instr.ffma.negate_c ? "-" : ""; - ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); - ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", - instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO - ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", - instr.ffma.tab5980_1.Value()); + UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.ffma.tab5980_0 != 1, "FFMA tab5980_0({}) not implemented", + instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO + UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented", + instr.ffma.tab5980_1.Value()); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in FFMA is not implemented"); switch (opcode->get().GetId()) { case OpCode::Id::FFMA_CR: { @@ -2170,27 +2384,19 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled FFMA instruction: {}", opcode->get().GetName()); } } regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 1, 1, instr.alu.saturate_d, 0, true); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code"); - UNREACHABLE(); - } - break; } case OpCode::Type::Hfma2: { if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { - ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, - "Unimplemented"); + UNIMPLEMENTED_IF(instr.hfma2.rr.precision != Tegra::Shader::HalfPrecision::None); } else { - ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, - "Unimplemented"); + UNIMPLEMENTED_IF(instr.hfma2.precision != Tegra::Shader::HalfPrecision::None); } const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR ? instr.hfma2.rr.saturate != 0 @@ -2240,7 +2446,7 @@ private: case OpCode::Type::Conversion: { switch (opcode->get().GetId()) { case OpCode::Id::I2I_R: { - ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); + UNIMPLEMENTED_IF(instr.conversion.selector); std::string op_a = regs.GetRegisterAsInteger( instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); @@ -2260,10 +2466,11 @@ private: } case OpCode::Id::I2F_R: case OpCode::Id::I2F_C: { - ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); - ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); - - std::string op_a{}; + UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); + UNIMPLEMENTED_IF(instr.conversion.selector); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in I2F is not implemented"); + std::string op_a; if (instr.is_b_gpr) { op_a = @@ -2286,16 +2493,13 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); - - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code"); - UNREACHABLE(); - } 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"); + UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); + UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in F2F is not implemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); if (instr.conversion.abs_a) { @@ -2322,23 +2526,19 @@ private: op_a = "trunc(" + op_a + ')'; break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", - static_cast<u32>(instr.conversion.f2f.rounding.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", + static_cast<u32>(instr.conversion.f2f.rounding.Value())); break; } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); - - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } case OpCode::Id::F2I_R: case OpCode::Id::F2I_C: { - ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); + UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in F2I is not implemented"); std::string op_a{}; if (instr.is_b_gpr) { @@ -2369,9 +2569,8 @@ private: op_a = "trunc(" + op_a + ')'; break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", - static_cast<u32>(instr.conversion.f2i.rounding.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}", + static_cast<u32>(instr.conversion.f2i.rounding.Value())); break; } @@ -2383,16 +2582,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1, false, 0, instr.conversion.dest_size); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName()); } } break; @@ -2401,10 +2594,10 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::LD_A: { // Note: Shouldn't this be interp mode flat? As in no interpolation made. - ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, - "Indirect attribute loads are not supported"); - ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, - "Unaligned attribute loads are not supported"); + UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, + "Indirect attribute loads are not supported"); + UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, + "Unaligned attribute loads are not supported"); Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, Tegra::Shader::IpaSampleMode::Default}; @@ -2431,12 +2624,9 @@ private: break; } case OpCode::Id::LD_C: { - ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); + UNIMPLEMENTED_IF(instr.ld_c.unknown != 0); - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine("{"); - ++shader.scope; + const auto scope = shader.Scope(); shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); @@ -2459,20 +2649,16 @@ private: break; } default: - LOG_CRITICAL(HW_GPU, "Unhandled type: {}", - static_cast<unsigned>(instr.ld_c.type.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled type: {}", + static_cast<unsigned>(instr.ld_c.type.Value())); } - - --shader.scope; - shader.AddLine("}"); break; } case OpCode::Id::LD_L: { - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine('{'); - ++shader.scope; + UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}", + static_cast<unsigned>(instr.ld_l.unknown.Value())); + + const auto scope = shader.Scope(); std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + std::to_string(instr.smem_imm.Value()) + ')'; @@ -2481,31 +2667,21 @@ private: const std::string op_a = regs.GetLocalMemoryAsFloat("index"); - if (instr.ld_l.unknown != 1) { - LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}", - static_cast<unsigned>(instr.ld_l.unknown.Value())); - UNREACHABLE(); - } - switch (instr.ldst_sl.type.Value()) { case Tegra::Shader::StoreType::Bytes32: regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); break; default: - LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", - static_cast<unsigned>(instr.ldst_sl.type.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("LD_L Unhandled type: {}", + static_cast<unsigned>(instr.ldst_sl.type.Value())); } - - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::ST_A: { - ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, - "Indirect attribute loads are not supported"); - ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, - "Unaligned attribute loads are not supported"); + UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, + "Indirect attribute loads are not supported"); + UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, + "Unaligned attribute loads are not supported"); u64 next_element = instr.attribute.fmt20.element; u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); @@ -2530,360 +2706,157 @@ private: break; } case OpCode::Id::ST_L: { - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine('{'); - ++shader.scope; + UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}", + static_cast<unsigned>(instr.st_l.unknown.Value())); + + const auto scope = shader.Scope(); std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + std::to_string(instr.smem_imm.Value()) + ')'; shader.AddLine("uint index = (" + op + " / 4);"); - if (instr.st_l.unknown != 0) { - LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}", - static_cast<unsigned>(instr.st_l.unknown.Value())); - UNREACHABLE(); - } - switch (instr.ldst_sl.type.Value()) { case Tegra::Shader::StoreType::Bytes32: regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); break; default: - LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", - static_cast<unsigned>(instr.ldst_sl.type.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("ST_L Unhandled type: {}", + static_cast<unsigned>(instr.ldst_sl.type.Value())); } - - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::TEX: { Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; - std::string coord; const bool is_array = instr.tex.array != 0; - - ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), - "AOFFI is not implemented"); - const bool depth_compare = instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - u32 num_coordinates = TextureCoordinates(texture_type); - if (depth_compare) - num_coordinates += 1; - - switch (num_coordinates) { - case 1: { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + index + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - coord = "float coords = " + x + ';'; - } - break; - } - case 2: { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - } - break; - } - case 3: { - if (depth_compare) { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + - ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } - } else { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3); - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + - ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } - } - break; - } - default: - LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); - UNREACHABLE(); + const auto process_mode = instr.tex.GetTextureProcessMode(); + UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); - // Fallback to interpreting as a 2D texture for now - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - texture_type = Tegra::Shader::TextureType::Texture2D; - } - // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias - // or lod. - std::string op_c; - - const std::string sampler = - GetSampler(instr.sampler, texture_type, is_array, depth_compare); - // 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. + const auto [coord, texture] = + GetTEXCode(instr, texture_type, process_mode, depth_compare, is_array); - shader.AddLine("{"); - ++shader.scope; + const auto scope = shader.Scope(); shader.AddLine(coord); - std::string texture; - switch (instr.tex.GetTextureProcessMode()) { - case Tegra::Shader::TextureProcessMode::None: { - texture = "texture(" + sampler + ", coords)"; - break; - } - case Tegra::Shader::TextureProcessMode::LZ: { - texture = "textureLod(" + sampler + ", coords, 0.0)"; - break; - } - case Tegra::Shader::TextureProcessMode::LB: - case Tegra::Shader::TextureProcessMode::LBA: { - if (depth_compare) { - if (is_array) - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); - else - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20); - } - // TODO: Figure if A suffix changes the equation at all. - texture = "texture(" + sampler + ", coords, " + op_c + ')'; - break; - } - case Tegra::Shader::TextureProcessMode::LL: - case Tegra::Shader::TextureProcessMode::LLA: { - if (num_coordinates <= 2) { - op_c = regs.GetRegisterAsFloat(instr.gpr20); - } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - } - // TODO: Figure if A suffix changes the equation at all. - texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; - break; - } - default: { - texture = "texture(" + sampler + ", coords)"; - LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", - static_cast<u32>(instr.tex.GetTextureProcessMode())); - UNREACHABLE(); - } - } - if (!depth_compare) { + if (depth_compare) { + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); + } else { + shader.AddLine("vec4 texture_tmp = " + texture + ';'); std::size_t dest_elem{}; for (std::size_t elem = 0; elem < 4; ++elem) { if (!instr.tex.IsComponentEnabled(elem)) { // Skip disabled components continue; } - regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + dest_elem); ++dest_elem; } - } else { - regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } - --shader.scope; - shader.AddLine("}"); break; } case OpCode::Id::TEXS: { - std::string coord; Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; - bool is_array{instr.texs.IsArrayTexture()}; - - ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - + const bool is_array{instr.texs.IsArrayTexture()}; const bool depth_compare = instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - u32 num_coordinates = TextureCoordinates(texture_type); - if (depth_compare) - num_coordinates += 1; + const auto process_mode = instr.texs.GetTextureProcessMode(); + UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); - switch (num_coordinates) { - case 2: { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - } - break; - } - case 3: { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = - "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } - break; - } - default: - LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); - UNREACHABLE(); + const auto scope = shader.Scope(); + + const auto [coord, texture] = + GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array); + + shader.AddLine(coord); - // Fallback to interpreting as a 2D texture for now - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - texture_type = Tegra::Shader::TextureType::Texture2D; - is_array = false; - } - const std::string sampler = - GetSampler(instr.sampler, texture_type, is_array, depth_compare); - std::string texture; - switch (instr.texs.GetTextureProcessMode()) { - case Tegra::Shader::TextureProcessMode::None: { - texture = "texture(" + sampler + ", coords)"; - break; - } - case Tegra::Shader::TextureProcessMode::LZ: { - if (depth_compare && is_array) { - texture = "texture(" + sampler + ", coords)"; - } else { - texture = "textureLod(" + sampler + ", coords, 0.0)"; - } - break; - } - case Tegra::Shader::TextureProcessMode::LL: { - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; - break; - } - default: { - texture = "texture(" + sampler + ", coords)"; - LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", - static_cast<u32>(instr.texs.GetTextureProcessMode())); - UNREACHABLE(); - } - } if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");"); } + + WriteTexsInstruction(instr, "texture_tmp"); break; } case OpCode::Id::TLDS: { - std::string coord; const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; const bool is_array{instr.tlds.IsArrayTexture()}; ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); ASSERT(is_array == false); - ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), - "AOFFI is not implemented"); - ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), - "MZ is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), + "MZ is not implemented"); - u32 op_c_offset = 0; + u32 extra_op_offset = 0; + + ShaderScopedScope scope = shader.Scope(); switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - coord = "int coords = " + x + ';'; + shader.AddLine("float coords = " + x + ';'); break; } case Tegra::Shader::TextureType::Texture2D: { - if (is_array) { - LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); - UNREACHABLE(); - } else { - const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - const std::string y = regs.GetRegisterAsInteger(instr.gpr20); - coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; - op_c_offset = 1; - } + UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture"); + + const std::string x = regs.GetRegisterAsInteger(instr.gpr8); + const std::string y = regs.GetRegisterAsInteger(instr.gpr20); + // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); + shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); + extra_op_offset = 1; break; } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", - static_cast<u32>(texture_type)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); } const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false); - std::string texture = "texelFetch(" + sampler + ", coords, 0)"; - switch (instr.tlds.GetTextureProcessMode()) { - case Tegra::Shader::TextureProcessMode::LZ: { - texture = "texelFetch(" + sampler + ", coords, 0)"; - break; - } - case Tegra::Shader::TextureProcessMode::LL: { - const std::string op_c = - regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); - texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; - break; - } - default: { - texture = "texelFetch(" + sampler + ", coords, 0)"; - LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", - static_cast<u32>(instr.tlds.GetTextureProcessMode())); - UNREACHABLE(); - } - } - WriteTexsInstruction(instr, coord, texture); + + const std::string texture = [&]() { + switch (instr.tlds.GetTextureProcessMode()) { + case Tegra::Shader::TextureProcessMode::LZ: + return "texelFetch(" + sampler + ", coords, 0)"; + case Tegra::Shader::TextureProcessMode::LL: + shader.AddLine( + "float lod = " + + regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); + return "texelFetch(" + sampler + ", coords, lod)"; + default: + UNIMPLEMENTED_MSG("Unhandled texture process mode {}", + static_cast<u32>(instr.tlds.GetTextureProcessMode())); + return "texelFetch(" + sampler + ", coords, 0)"; + } + }(); + + WriteTexsInstruction(instr, texture); break; } case OpCode::Id::TLD4: { ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); ASSERT(instr.tld4.array == 0); - std::string coord; - - ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), - "AOFFI is not implemented"); - ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), - "NDV is not implemented"); - ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), - "PTP is not implemented"); + + UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), + "NDV is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), + "PTP is not implemented"); const bool depth_compare = instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); auto texture_type = instr.tld4.texture_type.Value(); @@ -2891,40 +2864,40 @@ private: if (depth_compare) num_coordinates += 1; + const auto scope = shader.Scope(); + switch (num_coordinates) { case 2: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); break; } case 3: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); break; } default: - LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled coordinates number {}", + static_cast<u32>(num_coordinates)); const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; } const std::string sampler = GetSampler(instr.sampler, texture_type, false, depth_compare); - // 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 = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4.component) + ')'; - if (!depth_compare) { + + if (depth_compare) { + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); + } else { std::size_t dest_elem{}; for (std::size_t elem = 0; elem < 4; ++elem) { if (!instr.tex.IsComponentEnabled(elem)) { @@ -2934,18 +2907,18 @@ private: regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); ++dest_elem; } - } else { - regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } - --shader.scope; - shader.AddLine("}"); break; } case OpCode::Id::TLD4S: { - ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), - "AOFFI is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + + const auto scope = shader.Scope(); const bool depth_compare = instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); @@ -2954,52 +2927,58 @@ private: // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. const std::string sampler = GetSampler( instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); - std::string coord; - if (!depth_compare) { - coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; - } else { + if (depth_compare) { // Note: TLD4S coordinate encoding works just like TEXS's - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; + const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + shader.AddLine("vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");"); + } else { + shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");"); } - const std::string texture = "textureGather(" + sampler + ", coords, " + - std::to_string(instr.tld4s.component) + ')'; - if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); - } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + std::string texture = "textureGather(" + sampler + ", coords, " + + std::to_string(instr.tld4s.component) + ')'; + if (depth_compare) { + texture = "vec4(" + texture + ')'; } + WriteTexsInstruction(instr, texture); break; } case OpCode::Id::TXQ: { - ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + + const auto scope = shader.Scope(); - // TODO: the new commits on the texture refactor, change the way samplers work. + // TODO: The new commits on the texture refactor, change the way samplers work. // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. const std::string sampler = GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); switch (instr.txq.query_type) { case Tegra::Shader::TextureQueryType::Dimension: { - const std::string texture = "textureQueryLevels(" + sampler + ')'; - regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); + const std::string texture = "textureSize(" + sampler + ", " + + regs.GetRegisterAsInteger(instr.gpr8) + ')'; + const std::string mip_level = "textureQueryLevels(" + sampler + ')'; + shader.AddLine("ivec2 sizes = " + texture + ';'); + + regs.SetRegisterToInteger(instr.gpr0.Value() + 0, true, 0, "sizes.x", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1); break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", - static_cast<u32>(instr.txq.query_type.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled texture query type: {}", + static_cast<u32>(instr.txq.query_type.Value())); } } break; } case OpCode::Id::TMML: { - ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), - "NODEP is not implemented"); - ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), - "NDV is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), + "NDV is not implemented"); const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const bool is_array = instr.tmml.array != 0; @@ -3007,47 +2986,38 @@ private: const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false); - // TODO: add coordinates for different samplers once other texture types are + const auto scope = shader.Scope(); + + // TODO: Add coordinates for different samplers once other texture types are // implemented. - std::string coord; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { - coord = "float coords = " + x + ';'; + shader.AddLine("float coords = " + x + ';'); break; } case Tegra::Shader::TextureType::Texture2D: { const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); break; } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", - static_cast<u32>(texture_type)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); // Fallback to interpreting as a 2D texture for now const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; } - // 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 = "textureQueryLod(" + sampler + ", coords)"; - const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);"; - shader.AddLine(tmp); + shader.AddLine("vec2 tmp = " + texture + " * vec2(256.0, 256.0);"); regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1); regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1); - --shader.scope; - shader.AddLine('}'); break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); } } break; @@ -3133,7 +3103,7 @@ private: break; } case OpCode::Type::HalfSetPredicate: { - ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); + UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); const std::string op_a = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, @@ -3178,6 +3148,9 @@ private: break; } case OpCode::Type::PredicateSetRegister: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in PSET is not implemented"); + const std::string op_a = GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); const std::string op_b = @@ -3198,12 +3171,6 @@ private: const std::string value = '(' + result + ") ? 1.0 : 0.0"; regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); } - - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code"); - UNREACHABLE(); - } - break; } case OpCode::Type::PredicateSetPredicate: { @@ -3241,25 +3208,51 @@ private: const std::string pred = GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); const std::string combiner = GetPredicateCombiner(instr.csetp.op); - const std::string control_code = regs.GetControlCode(instr.csetp.cc); + const std::string condition_code = regs.GetConditionCode(instr.csetp.cc); if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { SetPredicate(instr.csetp.pred3, - '(' + control_code + ") " + combiner + " (" + pred + ')'); + '(' + condition_code + ") " + combiner + " (" + pred + ')'); } if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { SetPredicate(instr.csetp.pred0, - "!(" + control_code + ") " + combiner + " (" + pred + ')'); + "!(" + condition_code + ") " + combiner + " (" + pred + ')'); } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", - opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled predicate instruction: {}", opcode->get().GetName()); } } break; } + case OpCode::Type::RegisterSetPredicate: { + UNIMPLEMENTED_IF(instr.r2p.mode != Tegra::Shader::R2pMode::Pr); + + const std::string apply_mask = [&]() { + switch (opcode->get().GetId()) { + case OpCode::Id::R2P_IMM: + return std::to_string(instr.r2p.immediate_mask); + default: + UNREACHABLE(); + } + }(); + const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + + " >> " + std::to_string(instr.r2p.byte) + ')'; + + constexpr u64 programmable_preds = 7; + for (u64 pred = 0; pred < programmable_preds; ++pred) { + const auto shift = std::to_string(1 << pred); + + shader.AddLine("if ((" + apply_mask + " & " + shift + ") != 0) {"); + ++shader.scope; + + SetPredicate(pred, '(' + mask + " & " + shift + ") != 0"); + + --shader.scope; + shader.AddLine('}'); + } + break; + } case OpCode::Type::FloatSet: { const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fset.abs_a != 0, instr.fset.neg_a != 0); @@ -3297,6 +3290,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1, 1); } + if (instr.generates_cc.Value() != 0) { + regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate); + LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete"); + } break; } case OpCode::Type::IntegerSet: { @@ -3335,7 +3332,7 @@ private: break; } case OpCode::Type::HalfSet: { - ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); + UNIMPLEMENTED_IF(instr.hset2.ftz != 0); const std::string op_a = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, @@ -3379,15 +3376,17 @@ private: break; } case OpCode::Type::Xmad: { - ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); - ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); + UNIMPLEMENTED_IF(instr.xmad.sign_a); + UNIMPLEMENTED_IF(instr.xmad.sign_b); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in XMAD is not implemented"); std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; std::string op_b; std::string op_c; // TODO(bunnei): Needs to be fixed once op_a or op_b is signed - ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); + UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b); const bool is_signed{instr.xmad.sign_a == 1}; bool is_merge{}; @@ -3420,8 +3419,7 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); } } @@ -3457,9 +3455,8 @@ private: op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; break; default: { - LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", - static_cast<u32>(instr.xmad.mode.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}", + static_cast<u32>(instr.xmad.mode.Value())); } } @@ -3469,25 +3466,19 @@ private: } regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code"); - UNREACHABLE(); - } break; } default: { switch (opcode->get().GetId()) { case OpCode::Id::EXIT: { + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "EXIT condition code used: {}", static_cast<u32>(cc)); + if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { EmitFragmentOutputsWrite(); } - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc)); - UNREACHABLE(); - } - switch (instr.flow.cond) { case Tegra::Shader::FlowCondition::Always: shader.AddLine("return true;"); @@ -3502,26 +3493,24 @@ private: case Tegra::Shader::FlowCondition::Fcsm_Tr: // TODO(bunnei): What is this used for? If we assume this conditon is not // satisifed, dual vertex shaders in Farming Simulator make more sense - LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr"); + UNIMPLEMENTED_MSG("Skipping unknown FlowCondition::Fcsm_Tr"); break; default: - LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}", - static_cast<u32>(instr.flow.cond.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled flow condition: {}", + static_cast<u32>(instr.flow.cond.Value())); } break; } case OpCode::Id::KIL: { - ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); + UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always); + + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "KIL condition code used: {}", static_cast<u32>(cc)); // Enclose "discard" in a conditional, so that GLSL compilation does not complain // about unexecuted instructions that may follow this. - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc)); - UNREACHABLE(); - } shader.AddLine("if (true) {"); ++shader.scope; shader.AddLine("discard;"); @@ -3531,7 +3520,8 @@ private: break; } case OpCode::Id::OUT_R: { - ASSERT(instr.gpr20.Value() == Register::ZeroIndex); + UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex, + "Stream buffer is not supported"); ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, "OUT is expected to be used in a geometry shader."); @@ -3557,19 +3547,23 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); break; } + case Tegra::Shader::SystemVariable::Ydirection: { + // Config pack's third value is Y_NEGATE's state. + regs.SetRegisterToFloat(instr.gpr0, 0, "uintBitsToFloat(config_pack[2])", 1, 1); + break; + } default: { - LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", - static_cast<u32>(instr.sys20.Value())); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unhandled system move: {}", + static_cast<u32>(instr.sys20.Value())); } } break; } case OpCode::Id::ISBERD: { - ASSERT(instr.isberd.o == 0); - ASSERT(instr.isberd.skew == 0); - ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); - ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); + UNIMPLEMENTED_IF(instr.isberd.o != 0); + UNIMPLEMENTED_IF(instr.isberd.skew != 0); + UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None); + UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None); ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, "ISBERD is expected to be used in a geometry shader."); LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); @@ -3577,15 +3571,21 @@ private: break; } case OpCode::Id::BRA: { - ASSERT_MSG(instr.bra.constant_buffer == 0, - "BRA with constant buffers are not implemented"); - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc)); - UNREACHABLE(); - } + UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, + "BRA with constant buffers are not implemented"); + + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; const u32 target = offset + instr.bra.GetBranchTarget(); - shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + if (cc != Tegra::Shader::ConditionCode::T) { + const std::string condition_code = regs.GetConditionCode(cc); + shader.AddLine("if (" + condition_code + "){"); + shader.scope++; + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + shader.scope--; + shader.AddLine('}'); + } else { + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + } break; } case OpCode::Id::IPA: { @@ -3606,7 +3606,8 @@ private: // The SSY opcode tells the GPU where to re-converge divergent execution paths, it // sets the target of the jump that the SYNC instruction will make. The SSY opcode // has a similar structure to the BRA opcode. - ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); + UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, + "Constant buffer flow is not supported"); const u32 target = offset + instr.bra.GetBranchTarget(); EmitPushToFlowStack(target); @@ -3616,29 +3617,28 @@ private: // PBK pushes to a stack the address where BRK will jump to. This shares stack with // SSY but using SYNC on a PBK address will kill the shader execution. We don't // emulate this because it's very unlikely a driver will emit such invalid shader. - ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported"); + UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, + "Constant buffer PBK is not supported"); const u32 target = offset + instr.bra.GetBranchTarget(); EmitPushToFlowStack(target); break; } case OpCode::Id::SYNC: { + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "SYNC condition code used: {}", static_cast<u32>(cc)); + // The SYNC opcode jumps to the address previously set by the SSY opcode - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc)); - UNREACHABLE(); - } EmitPopFromFlowStack(); break; } case OpCode::Id::BRK: { // The BRK opcode jumps to the address previously set by the PBK opcode - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc)); - UNREACHABLE(); - } + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "BRK condition code used: {}", static_cast<u32>(cc)); + EmitPopFromFlowStack(); break; } @@ -3649,6 +3649,9 @@ private: break; } case OpCode::Id::VMAD: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in VMAD is not implemented"); + const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; const std::string op_a = GetVideoOperandA(instr); const std::string op_b = GetVideoOperandB(instr); @@ -3668,11 +3671,6 @@ private: regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, instr.vmad.saturate == 1, 0, Register::Size::Word, instr.vmad.cc); - if (instr.generates_cc) { - LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code"); - UNREACHABLE(); - } - break; } case OpCode::Id::VSETP: { @@ -3699,10 +3697,7 @@ private: } break; } - default: { - LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName()); - UNREACHABLE(); - } + default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } } break; @@ -3827,6 +3822,7 @@ private: Maxwell3D::Regs::ShaderStage stage; const std::string& suffix; u64 local_memory_size; + std::size_t shader_length; ShaderWriter shader; ShaderWriter declarations; @@ -3845,9 +3841,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) { try { - const auto subroutines = - ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); - GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); + ControlFlowAnalyzer analyzer(program_code, main_offset, suffix); + const auto subroutines = analyzer.GetSubroutines(); + GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix, + analyzer.GetShaderLength()); return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; } catch (const DecompileFail& exception) { LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); @@ -3855,4 +3852,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u return {}; } -} // namespace OpenGL::GLShader::Decompiler +} // namespace OpenGL::GLShader::Decompiler
\ No newline at end of file |