From a1b845b6514e135a5810b12c20261ec646216c28 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 24 Dec 2018 01:23:00 -0300 Subject: [PATCH] shader_decode: Implement VMAD and VSETP --- src/video_core/CMakeLists.txt | 1 + src/video_core/engines/shader_bytecode.h | 5 +- src/video_core/shader/decode.cpp | 1 + src/video_core/shader/decode/video.cpp | 120 +++++++++++++++++++++++ src/video_core/shader/shader_ir.h | 4 + 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/video_core/shader/decode/video.cpp diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index b68f3273d..d767fe77d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -79,6 +79,7 @@ add_library(video_core STATIC shader/decode/float_set.cpp shader/decode/integer_set.cpp shader/decode/half_set.cpp + shader/decode/video.cpp shader/decode/xmad.cpp shader/decode/other.cpp shader/decode.cpp diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 9cb23f375..cdef97bc6 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1436,6 +1436,7 @@ public: PredicateSetRegister, RegisterSetPredicate, Conversion, + Video, Xmad, Unknown, }; @@ -1567,8 +1568,8 @@ private: INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), - INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), - INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"), + INST("01011111--------", Id::VMAD, Type::Video, "VMAD"), + INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"), 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.cpp b/src/video_core/shader/decode.cpp index 722b32ff1..3265de869 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -174,6 +174,7 @@ u32 ShaderIR::DecodeInstr(BasicBlock& bb, u32 pc) { {OpCode::Type::FloatSet, &ShaderIR::DecodeFloatSet}, {OpCode::Type::IntegerSet, &ShaderIR::DecodeIntegerSet}, {OpCode::Type::HalfSet, &ShaderIR::DecodeHalfSet}, + {OpCode::Type::Video, &ShaderIR::DecodeVideo}, {OpCode::Type::Xmad, &ShaderIR::DecodeXmad}, }; diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp new file mode 100644 index 000000000..9510896e4 --- /dev/null +++ b/src/video_core/shader/decode/video.cpp @@ -0,0 +1,120 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; +using Tegra::Shader::Pred; +using Tegra::Shader::VideoType; +using Tegra::Shader::VmadShr; + +u32 ShaderIR::DecodeVideo(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + 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); + const Node op_b = [&]() { + if (instr.video.use_register_b) { + return GetVideoOperand(GetRegister(instr.gpr20), instr.video.is_byte_chunk_b, + instr.video.signed_b, instr.video.type_b, + instr.video.byte_height_b); + } + if (instr.video.signed_b) { + const auto imm = static_cast(instr.alu.GetImm20_16()); + return Immediate(static_cast(imm)); + } else { + return Immediate(instr.alu.GetImm20_16()); + } + }(); + + switch (opcode->get().GetId()) { + 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 Node op_c = GetRegister(instr.gpr39); + + Node value = SignedOperation(OperationCode::IMul, result_signed, NO_PRECISE, op_a, op_b); + value = SignedOperation(OperationCode::IAdd, result_signed, NO_PRECISE, value, op_c); + + if (instr.vmad.shr == VmadShr::Shr7 || instr.vmad.shr == VmadShr::Shr15) { + const Node shift = Immediate(instr.vmad.shr == VmadShr::Shr7 ? 7 : 15); + value = + SignedOperation(OperationCode::IArithmeticShiftRight, result_signed, value, shift); + } + + SetRegister(bb, instr.gpr0, value); + + break; + } + case OpCode::Id::VSETP: { + // We can't use the constant predicate as destination. + ASSERT(instr.vsetp.pred3 != static_cast(Pred::UnusedIndex)); + + const bool sign = instr.video.signed_a == 1 || instr.video.signed_b == 1; + const Node first_pred = GetPredicateComparisonInteger(instr.vsetp.cond, sign, op_a, op_b); + const Node second_pred = GetPredicate(instr.vsetp.pred39, false); + + const OperationCode combiner = GetPredicateCombiner(instr.vsetp.op); + + // Set the primary predicate to the result of Predicate OP SecondPredicate + SetPredicate(bb, instr.vsetp.pred3, Operation(combiner, first_pred, second_pred)); + + if (instr.vsetp.pred0 != static_cast(Pred::UnusedIndex)) { + // Set the secondary predicate to the result of !Predicate OP SecondPredicate, + // if enabled + const Node negate_pred = Operation(OperationCode::LogicalNegate, first_pred); + SetPredicate(bb, instr.vsetp.pred0, Operation(combiner, negate_pred, second_pred)); + } + break; + } + default: + UNIMPLEMENTED_MSG("Unhandled video instruction: {}", opcode->get().GetName()); + } + + return pc; +} + +Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed, + Tegra::Shader::VideoType type, u64 byte_height) { + if (!is_chunk) { + const auto offset = static_cast(byte_height * 8); + const Node shift = SignedOperation(OperationCode::ILogicalShiftRight, is_signed, NO_PRECISE, + op, Immediate(offset)); + return SignedOperation(OperationCode::IBitwiseAnd, is_signed, NO_PRECISE, shift, + Immediate(0xff)); + } + const Node zero = Immediate(0); + + switch (type) { + case Tegra::Shader::VideoType::Size16_Low: + return SignedOperation(OperationCode::IBitwiseAnd, is_signed, NO_PRECISE, op, + Immediate(0xffff)); + case Tegra::Shader::VideoType::Size16_High: + return SignedOperation(OperationCode::ILogicalShiftRight, is_signed, NO_PRECISE, op, + Immediate(16)); + 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: abort. + UNIMPLEMENTED(); + return zero; + case Tegra::Shader::VideoType::Invalid: + UNREACHABLE_MSG("Invalid instruction encoding"); + return zero; + default: + UNREACHABLE(); + return zero; + } +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 69ff18058..0ea2df6bd 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -595,6 +595,7 @@ private: u32 DecodeFloatSet(BasicBlock& bb, u32 pc); u32 DecodeIntegerSet(BasicBlock& bb, u32 pc); u32 DecodeHalfSet(BasicBlock& bb, u32 pc); + u32 DecodeVideo(BasicBlock& bb, u32 pc); u32 DecodeXmad(BasicBlock& bb, u32 pc); u32 DecodeOther(BasicBlock& bb, u32 pc); @@ -712,6 +713,9 @@ private: bool is_array, std::size_t array_offset, std::size_t bias_offset, std::vector&& coords); + Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type, + u64 byte_height); + void WriteLogicOperation(BasicBlock& bb, Tegra::Shader::Register dest, Tegra::Shader::LogicOperation logic_op, Node op_a, Node op_b, Tegra::Shader::PredicateResultMode predicate_mode,