From 76f178ba6e7cc2925ffada341d1e14fb159e93c7 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Sat, 28 Mar 2020 18:49:50 -0300 Subject: shader/video: Partially implement VMNMX Implements the common usages for VMNMX. Inputs with a different size than 32 bits are not supported and sign mismatches aren't supported either. VMNMX works as follows: It grabs Ra and Rb and applies a maximum/minimum on them (this is defined by .MX), having in mind the input sign. This result can then be saturated. After the intermediate result is calculated, it applies another operation on it using Rc. These operations are merges, accumulations or another min/max pass. This instruction allows to implement with a more flexible approach GCN's min3 and max3 instructions (for instance). --- src/video_core/engines/shader_bytecode.h | 55 ++++++++++++++++++++++++++++++ src/video_core/shader/decode/video.cpp | 58 ++++++++++++++++++++++++++++++++ src/video_core/shader/shader_ir.h | 3 ++ 3 files changed, 116 insertions(+) (limited to 'src') diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index fcb2d7935..7400e1aa9 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -302,6 +302,23 @@ enum class VmadShr : u64 { Shr15 = 2, }; +enum class VmnmxType : u64 { + Bits8, + Bits16, + Bits32, +}; + +enum class VmnmxOperation : u64 { + Mrg_16H = 0, + Mrg_16L = 1, + Mrg_8B0 = 2, + Mrg_8B2 = 3, + Acc = 4, + Min = 5, + Max = 6, + Nop = 7, +}; + enum class XmadMode : u64 { None = 0, CLo = 1, @@ -1662,6 +1679,42 @@ union Instruction { BitField<47, 1, u64> cc; } vmad; + union { + BitField<54, 1, u64> is_dest_signed; + BitField<48, 1, u64> is_src_a_signed; + BitField<49, 1, u64> is_src_b_signed; + BitField<37, 2, u64> src_format_a; + BitField<29, 2, u64> src_format_b; + BitField<56, 1, u64> mx; + BitField<55, 1, u64> sat; + BitField<36, 2, u64> selector_a; + BitField<28, 2, u64> selector_b; + BitField<50, 1, u64> is_op_b_register; + BitField<51, 3, VmnmxOperation> operation; + + VmnmxType SourceFormatA() const { + switch (src_format_a) { + case 0b11: + return VmnmxType::Bits32; + case 0b10: + return VmnmxType::Bits16; + default: + return VmnmxType::Bits8; + } + } + + VmnmxType SourceFormatB() const { + switch (src_format_b) { + case 0b11: + return VmnmxType::Bits32; + case 0b10: + return VmnmxType::Bits16; + default: + return VmnmxType::Bits8; + } + } + } vmnmx; + union { BitField<20, 16, u64> imm20_16; BitField<35, 1, u64> high_b_rr; // used on RR @@ -1773,6 +1826,7 @@ public: MEMBAR, VMAD, VSETP, + VMNMX, FFMA_IMM, // Fused Multiply and Add FFMA_CR, FFMA_RC, @@ -2078,6 +2132,7 @@ private: INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"), INST("01011111--------", Id::VMAD, Type::Video, "VMAD"), INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"), + INST("0011101---------", Id::VMNMX, Type::Video, "VMNMX"), INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 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.h b/src/video_core/shader/shader_ir.h index 80fc9b82c..0f1ebef1b 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -350,6 +350,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); -- cgit v1.2.3