memory.cpp 30 KB

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