summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-12-26 21:43:44 -0500
committerGravatar GitHub2019-12-26 21:43:44 -0500
commit8a76f816a4586444903180f12a0408a1ae82a82d (patch)
treea92b35863fe491f3e9b8412df7b33c6dc978a07c /src
parentMerge pull request #3244 from ReinUsesLisp/vk-fps (diff)
parentshader/texture: Implement TLD4.PTP (diff)
downloadyuzu-8a76f816a4586444903180f12a0408a1ae82a82d.tar.gz
yuzu-8a76f816a4586444903180f12a0408a1ae82a82d.tar.xz
yuzu-8a76f816a4586444903180f12a0408a1ae82a82d.zip
Merge pull request #3228 from ReinUsesLisp/ptp
shader/texture: Implement AOFFI and PTP for TLD4 and TLD4S
Diffstat (limited to 'src')
-rw-r--r--src/video_core/engines/shader_bytecode.h12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp90
-rw-r--r--src/video_core/shader/decode/texture.cpp108
-rw-r--r--src/video_core/shader/node.h1
-rw-r--r--src/video_core/shader/shader_ir.h5
5 files changed, 142 insertions, 74 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index dfb12cd2d..412ca5551 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1239,7 +1239,7 @@ union Instruction {
1239 BitField<35, 1, u64> ndv_flag; 1239 BitField<35, 1, u64> ndv_flag;
1240 BitField<49, 1, u64> nodep_flag; 1240 BitField<49, 1, u64> nodep_flag;
1241 BitField<50, 1, u64> dc_flag; 1241 BitField<50, 1, u64> dc_flag;
1242 BitField<54, 2, u64> info; 1242 BitField<54, 2, u64> offset_mode;
1243 BitField<56, 2, u64> component; 1243 BitField<56, 2, u64> component;
1244 1244
1245 bool UsesMiscMode(TextureMiscMode mode) const { 1245 bool UsesMiscMode(TextureMiscMode mode) const {
@@ -1251,9 +1251,9 @@ union Instruction {
1251 case TextureMiscMode::DC: 1251 case TextureMiscMode::DC:
1252 return dc_flag != 0; 1252 return dc_flag != 0;
1253 case TextureMiscMode::AOFFI: 1253 case TextureMiscMode::AOFFI:
1254 return info == 1; 1254 return offset_mode == 1;
1255 case TextureMiscMode::PTP: 1255 case TextureMiscMode::PTP:
1256 return info == 2; 1256 return offset_mode == 2;
1257 default: 1257 default:
1258 break; 1258 break;
1259 } 1259 }
@@ -1265,7 +1265,7 @@ union Instruction {
1265 BitField<35, 1, u64> ndv_flag; 1265 BitField<35, 1, u64> ndv_flag;
1266 BitField<49, 1, u64> nodep_flag; 1266 BitField<49, 1, u64> nodep_flag;
1267 BitField<50, 1, u64> dc_flag; 1267 BitField<50, 1, u64> dc_flag;
1268 BitField<33, 2, u64> info; 1268 BitField<33, 2, u64> offset_mode;
1269 BitField<37, 2, u64> component; 1269 BitField<37, 2, u64> component;
1270 1270
1271 bool UsesMiscMode(TextureMiscMode mode) const { 1271 bool UsesMiscMode(TextureMiscMode mode) const {
@@ -1277,9 +1277,9 @@ union Instruction {
1277 case TextureMiscMode::DC: 1277 case TextureMiscMode::DC:
1278 return dc_flag != 0; 1278 return dc_flag != 0;
1279 case TextureMiscMode::AOFFI: 1279 case TextureMiscMode::AOFFI:
1280 return info == 1; 1280 return offset_mode == 1;
1281 case TextureMiscMode::PTP: 1281 case TextureMiscMode::PTP:
1282 return info == 2; 1282 return offset_mode == 2;
1283 default: 1283 default:
1284 break; 1284 break;
1285 } 1285 }
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 0389c2143..a311dbcfe 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -48,10 +48,10 @@ class ExprDecompiler;
48 48
49enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat }; 49enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
50 50
51struct TextureAoffi {}; 51struct TextureOffset {};
52struct TextureDerivates {}; 52struct TextureDerivates {};
53using TextureArgument = std::pair<Type, Node>; 53using TextureArgument = std::pair<Type, Node>;
54using TextureIR = std::variant<TextureAoffi, TextureDerivates, TextureArgument>; 54using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>;
55 55
56constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 56constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
57 static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float)); 57 static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
@@ -1077,7 +1077,7 @@ private:
1077 } 1077 }
1078 1078
1079 std::string GenerateTexture(Operation operation, const std::string& function_suffix, 1079 std::string GenerateTexture(Operation operation, const std::string& function_suffix,
1080 const std::vector<TextureIR>& extras, bool sepparate_dc = false) { 1080 const std::vector<TextureIR>& extras, bool separate_dc = false) {
1081 constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"}; 1081 constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"};
1082 1082
1083 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1083 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
@@ -1090,10 +1090,12 @@ private:
1090 std::string expr = "texture" + function_suffix; 1090 std::string expr = "texture" + function_suffix;
1091 if (!meta->aoffi.empty()) { 1091 if (!meta->aoffi.empty()) {
1092 expr += "Offset"; 1092 expr += "Offset";
1093 } else if (!meta->ptp.empty()) {
1094 expr += "Offsets";
1093 } 1095 }
1094 expr += '(' + GetSampler(meta->sampler) + ", "; 1096 expr += '(' + GetSampler(meta->sampler) + ", ";
1095 expr += coord_constructors.at(count + (has_array ? 1 : 0) + 1097 expr += coord_constructors.at(count + (has_array ? 1 : 0) +
1096 (has_shadow && !sepparate_dc ? 1 : 0) - 1); 1098 (has_shadow && !separate_dc ? 1 : 0) - 1);
1097 expr += '('; 1099 expr += '(';
1098 for (std::size_t i = 0; i < count; ++i) { 1100 for (std::size_t i = 0; i < count; ++i) {
1099 expr += Visit(operation[i]).AsFloat(); 1101 expr += Visit(operation[i]).AsFloat();
@@ -1106,7 +1108,7 @@ private:
1106 expr += ", float(" + Visit(meta->array).AsInt() + ')'; 1108 expr += ", float(" + Visit(meta->array).AsInt() + ')';
1107 } 1109 }
1108 if (has_shadow) { 1110 if (has_shadow) {
1109 if (sepparate_dc) { 1111 if (separate_dc) {
1110 expr += "), " + Visit(meta->depth_compare).AsFloat(); 1112 expr += "), " + Visit(meta->depth_compare).AsFloat();
1111 } else { 1113 } else {
1112 expr += ", " + Visit(meta->depth_compare).AsFloat() + ')'; 1114 expr += ", " + Visit(meta->depth_compare).AsFloat() + ')';
@@ -1118,8 +1120,12 @@ private:
1118 for (const auto& variant : extras) { 1120 for (const auto& variant : extras) {
1119 if (const auto argument = std::get_if<TextureArgument>(&variant)) { 1121 if (const auto argument = std::get_if<TextureArgument>(&variant)) {
1120 expr += GenerateTextureArgument(*argument); 1122 expr += GenerateTextureArgument(*argument);
1121 } else if (std::holds_alternative<TextureAoffi>(variant)) { 1123 } else if (std::holds_alternative<TextureOffset>(variant)) {
1122 expr += GenerateTextureAoffi(meta->aoffi); 1124 if (!meta->aoffi.empty()) {
1125 expr += GenerateTextureAoffi(meta->aoffi);
1126 } else if (!meta->ptp.empty()) {
1127 expr += GenerateTexturePtp(meta->ptp);
1128 }
1123 } else if (std::holds_alternative<TextureDerivates>(variant)) { 1129 } else if (std::holds_alternative<TextureDerivates>(variant)) {
1124 expr += GenerateTextureDerivates(meta->derivates); 1130 expr += GenerateTextureDerivates(meta->derivates);
1125 } else { 1131 } else {
@@ -1160,6 +1166,20 @@ private:
1160 return expr; 1166 return expr;
1161 } 1167 }
1162 1168
1169 std::string ReadTextureOffset(const Node& value) {
1170 if (const auto immediate = std::get_if<ImmediateNode>(&*value)) {
1171 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required
1172 // to be constant by the standard).
1173 return std::to_string(static_cast<s32>(immediate->GetValue()));
1174 } else if (device.HasVariableAoffi()) {
1175 // Avoid using variable AOFFI on unsupported devices.
1176 return Visit(value).AsInt();
1177 } else {
1178 // Insert 0 on devices not supporting variable AOFFI.
1179 return "0";
1180 }
1181 }
1182
1163 std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) { 1183 std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) {
1164 if (aoffi.empty()) { 1184 if (aoffi.empty()) {
1165 return {}; 1185 return {};
@@ -1170,18 +1190,7 @@ private:
1170 expr += '('; 1190 expr += '(';
1171 1191
1172 for (std::size_t index = 0; index < aoffi.size(); ++index) { 1192 for (std::size_t index = 0; index < aoffi.size(); ++index) {
1173 const auto operand{aoffi.at(index)}; 1193 expr += ReadTextureOffset(aoffi.at(index));
1174 if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
1175 // Inline the string as an immediate integer in GLSL (AOFFI arguments are required
1176 // to be constant by the standard).
1177 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
1178 } else if (device.HasVariableAoffi()) {
1179 // Avoid using variable AOFFI on unsupported devices.
1180 expr += Visit(operand).AsInt();
1181 } else {
1182 // Insert 0 on devices not supporting variable AOFFI.
1183 expr += '0';
1184 }
1185 if (index + 1 < aoffi.size()) { 1194 if (index + 1 < aoffi.size()) {
1186 expr += ", "; 1195 expr += ", ";
1187 } 1196 }
@@ -1191,6 +1200,20 @@ private:
1191 return expr; 1200 return expr;
1192 } 1201 }
1193 1202
1203 std::string GenerateTexturePtp(const std::vector<Node>& ptp) {
1204 static constexpr std::size_t num_vectors = 4;
1205 ASSERT(ptp.size() == num_vectors * 2);
1206
1207 std::string expr = ", ivec2[](";
1208 for (std::size_t vector = 0; vector < num_vectors; ++vector) {
1209 const bool has_next = vector + 1 < num_vectors;
1210 expr += fmt::format("ivec2({}, {}){}", ReadTextureOffset(ptp.at(vector * 2)),
1211 ReadTextureOffset(ptp.at(vector * 2 + 1)), has_next ? ", " : "");
1212 }
1213 expr += ')';
1214 return expr;
1215 }
1216
1194 std::string GenerateTextureDerivates(const std::vector<Node>& derivates) { 1217 std::string GenerateTextureDerivates(const std::vector<Node>& derivates) {
1195 if (derivates.empty()) { 1218 if (derivates.empty()) {
1196 return {}; 1219 return {};
@@ -1689,7 +1712,7 @@ private:
1689 ASSERT(meta); 1712 ASSERT(meta);
1690 1713
1691 std::string expr = GenerateTexture( 1714 std::string expr = GenerateTexture(
1692 operation, "", {TextureAoffi{}, TextureArgument{Type::Float, meta->bias}}); 1715 operation, "", {TextureOffset{}, TextureArgument{Type::Float, meta->bias}});
1693 if (meta->sampler.IsShadow()) { 1716 if (meta->sampler.IsShadow()) {
1694 expr = "vec4(" + expr + ')'; 1717 expr = "vec4(" + expr + ')';
1695 } 1718 }
@@ -1701,7 +1724,7 @@ private:
1701 ASSERT(meta); 1724 ASSERT(meta);
1702 1725
1703 std::string expr = GenerateTexture( 1726 std::string expr = GenerateTexture(
1704 operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureAoffi{}}); 1727 operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}});
1705 if (meta->sampler.IsShadow()) { 1728 if (meta->sampler.IsShadow()) {
1706 expr = "vec4(" + expr + ')'; 1729 expr = "vec4(" + expr + ')';
1707 } 1730 }
@@ -1709,21 +1732,19 @@ private:
1709 } 1732 }
1710 1733
1711 Expression TextureGather(Operation operation) { 1734 Expression TextureGather(Operation operation) {
1712 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1735 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1713 ASSERT(meta);
1714 1736
1715 const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; 1737 const auto type = meta.sampler.IsShadow() ? Type::Float : Type::Int;
1716 if (meta->sampler.IsShadow()) { 1738 const bool separate_dc = meta.sampler.IsShadow();
1717 return {GenerateTexture(operation, "Gather", {TextureAoffi{}}, true) + 1739
1718 GetSwizzle(meta->element), 1740 std::vector<TextureIR> ir;
1719 Type::Float}; 1741 if (meta.sampler.IsShadow()) {
1742 ir = {TextureOffset{}};
1720 } else { 1743 } else {
1721 return {GenerateTexture(operation, "Gather", 1744 ir = {TextureOffset{}, TextureArgument{type, meta.component}};
1722 {TextureAoffi{}, TextureArgument{type, meta->component}},
1723 false) +
1724 GetSwizzle(meta->element),
1725 Type::Float};
1726 } 1745 }
1746 return {GenerateTexture(operation, "Gather", ir, separate_dc) + GetSwizzle(meta.element),
1747 Type::Float};
1727 } 1748 }
1728 1749
1729 Expression TextureQueryDimensions(Operation operation) { 1750 Expression TextureQueryDimensions(Operation operation) {
@@ -1794,7 +1815,8 @@ private:
1794 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1815 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1795 ASSERT(meta); 1816 ASSERT(meta);
1796 1817
1797 std::string expr = GenerateTexture(operation, "Grad", {TextureDerivates{}, TextureAoffi{}}); 1818 std::string expr =
1819 GenerateTexture(operation, "Grad", {TextureDerivates{}, TextureOffset{}});
1798 return {std::move(expr) + GetSwizzle(meta->element), Type::Float}; 1820 return {std::move(expr) + GetSwizzle(meta->element), Type::Float};
1799 } 1821 }
1800 1822
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index dff01a541..4b14cdf58 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -89,59 +89,62 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
89 [[fallthrough]]; 89 [[fallthrough]];
90 } 90 }
91 case OpCode::Id::TLD4: { 91 case OpCode::Id::TLD4: {
92 ASSERT(instr.tld4.array == 0);
93 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV), 92 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV),
94 "NDV is not implemented"); 93 "NDV is not implemented");
95 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP),
96 "PTP is not implemented");
97
98 const auto texture_type = instr.tld4.texture_type.Value(); 94 const auto texture_type = instr.tld4.texture_type.Value();
99 const bool depth_compare = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::DC) 95 const bool depth_compare = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::DC)
100 : instr.tld4.UsesMiscMode(TextureMiscMode::DC); 96 : instr.tld4.UsesMiscMode(TextureMiscMode::DC);
101 const bool is_array = instr.tld4.array != 0; 97 const bool is_array = instr.tld4.array != 0;
102 const bool is_aoffi = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::AOFFI) 98 const bool is_aoffi = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::AOFFI)
103 : instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI); 99 : instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI);
104 WriteTexInstructionFloat( 100 const bool is_ptp = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::PTP)
105 bb, instr, 101 : instr.tld4.UsesMiscMode(TextureMiscMode::PTP);
106 GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi, is_bindless)); 102 WriteTexInstructionFloat(bb, instr,
103 GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi,
104 is_ptp, is_bindless));
107 break; 105 break;
108 } 106 }
109 case OpCode::Id::TLD4S: { 107 case OpCode::Id::TLD4S: {
110 const bool uses_aoffi = instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI); 108 constexpr std::size_t num_coords = 2;
111 UNIMPLEMENTED_IF_MSG(uses_aoffi, "AOFFI is not implemented"); 109 const bool is_aoffi = instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI);
112 110 const bool is_depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
113 const bool depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
114 const Node op_a = GetRegister(instr.gpr8); 111 const Node op_a = GetRegister(instr.gpr8);
115 const Node op_b = GetRegister(instr.gpr20); 112 const Node op_b = GetRegister(instr.gpr20);
116 113
117 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 114 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
118 std::vector<Node> coords; 115 std::vector<Node> coords;
119 Node dc_reg; 116 std::vector<Node> aoffi;
120 if (depth_compare) { 117 Node depth_compare;
118 if (is_depth_compare) {
121 // Note: TLD4S coordinate encoding works just like TEXS's 119 // Note: TLD4S coordinate encoding works just like TEXS's
122 const Node op_y = GetRegister(instr.gpr8.Value() + 1); 120 const Node op_y = GetRegister(instr.gpr8.Value() + 1);
123 coords.push_back(op_a); 121 coords.push_back(op_a);
124 coords.push_back(op_y); 122 coords.push_back(op_y);
125 dc_reg = uses_aoffi ? GetRegister(instr.gpr20.Value() + 1) : op_b; 123 if (is_aoffi) {
124 aoffi = GetAoffiCoordinates(op_b, num_coords, true);
125 depth_compare = GetRegister(instr.gpr20.Value() + 1);
126 } else {
127 depth_compare = op_b;
128 }
126 } else { 129 } else {
130 // There's no depth compare
127 coords.push_back(op_a); 131 coords.push_back(op_a);
128 if (uses_aoffi) { 132 if (is_aoffi) {
129 const Node op_y = GetRegister(instr.gpr8.Value() + 1); 133 coords.push_back(GetRegister(instr.gpr8.Value() + 1));
130 coords.push_back(op_y); 134 aoffi = GetAoffiCoordinates(op_b, num_coords, true);
131 } else { 135 } else {
132 coords.push_back(op_b); 136 coords.push_back(op_b);
133 } 137 }
134 dc_reg = {};
135 } 138 }
136 const Node component = Immediate(static_cast<u32>(instr.tld4s.component)); 139 const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
137 140
138 const SamplerInfo info{TextureType::Texture2D, false, depth_compare}; 141 const SamplerInfo info{TextureType::Texture2D, false, is_depth_compare};
139 const Sampler& sampler = *GetSampler(instr.sampler, info); 142 const Sampler& sampler = *GetSampler(instr.sampler, info);
140 143
141 Node4 values; 144 Node4 values;
142 for (u32 element = 0; element < values.size(); ++element) { 145 for (u32 element = 0; element < values.size(); ++element) {
143 auto coords_copy = coords; 146 auto coords_copy = coords;
144 MetaTexture meta{sampler, {}, dc_reg, {}, {}, {}, {}, component, element}; 147 MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element};
145 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 148 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
146 } 149 }
147 150
@@ -190,7 +193,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
190 } 193 }
191 194
192 for (u32 element = 0; element < values.size(); ++element) { 195 for (u32 element = 0; element < values.size(); ++element) {
193 MetaTexture meta{*sampler, {}, {}, {}, derivates, {}, {}, {}, element}; 196 MetaTexture meta{*sampler, {}, {}, {}, {}, derivates, {}, {}, {}, element};
194 values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); 197 values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords);
195 } 198 }
196 199
@@ -230,7 +233,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
230 if (!instr.txq.IsComponentEnabled(element)) { 233 if (!instr.txq.IsComponentEnabled(element)) {
231 continue; 234 continue;
232 } 235 }
233 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, element}; 236 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
234 const Node value = 237 const Node value =
235 Operation(OperationCode::TextureQueryDimensions, meta, 238 Operation(OperationCode::TextureQueryDimensions, meta,
236 GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); 239 GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
@@ -299,7 +302,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
299 continue; 302 continue;
300 } 303 }
301 auto params = coords; 304 auto params = coords;
302 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, element}; 305 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
303 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); 306 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
304 SetTemporary(bb, indexer++, value); 307 SetTemporary(bb, indexer++, value);
305 } 308 }
@@ -367,7 +370,7 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
367 if (it != used_samplers.end()) { 370 if (it != used_samplers.end()) {
368 ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && 371 ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
369 it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer); 372 it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer);
370 return &(*it); 373 return &*it;
371 } 374 }
372 375
373 // Otherwise create a new mapping for this sampler 376 // Otherwise create a new mapping for this sampler
@@ -397,7 +400,7 @@ const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
397 if (it != used_samplers.end()) { 400 if (it != used_samplers.end()) {
398 ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && 401 ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
399 it->IsShadow() == info.is_shadow); 402 it->IsShadow() == info.is_shadow);
400 return &(*it); 403 return &*it;
401 } 404 }
402 405
403 // Otherwise create a new mapping for this sampler 406 // Otherwise create a new mapping for this sampler
@@ -538,7 +541,7 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
538 541
539 for (u32 element = 0; element < values.size(); ++element) { 542 for (u32 element = 0; element < values.size(); ++element) {
540 auto copy_coords = coords; 543 auto copy_coords = coords;
541 MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, bias, lod, {}, element}; 544 MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element};
542 values[element] = Operation(read_method, meta, std::move(copy_coords)); 545 values[element] = Operation(read_method, meta, std::move(copy_coords));
543 } 546 }
544 547
@@ -635,7 +638,9 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
635} 638}
636 639
637Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, 640Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
638 bool is_array, bool is_aoffi, bool is_bindless) { 641 bool is_array, bool is_aoffi, bool is_ptp, bool is_bindless) {
642 ASSERT_MSG(!(is_aoffi && is_ptp), "AOFFI and PTP can't be enabled at the same time");
643
639 const std::size_t coord_count = GetCoordCount(texture_type); 644 const std::size_t coord_count = GetCoordCount(texture_type);
640 645
641 // If enabled arrays index is always stored in the gpr8 field 646 // If enabled arrays index is always stored in the gpr8 field
@@ -661,12 +666,15 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
661 return values; 666 return values;
662 } 667 }
663 668
664 std::vector<Node> aoffi; 669 std::vector<Node> aoffi, ptp;
665 if (is_aoffi) { 670 if (is_aoffi) {
666 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true); 671 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true);
672 } else if (is_ptp) {
673 ptp = GetPtpCoordinates(
674 {GetRegister(parameter_register++), GetRegister(parameter_register++)});
667 } 675 }
668 676
669 Node dc{}; 677 Node dc;
670 if (depth_compare) { 678 if (depth_compare) {
671 dc = GetRegister(parameter_register++); 679 dc = GetRegister(parameter_register++);
672 } 680 }
@@ -676,8 +684,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
676 684
677 for (u32 element = 0; element < values.size(); ++element) { 685 for (u32 element = 0; element < values.size(); ++element) {
678 auto coords_copy = coords; 686 auto coords_copy = coords;
679 MetaTexture meta{*sampler, GetRegister(array_register), dc, aoffi, {}, {}, {}, component, 687 MetaTexture meta{
680 element}; 688 *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element};
681 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 689 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
682 } 690 }
683 691
@@ -710,7 +718,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
710 Node4 values; 718 Node4 values;
711 for (u32 element = 0; element < values.size(); ++element) { 719 for (u32 element = 0; element < values.size(); ++element) {
712 auto coords_copy = coords; 720 auto coords_copy = coords;
713 MetaTexture meta{sampler, array_register, {}, {}, {}, {}, lod, {}, element}; 721 MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element};
714 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); 722 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
715 } 723 }
716 724
@@ -760,7 +768,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
760 Node4 values; 768 Node4 values;
761 for (u32 element = 0; element < values.size(); ++element) { 769 for (u32 element = 0; element < values.size(); ++element) {
762 auto coords_copy = coords; 770 auto coords_copy = coords;
763 MetaTexture meta{sampler, array, {}, {}, {}, {}, lod, {}, element}; 771 MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element};
764 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); 772 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
765 } 773 }
766 return values; 774 return values;
@@ -825,4 +833,38 @@ std::vector<Node> ShaderIR::GetAoffiCoordinates(Node aoffi_reg, std::size_t coor
825 return aoffi; 833 return aoffi;
826} 834}
827 835
836std::vector<Node> ShaderIR::GetPtpCoordinates(std::array<Node, 2> ptp_regs) {
837 static constexpr u32 num_entries = 8;
838
839 std::vector<Node> ptp;
840 ptp.reserve(num_entries);
841
842 const auto global_size = static_cast<s64>(global_code.size());
843 const std::optional low = TrackImmediate(ptp_regs[0], global_code, global_size);
844 const std::optional high = TrackImmediate(ptp_regs[1], global_code, global_size);
845 if (!low || !high) {
846 for (u32 entry = 0; entry < num_entries; ++entry) {
847 const u32 reg = entry / 4;
848 const u32 offset = entry % 4;
849 const Node value = BitfieldExtract(ptp_regs[reg], offset * 8, 6);
850 const Node condition =
851 Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(32));
852 const Node negative = Operation(OperationCode::IAdd, value, Immediate(-64));
853 ptp.push_back(Operation(OperationCode::Select, condition, negative, value));
854 }
855 return ptp;
856 }
857
858 const u64 immediate = (static_cast<u64>(*high) << 32) | static_cast<u64>(*low);
859 for (u32 entry = 0; entry < num_entries; ++entry) {
860 s32 value = (immediate >> (entry * 8)) & 0b111111;
861 if (value >= 32) {
862 value -= 64;
863 }
864 ptp.push_back(Immediate(value));
865 }
866
867 return ptp;
868}
869
828} // namespace VideoCommon::Shader 870} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index abd40f582..4d2f4d6a8 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -374,6 +374,7 @@ struct MetaTexture {
374 Node array; 374 Node array;
375 Node depth_compare; 375 Node depth_compare;
376 std::vector<Node> aoffi; 376 std::vector<Node> aoffi;
377 std::vector<Node> ptp;
377 std::vector<Node> derivates; 378 std::vector<Node> derivates;
378 Node bias; 379 Node bias;
379 Node lod; 380 Node lod;
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 04ae5f822..baed06ccd 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -350,7 +350,8 @@ private:
350 bool is_array); 350 bool is_array);
351 351
352 Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 352 Node4 GetTld4Code(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
353 bool depth_compare, bool is_array, bool is_aoffi, bool is_bindless); 353 bool depth_compare, bool is_array, bool is_aoffi, bool is_ptp,
354 bool is_bindless);
354 355
355 Node4 GetTldCode(Tegra::Shader::Instruction instr); 356 Node4 GetTldCode(Tegra::Shader::Instruction instr);
356 357
@@ -363,6 +364,8 @@ private:
363 364
364 std::vector<Node> GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count, bool is_tld4); 365 std::vector<Node> GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count, bool is_tld4);
365 366
367 std::vector<Node> GetPtpCoordinates(std::array<Node, 2> ptp_regs);
368
366 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type, 369 Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
367 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords, 370 Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords,
368 Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi, 371 Node array, Node depth_compare, u32 bias_offset, std::vector<Node> aoffi,