diff options
Diffstat (limited to 'src/video_core/shader')
| -rw-r--r-- | src/video_core/shader/decode/conversion.cpp | 113 | ||||
| -rw-r--r-- | src/video_core/shader/decode/other.cpp | 12 | ||||
| -rw-r--r-- | src/video_core/shader/decode/texture.cpp | 14 | ||||
| -rw-r--r-- | src/video_core/shader/decode/video.cpp | 58 | ||||
| -rw-r--r-- | src/video_core/shader/shader_ir.cpp | 3 | ||||
| -rw-r--r-- | src/video_core/shader/shader_ir.h | 3 |
6 files changed, 173 insertions, 30 deletions
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index c72690b2b..b9989c88c 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <limits> +#include <optional> +#include <utility> + #include "common/assert.h" #include "common/common_types.h" #include "video_core/engines/shader_bytecode.h" @@ -15,9 +19,49 @@ using Tegra::Shader::OpCode; using Tegra::Shader::Register; namespace { + constexpr OperationCode GetFloatSelector(u64 selector) { return selector == 0 ? OperationCode::FCastHalf0 : OperationCode::FCastHalf1; } + +constexpr u32 SizeInBits(Register::Size size) { + switch (size) { + case Register::Size::Byte: + return 8; + case Register::Size::Short: + return 16; + case Register::Size::Word: + return 32; + case Register::Size::Long: + return 64; + } + return 0; +} + +constexpr std::optional<std::pair<s32, s32>> IntegerSaturateBounds(Register::Size src_size, + Register::Size dst_size, + bool src_signed, + bool dst_signed) { + const u32 dst_bits = SizeInBits(dst_size); + if (src_size == Register::Size::Word && dst_size == Register::Size::Word) { + if (src_signed == dst_signed) { + return std::nullopt; + } + return std::make_pair(0, std::numeric_limits<s32>::max()); + } + if (dst_signed) { + // Signed destination, clamp to [-128, 127] for instance + return std::make_pair(-(1 << (dst_bits - 1)), (1 << (dst_bits - 1)) - 1); + } else { + // Unsigned destination + if (dst_bits == 32) { + // Avoid shifting by 32, that is undefined behavior + return std::make_pair(0, s32(std::numeric_limits<u32>::max())); + } + return std::make_pair(0, (1 << dst_bits) - 1); + } +} + } // Anonymous namespace u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { @@ -28,14 +72,13 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { case OpCode::Id::I2I_R: case OpCode::Id::I2I_C: case OpCode::Id::I2I_IMM: { - UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0); - UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word); - UNIMPLEMENTED_IF(instr.alu.saturate_d); + const bool src_signed = instr.conversion.is_input_signed; + const bool dst_signed = instr.conversion.is_output_signed; + const Register::Size src_size = instr.conversion.src_size; + const Register::Size dst_size = instr.conversion.dst_size; + const u32 selector = static_cast<u32>(instr.conversion.int_src.selector); - const bool input_signed = instr.conversion.is_input_signed; - const bool output_signed = instr.conversion.is_output_signed; - - Node value = [&]() { + Node value = [this, instr, opcode] { switch (opcode->get().GetId()) { case OpCode::Id::I2I_R: return GetRegister(instr.gpr20); @@ -48,16 +91,60 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { return Immediate(0); } }(); - value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); - value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, - input_signed); - if (input_signed != output_signed) { - value = SignedOperation(OperationCode::ICastUnsigned, output_signed, NO_PRECISE, value); + // Ensure the source selector is valid + switch (instr.conversion.src_size) { + case Register::Size::Byte: + break; + case Register::Size::Short: + ASSERT(selector == 0 || selector == 2); + break; + default: + ASSERT(selector == 0); + break; + } + + if (src_size != Register::Size::Word || selector != 0) { + value = SignedOperation(OperationCode::IBitfieldExtract, src_signed, std::move(value), + Immediate(selector * 8), Immediate(SizeInBits(src_size))); + } + + value = GetOperandAbsNegInteger(std::move(value), instr.conversion.abs_a, + instr.conversion.negate_a, src_signed); + + if (instr.alu.saturate_d) { + if (src_signed && !dst_signed) { + Node is_negative = Operation(OperationCode::LogicalUGreaterEqual, value, + Immediate(1 << (SizeInBits(src_size) - 1))); + value = Operation(OperationCode::Select, std::move(is_negative), Immediate(0), + std::move(value)); + + // Simplify generated expressions, this can be removed without semantic impact + SetTemporary(bb, 0, std::move(value)); + value = GetTemporary(0); + + if (dst_size != Register::Size::Word) { + const Node limit = Immediate((1 << SizeInBits(dst_size)) - 1); + Node is_large = + Operation(OperationCode::LogicalUGreaterThan, std::move(value), limit); + value = Operation(OperationCode::Select, std::move(is_large), limit, + std::move(value)); + } + } else if (const std::optional bounds = + IntegerSaturateBounds(src_size, dst_size, src_signed, dst_signed)) { + value = SignedOperation(OperationCode::IMax, src_signed, std::move(value), + Immediate(bounds->first)); + value = SignedOperation(OperationCode::IMin, src_signed, std::move(value), + Immediate(bounds->second)); + } + } else if (dst_size != Register::Size::Word) { + // No saturation, we only have to mask the result + Node mask = Immediate((1 << SizeInBits(dst_size)) - 1); + value = Operation(OperationCode::UBitwiseAnd, std::move(value), std::move(mask)); } SetInternalFlagsFromInteger(bb, value, instr.generates_cc); - SetRegister(bb, instr.gpr0, value); + SetRegister(bb, instr.gpr0, std::move(value)); break; } case OpCode::Id::I2F_R: diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index e6edec459..d4f95b18c 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -71,18 +71,24 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { bb.push_back(Operation(OperationCode::Discard)); break; } - case OpCode::Id::MOV_SYS: { + case OpCode::Id::S2R: { const Node value = [this, instr] { switch (instr.sys20) { case SystemVariable::LaneId: - LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete"); + LOG_WARNING(HW_GPU, "S2R instruction with LaneId is incomplete"); return Immediate(0U); case SystemVariable::InvocationId: return Operation(OperationCode::InvocationId); case SystemVariable::Ydirection: return Operation(OperationCode::YNegate); case SystemVariable::InvocationInfo: - LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); + LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete"); + return Immediate(0U); + case SystemVariable::WscaleFactorXY: + UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented"); + return Immediate(0U); + case SystemVariable::WscaleFactorZ: + UNIMPLEMENTED_MSG("S2R WscaleFactorZ is not implemented"); return Immediate(0U); case SystemVariable::Tid: { Node value = Immediate(0); diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index 48350e042..6c4a1358b 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -780,20 +780,6 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is // When lod is used always is in gpr20 const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0); - // Fill empty entries from the guest sampler - const std::size_t entry_coord_count = GetCoordCount(sampler.GetType()); - if (type_coord_count != entry_coord_count) { - LOG_WARNING(HW_GPU, "Bound and built texture types mismatch"); - - // When the size is higher we insert zeroes - for (std::size_t i = type_coord_count; i < entry_coord_count; ++i) { - coords.push_back(GetRegister(Register::ZeroIndex)); - } - - // Then we ensure the size matches the number of entries (dropping unused values) - coords.resize(entry_coord_count); - } - Node4 values; for (u32 element = 0; element < values.size(); ++element) { auto coords_copy = coords; diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp index b047cf870..64ba60ea2 100644 --- a/src/video_core/shader/decode/video.cpp +++ b/src/video_core/shader/decode/video.cpp @@ -10,16 +10,24 @@ namespace VideoCommon::Shader { +using std::move; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; using Tegra::Shader::Pred; using Tegra::Shader::VideoType; using Tegra::Shader::VmadShr; +using Tegra::Shader::VmnmxOperation; +using Tegra::Shader::VmnmxType; u32 ShaderIR::DecodeVideo(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); + if (opcode->get().GetId() == OpCode::Id::VMNMX) { + DecodeVMNMX(bb, instr); + return pc; + } + const Node op_a = GetVideoOperand(GetRegister(instr.gpr8), instr.video.is_byte_chunk_a, instr.video.signed_a, instr.video.type_a, instr.video.byte_height_a); @@ -109,4 +117,54 @@ Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed, } } +void ShaderIR::DecodeVMNMX(NodeBlock& bb, Tegra::Shader::Instruction instr) { + UNIMPLEMENTED_IF(!instr.vmnmx.is_op_b_register); + UNIMPLEMENTED_IF(instr.vmnmx.SourceFormatA() != VmnmxType::Bits32); + UNIMPLEMENTED_IF(instr.vmnmx.SourceFormatB() != VmnmxType::Bits32); + UNIMPLEMENTED_IF(instr.vmnmx.is_src_a_signed != instr.vmnmx.is_src_b_signed); + UNIMPLEMENTED_IF(instr.vmnmx.sat); + UNIMPLEMENTED_IF(instr.generates_cc); + + Node op_a = GetRegister(instr.gpr8); + Node op_b = GetRegister(instr.gpr20); + Node op_c = GetRegister(instr.gpr39); + + const bool is_oper1_signed = instr.vmnmx.is_src_a_signed; // Stubbed + const bool is_oper2_signed = instr.vmnmx.is_dest_signed; + + const auto operation_a = instr.vmnmx.mx ? OperationCode::IMax : OperationCode::IMin; + Node value = SignedOperation(operation_a, is_oper1_signed, move(op_a), move(op_b)); + + switch (instr.vmnmx.operation) { + case VmnmxOperation::Mrg_16H: + value = BitfieldInsert(move(op_c), move(value), 16, 16); + break; + case VmnmxOperation::Mrg_16L: + value = BitfieldInsert(move(op_c), move(value), 0, 16); + break; + case VmnmxOperation::Mrg_8B0: + value = BitfieldInsert(move(op_c), move(value), 0, 8); + break; + case VmnmxOperation::Mrg_8B2: + value = BitfieldInsert(move(op_c), move(value), 16, 8); + break; + case VmnmxOperation::Acc: + value = Operation(OperationCode::IAdd, move(value), move(op_c)); + break; + case VmnmxOperation::Min: + value = SignedOperation(OperationCode::IMin, is_oper2_signed, move(value), move(op_c)); + break; + case VmnmxOperation::Max: + value = SignedOperation(OperationCode::IMax, is_oper2_signed, move(value), move(op_c)); + break; + case VmnmxOperation::Nop: + break; + default: + UNREACHABLE(); + break; + } + + SetRegister(bb, instr.gpr0, move(value)); +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index baf7188d2..8852c8a1b 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -359,6 +359,9 @@ Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const { switch (cc) { case Tegra::Shader::ConditionCode::NEU: return GetInternalFlag(InternalFlag::Zero, true); + case Tegra::Shader::ConditionCode::FCSM_TR: + UNIMPLEMENTED_MSG("EXIT.FCSM_TR is not implemented"); + return MakeNode<PredicateNode>(Pred::NeverExecute, false); default: UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); return MakeNode<PredicateNode>(Pred::NeverExecute, false); diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index ca6c976c9..c6e7bdf50 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -354,6 +354,9 @@ private: /// Marks the usage of a input or output attribute. void MarkAttributeUsage(Tegra::Shader::Attribute::Index index, u64 element); + /// Decodes VMNMX instruction and inserts its code into the passed basic block. + void DecodeVMNMX(NodeBlock& bb, Tegra::Shader::Instruction instr); + void WriteTexInstructionFloat(NodeBlock& bb, Tegra::Shader::Instruction instr, const Node4& components); |
