texture.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <vector>
  6. #include <fmt/format.h>
  7. #include "common/assert.h"
  8. #include "common/bit_field.h"
  9. #include "common/common_types.h"
  10. #include "common/logging/log.h"
  11. #include "video_core/engines/shader_bytecode.h"
  12. #include "video_core/shader/shader_ir.h"
  13. namespace VideoCommon::Shader {
  14. using Tegra::Shader::Instruction;
  15. using Tegra::Shader::OpCode;
  16. using Tegra::Shader::Register;
  17. using Tegra::Shader::TextureMiscMode;
  18. using Tegra::Shader::TextureProcessMode;
  19. using Tegra::Shader::TextureType;
  20. static std::size_t GetCoordCount(TextureType texture_type) {
  21. switch (texture_type) {
  22. case TextureType::Texture1D:
  23. return 1;
  24. case TextureType::Texture2D:
  25. return 2;
  26. case TextureType::Texture3D:
  27. case TextureType::TextureCube:
  28. return 3;
  29. default:
  30. UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type));
  31. return 0;
  32. }
  33. }
  34. u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
  35. const Instruction instr = {program_code[pc]};
  36. const auto opcode = OpCode::Decode(instr);
  37. bool is_bindless = false;
  38. switch (opcode->get().GetId()) {
  39. case OpCode::Id::TEX: {
  40. if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
  41. LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
  42. }
  43. const TextureType texture_type{instr.tex.texture_type};
  44. const bool is_array = instr.tex.array != 0;
  45. const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
  46. const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC);
  47. const auto process_mode = instr.tex.GetTextureProcessMode();
  48. WriteTexInstructionFloat(
  49. bb, instr,
  50. GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi, {}));
  51. break;
  52. }
  53. case OpCode::Id::TEX_B: {
  54. UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
  55. "AOFFI is not implemented");
  56. if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
  57. LOG_WARNING(HW_GPU, "TEX.NODEP implementation is incomplete");
  58. }
  59. const TextureType texture_type{instr.tex_b.texture_type};
  60. const bool is_array = instr.tex_b.array != 0;
  61. const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
  62. const bool depth_compare = instr.tex_b.UsesMiscMode(TextureMiscMode::DC);
  63. const auto process_mode = instr.tex_b.GetTextureProcessMode();
  64. WriteTexInstructionFloat(bb, instr,
  65. GetTexCode(instr, texture_type, process_mode, depth_compare,
  66. is_array, is_aoffi, {instr.gpr20}));
  67. break;
  68. }
  69. case OpCode::Id::TEXS: {
  70. const TextureType texture_type{instr.texs.GetTextureType()};
  71. const bool is_array{instr.texs.IsArrayTexture()};
  72. const bool depth_compare = instr.texs.UsesMiscMode(TextureMiscMode::DC);
  73. const auto process_mode = instr.texs.GetTextureProcessMode();
  74. if (instr.texs.UsesMiscMode(TextureMiscMode::NODEP)) {
  75. LOG_WARNING(HW_GPU, "TEXS.NODEP implementation is incomplete");
  76. }
  77. const Node4 components =
  78. GetTexsCode(instr, texture_type, process_mode, depth_compare, is_array);
  79. if (instr.texs.fp32_flag) {
  80. WriteTexsInstructionFloat(bb, instr, components);
  81. } else {
  82. WriteTexsInstructionHalfFloat(bb, instr, components);
  83. }
  84. break;
  85. }
  86. case OpCode::Id::TLD4: {
  87. ASSERT(instr.tld4.array == 0);
  88. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV),
  89. "NDV is not implemented");
  90. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP),
  91. "PTP is not implemented");
  92. if (instr.tld4.UsesMiscMode(TextureMiscMode::NODEP)) {
  93. LOG_WARNING(HW_GPU, "TLD4.NODEP implementation is incomplete");
  94. }
  95. const auto texture_type = instr.tld4.texture_type.Value();
  96. const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC);
  97. const bool is_array = instr.tld4.array != 0;
  98. const bool is_aoffi = instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI);
  99. WriteTexInstructionFloat(
  100. bb, instr, GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi));
  101. break;
  102. }
  103. case OpCode::Id::TLD4S: {
  104. UNIMPLEMENTED_IF_MSG(instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI),
  105. "AOFFI is not implemented");
  106. if (instr.tld4s.UsesMiscMode(TextureMiscMode::NODEP)) {
  107. LOG_WARNING(HW_GPU, "TLD4S.NODEP implementation is incomplete");
  108. }
  109. const bool depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
  110. const Node op_a = GetRegister(instr.gpr8);
  111. const Node op_b = GetRegister(instr.gpr20);
  112. // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
  113. std::vector<Node> coords;
  114. if (depth_compare) {
  115. // Note: TLD4S coordinate encoding works just like TEXS's
  116. const Node op_y = GetRegister(instr.gpr8.Value() + 1);
  117. coords.push_back(op_a);
  118. coords.push_back(op_y);
  119. coords.push_back(op_b);
  120. } else {
  121. coords.push_back(op_a);
  122. coords.push_back(op_b);
  123. }
  124. const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
  125. const auto& sampler =
  126. GetSampler(instr.sampler, TextureType::Texture2D, false, depth_compare);
  127. Node4 values;
  128. for (u32 element = 0; element < values.size(); ++element) {
  129. auto coords_copy = coords;
  130. MetaTexture meta{sampler, {}, {}, {}, {}, {}, component, element};
  131. values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
  132. }
  133. WriteTexsInstructionFloat(bb, instr, values);
  134. break;
  135. }
  136. case OpCode::Id::TXQ_B:
  137. is_bindless = true;
  138. [[fallthrough]];
  139. case OpCode::Id::TXQ: {
  140. if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) {
  141. LOG_WARNING(HW_GPU, "TXQ.NODEP implementation is incomplete");
  142. }
  143. // TODO: The new commits on the texture refactor, change the way samplers work.
  144. // Sadly, not all texture instructions specify the type of texture their sampler
  145. // uses. This must be fixed at a later instance.
  146. const auto& sampler =
  147. is_bindless
  148. ? GetBindlessSampler(instr.gpr8, Tegra::Shader::TextureType::Texture2D, false,
  149. false)
  150. : GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
  151. u32 indexer = 0;
  152. switch (instr.txq.query_type) {
  153. case Tegra::Shader::TextureQueryType::Dimension: {
  154. for (u32 element = 0; element < 4; ++element) {
  155. if (!instr.txq.IsComponentEnabled(element)) {
  156. continue;
  157. }
  158. MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
  159. const Node value =
  160. Operation(OperationCode::TextureQueryDimensions, meta,
  161. GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
  162. SetTemporal(bb, indexer++, value);
  163. }
  164. for (u32 i = 0; i < indexer; ++i) {
  165. SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
  166. }
  167. break;
  168. }
  169. default:
  170. UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
  171. static_cast<u32>(instr.txq.query_type.Value()));
  172. }
  173. break;
  174. }
  175. case OpCode::Id::TMML_B:
  176. is_bindless = true;
  177. [[fallthrough]];
  178. case OpCode::Id::TMML: {
  179. UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
  180. "NDV is not implemented");
  181. if (instr.tmml.UsesMiscMode(TextureMiscMode::NODEP)) {
  182. LOG_WARNING(HW_GPU, "TMML.NODEP implementation is incomplete");
  183. }
  184. auto texture_type = instr.tmml.texture_type.Value();
  185. const bool is_array = instr.tmml.array != 0;
  186. const auto& sampler = is_bindless
  187. ? GetBindlessSampler(instr.gpr20, texture_type, is_array, false)
  188. : GetSampler(instr.sampler, texture_type, is_array, false);
  189. std::vector<Node> coords;
  190. // TODO: Add coordinates for different samplers once other texture types are implemented.
  191. switch (texture_type) {
  192. case TextureType::Texture1D:
  193. coords.push_back(GetRegister(instr.gpr8));
  194. break;
  195. case TextureType::Texture2D:
  196. coords.push_back(GetRegister(instr.gpr8.Value() + 0));
  197. coords.push_back(GetRegister(instr.gpr8.Value() + 1));
  198. break;
  199. default:
  200. UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
  201. // Fallback to interpreting as a 2D texture for now
  202. coords.push_back(GetRegister(instr.gpr8.Value() + 0));
  203. coords.push_back(GetRegister(instr.gpr8.Value() + 1));
  204. texture_type = TextureType::Texture2D;
  205. }
  206. u32 indexer = 0;
  207. for (u32 element = 0; element < 2; ++element) {
  208. if (!instr.tmml.IsComponentEnabled(element)) {
  209. continue;
  210. }
  211. auto params = coords;
  212. MetaTexture meta{sampler, {}, {}, {}, {}, {}, {}, element};
  213. const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
  214. SetTemporal(bb, indexer++, value);
  215. }
  216. for (u32 i = 0; i < indexer; ++i) {
  217. SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
  218. }
  219. break;
  220. }
  221. case OpCode::Id::TLDS: {
  222. const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
  223. const bool is_array{instr.tlds.IsArrayTexture()};
  224. UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI),
  225. "AOFFI is not implemented");
  226. UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
  227. if (instr.tlds.UsesMiscMode(TextureMiscMode::NODEP)) {
  228. LOG_WARNING(HW_GPU, "TLDS.NODEP implementation is incomplete");
  229. }
  230. WriteTexsInstructionFloat(bb, instr, GetTldsCode(instr, texture_type, is_array));
  231. break;
  232. }
  233. default:
  234. UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
  235. }
  236. return pc;
  237. }
  238. const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, TextureType type,
  239. bool is_array, bool is_shadow) {
  240. const auto offset = static_cast<std::size_t>(sampler.index.Value());
  241. // If this sampler has already been used, return the existing mapping.
  242. const auto itr =
  243. std::find_if(used_samplers.begin(), used_samplers.end(),
  244. [&](const Sampler& entry) { return entry.GetOffset() == offset; });
  245. if (itr != used_samplers.end()) {
  246. ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
  247. itr->IsShadow() == is_shadow);
  248. return *itr;
  249. }
  250. // Otherwise create a new mapping for this sampler
  251. const std::size_t next_index = used_samplers.size();
  252. const Sampler entry{offset, next_index, type, is_array, is_shadow};
  253. return *used_samplers.emplace(entry).first;
  254. }
  255. const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, TextureType type,
  256. bool is_array, bool is_shadow) {
  257. const Node sampler_register = GetRegister(reg);
  258. const Node base_sampler =
  259. TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
  260. const auto cbuf = std::get_if<CbufNode>(base_sampler);
  261. const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
  262. ASSERT(cbuf_offset_imm != nullptr);
  263. const auto cbuf_offset = cbuf_offset_imm->GetValue();
  264. const auto cbuf_index = cbuf->GetIndex();
  265. const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset);
  266. // If this sampler has already been used, return the existing mapping.
  267. const auto itr =
  268. std::find_if(used_samplers.begin(), used_samplers.end(),
  269. [&](const Sampler& entry) { return entry.GetOffset() == cbuf_key; });
  270. if (itr != used_samplers.end()) {
  271. ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
  272. itr->IsShadow() == is_shadow);
  273. return *itr;
  274. }
  275. // Otherwise create a new mapping for this sampler
  276. const std::size_t next_index = used_samplers.size();
  277. const Sampler entry{cbuf_index, cbuf_offset, next_index, type, is_array, is_shadow};
  278. return *used_samplers.emplace(entry).first;
  279. }
  280. void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
  281. u32 dest_elem = 0;
  282. for (u32 elem = 0; elem < 4; ++elem) {
  283. if (!instr.tex.IsComponentEnabled(elem)) {
  284. // Skip disabled components
  285. continue;
  286. }
  287. SetTemporal(bb, dest_elem++, components[elem]);
  288. }
  289. // After writing values in temporals, move them to the real registers
  290. for (u32 i = 0; i < dest_elem; ++i) {
  291. SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
  292. }
  293. }
  294. void ShaderIR::WriteTexsInstructionFloat(NodeBlock& bb, Instruction instr,
  295. const Node4& components) {
  296. // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
  297. // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
  298. u32 dest_elem = 0;
  299. for (u32 component = 0; component < 4; ++component) {
  300. if (!instr.texs.IsComponentEnabled(component))
  301. continue;
  302. SetTemporal(bb, dest_elem++, components[component]);
  303. }
  304. for (u32 i = 0; i < dest_elem; ++i) {
  305. if (i < 2) {
  306. // Write the first two swizzle components to gpr0 and gpr0+1
  307. SetRegister(bb, instr.gpr0.Value() + i % 2, GetTemporal(i));
  308. } else {
  309. ASSERT(instr.texs.HasTwoDestinations());
  310. // Write the rest of the swizzle components to gpr28 and gpr28+1
  311. SetRegister(bb, instr.gpr28.Value() + i % 2, GetTemporal(i));
  312. }
  313. }
  314. }
  315. void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr,
  316. const Node4& components) {
  317. // TEXS.F16 destionation registers are packed in two registers in pairs (just like any half
  318. // float instruction).
  319. Node4 values;
  320. u32 dest_elem = 0;
  321. for (u32 component = 0; component < 4; ++component) {
  322. if (!instr.texs.IsComponentEnabled(component))
  323. continue;
  324. values[dest_elem++] = components[component];
  325. }
  326. if (dest_elem == 0)
  327. return;
  328. std::generate(values.begin() + dest_elem, values.end(), [&]() { return Immediate(0); });
  329. const Node first_value = Operation(OperationCode::HPack2, values[0], values[1]);
  330. if (dest_elem <= 2) {
  331. SetRegister(bb, instr.gpr0, first_value);
  332. return;
  333. }
  334. SetTemporal(bb, 0, first_value);
  335. SetTemporal(bb, 1, Operation(OperationCode::HPack2, values[2], values[3]));
  336. SetRegister(bb, instr.gpr0, GetTemporal(0));
  337. SetRegister(bb, instr.gpr28, GetTemporal(1));
  338. }
  339. Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
  340. TextureProcessMode process_mode, std::vector<Node> coords,
  341. Node array, Node depth_compare, u32 bias_offset,
  342. std::vector<Node> aoffi,
  343. std::optional<Tegra::Shader::Register> bindless_reg) {
  344. const bool is_array = array;
  345. const bool is_shadow = depth_compare;
  346. const bool is_bindless = bindless_reg.has_value();
  347. UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
  348. (texture_type == TextureType::TextureCube && is_array && is_shadow),
  349. "This method is not supported.");
  350. const auto& sampler = is_bindless
  351. ? GetBindlessSampler(*bindless_reg, texture_type, is_array, is_shadow)
  352. : GetSampler(instr.sampler, texture_type, is_array, is_shadow);
  353. const bool lod_needed = process_mode == TextureProcessMode::LZ ||
  354. process_mode == TextureProcessMode::LL ||
  355. process_mode == TextureProcessMode::LLA;
  356. // LOD selection (either via bias or explicit textureLod) not
  357. // supported in GL for sampler2DArrayShadow and
  358. // samplerCubeArrayShadow.
  359. const bool gl_lod_supported =
  360. !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
  361. (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
  362. const OperationCode read_method =
  363. (lod_needed && gl_lod_supported) ? OperationCode::TextureLod : OperationCode::Texture;
  364. UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
  365. Node bias = {};
  366. Node lod = {};
  367. if (process_mode != TextureProcessMode::None && gl_lod_supported) {
  368. switch (process_mode) {
  369. case TextureProcessMode::LZ:
  370. lod = Immediate(0.0f);
  371. break;
  372. case TextureProcessMode::LB:
  373. // If present, lod or bias are always stored in the register
  374. // indexed by the gpr20 field with an offset depending on the
  375. // usage of the other registers
  376. bias = GetRegister(instr.gpr20.Value() + bias_offset);
  377. break;
  378. case TextureProcessMode::LL:
  379. lod = GetRegister(instr.gpr20.Value() + bias_offset);
  380. break;
  381. default:
  382. UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode));
  383. break;
  384. }
  385. }
  386. Node4 values;
  387. for (u32 element = 0; element < values.size(); ++element) {
  388. auto copy_coords = coords;
  389. MetaTexture meta{sampler, array, depth_compare, aoffi, bias, lod, {}, element};
  390. values[element] = Operation(read_method, meta, std::move(copy_coords));
  391. }
  392. return values;
  393. }
  394. Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
  395. TextureProcessMode process_mode, bool depth_compare, bool is_array,
  396. bool is_aoffi, std::optional<Tegra::Shader::Register> bindless_reg) {
  397. const bool lod_bias_enabled{
  398. (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)};
  399. const bool is_bindless = bindless_reg.has_value();
  400. u64 parameter_register = instr.gpr20.Value();
  401. if (is_bindless) {
  402. ++parameter_register;
  403. }
  404. const u32 bias_lod_offset = (is_bindless ? 1 : 0);
  405. if (lod_bias_enabled) {
  406. ++parameter_register;
  407. }
  408. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  409. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
  410. // If enabled arrays index is always stored in the gpr8 field
  411. const u64 array_register = instr.gpr8.Value();
  412. // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
  413. const u64 coord_register = array_register + (is_array ? 1 : 0);
  414. std::vector<Node> coords;
  415. for (std::size_t i = 0; i < coord_count; ++i) {
  416. coords.push_back(GetRegister(coord_register + i));
  417. }
  418. // 1D.DC in OpenGL the 2nd component is ignored.
  419. if (depth_compare && !is_array && texture_type == TextureType::Texture1D) {
  420. coords.push_back(Immediate(0.0f));
  421. }
  422. const Node array = is_array ? GetRegister(array_register) : nullptr;
  423. std::vector<Node> aoffi;
  424. if (is_aoffi) {
  425. aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false);
  426. }
  427. Node dc{};
  428. if (depth_compare) {
  429. // Depth is always stored in the register signaled by gpr20 or in the next register if lod
  430. // or bias are used
  431. dc = GetRegister(parameter_register++);
  432. }
  433. return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_lod_offset,
  434. aoffi, bindless_reg);
  435. }
  436. Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
  437. TextureProcessMode process_mode, bool depth_compare, bool is_array) {
  438. const bool lod_bias_enabled =
  439. (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ);
  440. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  441. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4);
  442. // If enabled arrays index is always stored in the gpr8 field
  443. const u64 array_register = instr.gpr8.Value();
  444. // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
  445. const u64 coord_register = array_register + (is_array ? 1 : 0);
  446. const u64 last_coord_register =
  447. (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
  448. ? static_cast<u64>(instr.gpr20.Value())
  449. : coord_register + 1;
  450. const u32 bias_offset = coord_count > 2 ? 1 : 0;
  451. std::vector<Node> coords;
  452. for (std::size_t i = 0; i < coord_count; ++i) {
  453. const bool last = (i == (coord_count - 1)) && (coord_count > 1);
  454. coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
  455. }
  456. const Node array = is_array ? GetRegister(array_register) : nullptr;
  457. Node dc{};
  458. if (depth_compare) {
  459. // Depth is always stored in the register signaled by gpr20 or in the next register if lod
  460. // or bias are used
  461. const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
  462. dc = GetRegister(depth_register);
  463. }
  464. return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {},
  465. {});
  466. }
  467. Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
  468. bool is_array, bool is_aoffi) {
  469. const std::size_t coord_count = GetCoordCount(texture_type);
  470. // If enabled arrays index is always stored in the gpr8 field
  471. const u64 array_register = instr.gpr8.Value();
  472. // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
  473. const u64 coord_register = array_register + (is_array ? 1 : 0);
  474. std::vector<Node> coords;
  475. for (std::size_t i = 0; i < coord_count; ++i) {
  476. coords.push_back(GetRegister(coord_register + i));
  477. }
  478. u64 parameter_register = instr.gpr20.Value();
  479. std::vector<Node> aoffi;
  480. if (is_aoffi) {
  481. aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true);
  482. }
  483. Node dc{};
  484. if (depth_compare) {
  485. dc = GetRegister(parameter_register++);
  486. }
  487. const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare);
  488. Node4 values;
  489. for (u32 element = 0; element < values.size(); ++element) {
  490. auto coords_copy = coords;
  491. MetaTexture meta{sampler, GetRegister(array_register), dc, aoffi, {}, {}, {}, element};
  492. values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
  493. }
  494. return values;
  495. }
  496. Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is_array) {
  497. const std::size_t type_coord_count = GetCoordCount(texture_type);
  498. const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
  499. // If enabled arrays index is always stored in the gpr8 field
  500. const u64 array_register = instr.gpr8.Value();
  501. // if is array gpr20 is used
  502. const u64 coord_register = is_array ? instr.gpr20.Value() : instr.gpr8.Value();
  503. const u64 last_coord_register =
  504. ((type_coord_count > 2) || (type_coord_count == 2 && !lod_enabled)) && !is_array
  505. ? static_cast<u64>(instr.gpr20.Value())
  506. : coord_register + 1;
  507. std::vector<Node> coords;
  508. for (std::size_t i = 0; i < type_coord_count; ++i) {
  509. const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
  510. coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
  511. }
  512. const Node array = is_array ? GetRegister(array_register) : nullptr;
  513. // When lod is used always is in gpr20
  514. const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
  515. const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, false);
  516. Node4 values;
  517. for (u32 element = 0; element < values.size(); ++element) {
  518. auto coords_copy = coords;
  519. MetaTexture meta{sampler, array, {}, {}, {}, lod, {}, element};
  520. values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
  521. }
  522. return values;
  523. }
  524. std::tuple<std::size_t, std::size_t> ShaderIR::ValidateAndGetCoordinateElement(
  525. TextureType texture_type, bool depth_compare, bool is_array, bool lod_bias_enabled,
  526. std::size_t max_coords, std::size_t max_inputs) {
  527. const std::size_t coord_count = GetCoordCount(texture_type);
  528. std::size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0);
  529. const std::size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0);
  530. if (total_coord_count > max_coords || total_reg_count > max_inputs) {
  531. UNIMPLEMENTED_MSG("Unsupported Texture operation");
  532. total_coord_count = std::min(total_coord_count, max_coords);
  533. }
  534. // 1D.DC OpenGL is using a vec3 but 2nd component is ignored later.
  535. total_coord_count +=
  536. (depth_compare && !is_array && texture_type == TextureType::Texture1D) ? 1 : 0;
  537. return {coord_count, total_coord_count};
  538. }
  539. std::vector<Node> ShaderIR::GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count,
  540. bool is_tld4) {
  541. const auto [coord_offsets, size, wrap_value,
  542. diff_value] = [is_tld4]() -> std::tuple<std::array<u32, 3>, u32, s32, s32> {
  543. if (is_tld4) {
  544. return {{0, 8, 16}, 6, 32, 64};
  545. } else {
  546. return {{0, 4, 8}, 4, 8, 16};
  547. }
  548. }();
  549. const u32 mask = (1U << size) - 1;
  550. std::vector<Node> aoffi;
  551. aoffi.reserve(coord_count);
  552. const auto aoffi_immediate{
  553. TrackImmediate(aoffi_reg, global_code, static_cast<s64>(global_code.size()))};
  554. if (!aoffi_immediate) {
  555. // Variable access, not supported on AMD.
  556. LOG_WARNING(HW_GPU,
  557. "AOFFI constant folding failed, some hardware might have graphical issues");
  558. for (std::size_t coord = 0; coord < coord_count; ++coord) {
  559. const Node value = BitfieldExtract(aoffi_reg, coord_offsets.at(coord), size);
  560. const Node condition =
  561. Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(wrap_value));
  562. const Node negative = Operation(OperationCode::IAdd, value, Immediate(-diff_value));
  563. aoffi.push_back(Operation(OperationCode::Select, condition, negative, value));
  564. }
  565. return aoffi;
  566. }
  567. for (std::size_t coord = 0; coord < coord_count; ++coord) {
  568. s32 value = (*aoffi_immediate >> coord_offsets.at(coord)) & mask;
  569. if (value >= wrap_value) {
  570. value -= diff_value;
  571. }
  572. aoffi.push_back(Immediate(value));
  573. }
  574. return aoffi;
  575. }
  576. } // namespace VideoCommon::Shader