memory.cpp 29 KB

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