summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-04-07 02:16:51 -0300
committerGravatar ReinUsesLisp2020-04-07 02:19:44 -0300
commitda706cad25892de3636ecf92bca26bdf66eedd91 (patch)
tree6d03e58d7cd4848d7762fd5771a7e14ad38fa3e0 /src
parentfile_sys: fix LayeredFS error when loading some games made with… (#3602) (diff)
downloadyuzu-da706cad25892de3636ecf92bca26bdf66eedd91.tar.gz
yuzu-da706cad25892de3636ecf92bca26bdf66eedd91.tar.xz
yuzu-da706cad25892de3636ecf92bca26bdf66eedd91.zip
shader/conversion: Implement I2I sign extension, saturation and selection
Reimplements I2I adding sign extension, saturation (clamp source value to the destination), selection and destination sizes that are not 32 bits wide. It doesn't implement CC yet.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/shader/decode/conversion.cpp113
2 files changed, 101 insertions, 14 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 930b605af..59d070d7d 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -2166,7 +2166,7 @@ private:
2166 INST("0011011-11111---", Id::SHF_LEFT_IMM, Type::Shift, "SHF_LEFT_IMM"), 2166 INST("0011011-11111---", Id::SHF_LEFT_IMM, Type::Shift, "SHF_LEFT_IMM"),
2167 INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), 2167 INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
2168 INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), 2168 INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
2169 INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), 2169 INST("0011100-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
2170 INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"), 2170 INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
2171 INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"), 2171 INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
2172 INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"), 2172 INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <limits>
6#include <optional>
7#include <utility>
8
5#include "common/assert.h" 9#include "common/assert.h"
6#include "common/common_types.h" 10#include "common/common_types.h"
7#include "video_core/engines/shader_bytecode.h" 11#include "video_core/engines/shader_bytecode.h"
@@ -15,9 +19,49 @@ using Tegra::Shader::OpCode;
15using Tegra::Shader::Register; 19using Tegra::Shader::Register;
16 20
17namespace { 21namespace {
22
18constexpr OperationCode GetFloatSelector(u64 selector) { 23constexpr OperationCode GetFloatSelector(u64 selector) {
19 return selector == 0 ? OperationCode::FCastHalf0 : OperationCode::FCastHalf1; 24 return selector == 0 ? OperationCode::FCastHalf0 : OperationCode::FCastHalf1;
20} 25}
26
27constexpr u32 SizeInBits(Register::Size size) {
28 switch (size) {
29 case Register::Size::Byte:
30 return 8;
31 case Register::Size::Short:
32 return 16;
33 case Register::Size::Word:
34 return 32;
35 case Register::Size::Long:
36 return 64;
37 }
38 return 0;
39}
40
41constexpr std::optional<std::pair<s32, s32>> IntegerSaturateBounds(Register::Size src_size,
42 Register::Size dst_size,
43 bool src_signed,
44 bool dst_signed) {
45 const u32 dst_bits = SizeInBits(dst_size);
46 if (src_size == Register::Size::Word && dst_size == Register::Size::Word) {
47 if (src_signed == dst_signed) {
48 return std::nullopt;
49 }
50 return std::make_pair(0, std::numeric_limits<s32>::max());
51 }
52 if (dst_signed) {
53 // Signed destination, clamp to [-128, 127] for instance
54 return std::make_pair(-(1 << (dst_bits - 1)), (1 << (dst_bits - 1)) - 1);
55 } else {
56 // Unsigned destination
57 if (dst_bits == 32) {
58 // Avoid shifting by 32, that is undefined behavior
59 return std::make_pair(0, s32(std::numeric_limits<u32>::max()));
60 }
61 return std::make_pair(0, (1 << dst_bits) - 1);
62 }
63}
64
21} // Anonymous namespace 65} // Anonymous namespace
22 66
23u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { 67u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
@@ -28,14 +72,13 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
28 case OpCode::Id::I2I_R: 72 case OpCode::Id::I2I_R:
29 case OpCode::Id::I2I_C: 73 case OpCode::Id::I2I_C:
30 case OpCode::Id::I2I_IMM: { 74 case OpCode::Id::I2I_IMM: {
31 UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0); 75 const bool src_signed = instr.conversion.is_input_signed;
32 UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word); 76 const bool dst_signed = instr.conversion.is_output_signed;
33 UNIMPLEMENTED_IF(instr.alu.saturate_d); 77 const Register::Size src_size = instr.conversion.src_size;
78 const Register::Size dst_size = instr.conversion.dst_size;
79 const u32 selector = static_cast<u32>(instr.conversion.int_src.selector);
34 80
35 const bool input_signed = instr.conversion.is_input_signed; 81 Node value = [this, instr, opcode] {
36 const bool output_signed = instr.conversion.is_output_signed;
37
38 Node value = [&]() {
39 switch (opcode->get().GetId()) { 82 switch (opcode->get().GetId()) {
40 case OpCode::Id::I2I_R: 83 case OpCode::Id::I2I_R:
41 return GetRegister(instr.gpr20); 84 return GetRegister(instr.gpr20);
@@ -48,16 +91,60 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
48 return Immediate(0); 91 return Immediate(0);
49 } 92 }
50 }(); 93 }();
51 value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed);
52 94
53 value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, 95 // Ensure the source selector is valid
54 input_signed); 96 switch (instr.conversion.src_size) {
55 if (input_signed != output_signed) { 97 case Register::Size::Byte:
56 value = SignedOperation(OperationCode::ICastUnsigned, output_signed, NO_PRECISE, value); 98 break;
99 case Register::Size::Short:
100 ASSERT(selector == 0 || selector == 2);
101 break;
102 default:
103 ASSERT(selector == 0);
104 break;
105 }
106
107 if (src_size != Register::Size::Word || selector != 0) {
108 value = SignedOperation(OperationCode::IBitfieldExtract, src_signed, std::move(value),
109 Immediate(selector * 8), Immediate(SizeInBits(src_size)));
110 }
111
112 value = GetOperandAbsNegInteger(std::move(value), instr.conversion.abs_a,
113 instr.conversion.negate_a, src_signed);
114
115 if (instr.alu.saturate_d) {
116 if (src_signed && !dst_signed) {
117 Node is_negative = Operation(OperationCode::LogicalUGreaterEqual, value,
118 Immediate(1 << (SizeInBits(src_size) - 1)));
119 value = Operation(OperationCode::Select, std::move(is_negative), Immediate(0),
120 std::move(value));
121
122 // Simplify generated expressions, this can be removed without semantic impact
123 SetTemporary(bb, 0, std::move(value));
124 value = GetTemporary(0);
125
126 if (dst_size != Register::Size::Word) {
127 const Node limit = Immediate((1 << SizeInBits(dst_size)) - 1);
128 Node is_large =
129 Operation(OperationCode::LogicalUGreaterThan, std::move(value), limit);
130 value = Operation(OperationCode::Select, std::move(is_large), limit,
131 std::move(value));
132 }
133 } else if (const std::optional bounds =
134 IntegerSaturateBounds(src_size, dst_size, src_signed, dst_signed)) {
135 value = SignedOperation(OperationCode::IMax, src_signed, std::move(value),
136 Immediate(bounds->first));
137 value = SignedOperation(OperationCode::IMin, src_signed, std::move(value),
138 Immediate(bounds->second));
139 }
140 } else if (dst_size != Register::Size::Word) {
141 // No saturation, we only have to mask the result
142 Node mask = Immediate((1 << SizeInBits(dst_size)) - 1);
143 value = Operation(OperationCode::UBitwiseAnd, std::move(value), std::move(mask));
57 } 144 }
58 145
59 SetInternalFlagsFromInteger(bb, value, instr.generates_cc); 146 SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
60 SetRegister(bb, instr.gpr0, value); 147 SetRegister(bb, instr.gpr0, std::move(value));
61 break; 148 break;
62 } 149 }
63 case OpCode::Id::I2F_R: 150 case OpCode::Id::I2F_R: