texture.cpp 30 KB

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