texture.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  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. const TextureType texture_type{instr.tex.texture_type};
  42. const bool is_array = instr.tex.array != 0;
  43. const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
  44. const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC);
  45. const auto process_mode = instr.tex.GetTextureProcessMode();
  46. WriteTexInstructionFloat(
  47. bb, instr,
  48. GetTexCode(instr, texture_type, process_mode, depth_compare, is_array, is_aoffi, {}));
  49. break;
  50. }
  51. case OpCode::Id::TEX_B: {
  52. UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
  53. "AOFFI is not implemented");
  54. const TextureType texture_type{instr.tex_b.texture_type};
  55. const bool is_array = instr.tex_b.array != 0;
  56. const bool is_aoffi = instr.tex.UsesMiscMode(TextureMiscMode::AOFFI);
  57. const bool depth_compare = instr.tex_b.UsesMiscMode(TextureMiscMode::DC);
  58. const auto process_mode = instr.tex_b.GetTextureProcessMode();
  59. WriteTexInstructionFloat(bb, instr,
  60. GetTexCode(instr, texture_type, process_mode, depth_compare,
  61. is_array, is_aoffi, {instr.gpr20}));
  62. break;
  63. }
  64. case OpCode::Id::TEXS: {
  65. const TextureType texture_type{instr.texs.GetTextureType()};
  66. const bool is_array{instr.texs.IsArrayTexture()};
  67. const bool depth_compare = instr.texs.UsesMiscMode(TextureMiscMode::DC);
  68. const auto process_mode = instr.texs.GetTextureProcessMode();
  69. const Node4 components =
  70. GetTexsCode(instr, texture_type, process_mode, depth_compare, is_array);
  71. if (instr.texs.fp32_flag) {
  72. WriteTexsInstructionFloat(bb, instr, components);
  73. } else {
  74. WriteTexsInstructionHalfFloat(bb, instr, components);
  75. }
  76. break;
  77. }
  78. case OpCode::Id::TLD4_B: {
  79. is_bindless = true;
  80. [[fallthrough]];
  81. }
  82. case OpCode::Id::TLD4: {
  83. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV),
  84. "NDV is not implemented");
  85. const auto texture_type = instr.tld4.texture_type.Value();
  86. const bool depth_compare = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::DC)
  87. : instr.tld4.UsesMiscMode(TextureMiscMode::DC);
  88. const bool is_array = instr.tld4.array != 0;
  89. const bool is_aoffi = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::AOFFI)
  90. : instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI);
  91. const bool is_ptp = is_bindless ? instr.tld4_b.UsesMiscMode(TextureMiscMode::PTP)
  92. : instr.tld4.UsesMiscMode(TextureMiscMode::PTP);
  93. WriteTexInstructionFloat(bb, instr,
  94. GetTld4Code(instr, texture_type, depth_compare, is_array, is_aoffi,
  95. is_ptp, is_bindless));
  96. break;
  97. }
  98. case OpCode::Id::TLD4S: {
  99. constexpr std::size_t num_coords = 2;
  100. const bool is_aoffi = instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI);
  101. const bool is_depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
  102. const Node op_a = GetRegister(instr.gpr8);
  103. const Node op_b = GetRegister(instr.gpr20);
  104. // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
  105. std::vector<Node> coords;
  106. std::vector<Node> aoffi;
  107. Node depth_compare;
  108. if (is_depth_compare) {
  109. // Note: TLD4S coordinate encoding works just like TEXS's
  110. const Node op_y = GetRegister(instr.gpr8.Value() + 1);
  111. coords.push_back(op_a);
  112. coords.push_back(op_y);
  113. if (is_aoffi) {
  114. aoffi = GetAoffiCoordinates(op_b, num_coords, true);
  115. depth_compare = GetRegister(instr.gpr20.Value() + 1);
  116. } else {
  117. depth_compare = op_b;
  118. }
  119. } else {
  120. // There's no depth compare
  121. coords.push_back(op_a);
  122. if (is_aoffi) {
  123. coords.push_back(GetRegister(instr.gpr8.Value() + 1));
  124. aoffi = GetAoffiCoordinates(op_b, num_coords, true);
  125. } else {
  126. coords.push_back(op_b);
  127. }
  128. }
  129. const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
  130. const SamplerInfo info{TextureType::Texture2D, false, is_depth_compare};
  131. const Sampler& sampler = *GetSampler(instr.sampler, info);
  132. Node4 values;
  133. for (u32 element = 0; element < values.size(); ++element) {
  134. auto coords_copy = coords;
  135. MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element};
  136. values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
  137. }
  138. if (instr.tld4s.fp16_flag) {
  139. WriteTexsInstructionHalfFloat(bb, instr, values, true);
  140. } else {
  141. WriteTexsInstructionFloat(bb, instr, values, true);
  142. }
  143. break;
  144. }
  145. case OpCode::Id::TXD_B:
  146. is_bindless = true;
  147. [[fallthrough]];
  148. case OpCode::Id::TXD: {
  149. UNIMPLEMENTED_IF_MSG(instr.txd.UsesMiscMode(TextureMiscMode::AOFFI),
  150. "AOFFI is not implemented");
  151. const bool is_array = instr.txd.is_array != 0;
  152. u64 base_reg = instr.gpr8.Value();
  153. const auto derivate_reg = instr.gpr20.Value();
  154. const auto texture_type = instr.txd.texture_type.Value();
  155. const auto coord_count = GetCoordCount(texture_type);
  156. const Sampler* sampler =
  157. is_bindless ? GetBindlessSampler(base_reg, {{texture_type, is_array, false}})
  158. : GetSampler(instr.sampler, {{texture_type, is_array, false}});
  159. Node4 values;
  160. if (sampler == nullptr) {
  161. for (u32 element = 0; element < values.size(); ++element) {
  162. values[element] = Immediate(0);
  163. }
  164. WriteTexInstructionFloat(bb, instr, values);
  165. break;
  166. }
  167. if (is_bindless) {
  168. base_reg++;
  169. }
  170. std::vector<Node> coords;
  171. std::vector<Node> derivates;
  172. for (std::size_t i = 0; i < coord_count; ++i) {
  173. coords.push_back(GetRegister(base_reg + i));
  174. const std::size_t derivate = i * 2;
  175. derivates.push_back(GetRegister(derivate_reg + derivate));
  176. derivates.push_back(GetRegister(derivate_reg + derivate + 1));
  177. }
  178. Node array_node = {};
  179. if (is_array) {
  180. const Node info_reg = GetRegister(base_reg + coord_count);
  181. array_node = BitfieldExtract(info_reg, 0, 16);
  182. }
  183. for (u32 element = 0; element < values.size(); ++element) {
  184. MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, {}, {}, {}, element};
  185. values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords);
  186. }
  187. WriteTexInstructionFloat(bb, instr, values);
  188. break;
  189. }
  190. case OpCode::Id::TXQ_B:
  191. is_bindless = true;
  192. [[fallthrough]];
  193. case OpCode::Id::TXQ: {
  194. // TODO: The new commits on the texture refactor, change the way samplers work.
  195. // Sadly, not all texture instructions specify the type of texture their sampler
  196. // uses. This must be fixed at a later instance.
  197. const Sampler* sampler =
  198. is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler);
  199. if (sampler == nullptr) {
  200. u32 indexer = 0;
  201. for (u32 element = 0; element < 4; ++element) {
  202. if (!instr.txq.IsComponentEnabled(element)) {
  203. continue;
  204. }
  205. const Node value = Immediate(0);
  206. SetTemporary(bb, indexer++, value);
  207. }
  208. for (u32 i = 0; i < indexer; ++i) {
  209. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  210. }
  211. break;
  212. }
  213. u32 indexer = 0;
  214. switch (instr.txq.query_type) {
  215. case Tegra::Shader::TextureQueryType::Dimension: {
  216. for (u32 element = 0; element < 4; ++element) {
  217. if (!instr.txq.IsComponentEnabled(element)) {
  218. continue;
  219. }
  220. MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
  221. const Node value =
  222. Operation(OperationCode::TextureQueryDimensions, meta,
  223. GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
  224. SetTemporary(bb, indexer++, value);
  225. }
  226. for (u32 i = 0; i < indexer; ++i) {
  227. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  228. }
  229. break;
  230. }
  231. default:
  232. UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
  233. static_cast<u32>(instr.txq.query_type.Value()));
  234. }
  235. break;
  236. }
  237. case OpCode::Id::TMML_B:
  238. is_bindless = true;
  239. [[fallthrough]];
  240. case OpCode::Id::TMML: {
  241. UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
  242. "NDV is not implemented");
  243. auto texture_type = instr.tmml.texture_type.Value();
  244. const bool is_array = instr.tmml.array != 0;
  245. const Sampler* sampler =
  246. is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler);
  247. if (sampler == nullptr) {
  248. u32 indexer = 0;
  249. for (u32 element = 0; element < 2; ++element) {
  250. if (!instr.tmml.IsComponentEnabled(element)) {
  251. continue;
  252. }
  253. const Node value = Immediate(0);
  254. SetTemporary(bb, indexer++, value);
  255. }
  256. for (u32 i = 0; i < indexer; ++i) {
  257. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  258. }
  259. break;
  260. }
  261. std::vector<Node> coords;
  262. // TODO: Add coordinates for different samplers once other texture types are implemented.
  263. switch (texture_type) {
  264. case TextureType::Texture1D:
  265. coords.push_back(GetRegister(instr.gpr8));
  266. break;
  267. case TextureType::Texture2D:
  268. coords.push_back(GetRegister(instr.gpr8.Value() + 0));
  269. coords.push_back(GetRegister(instr.gpr8.Value() + 1));
  270. break;
  271. default:
  272. UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
  273. // Fallback to interpreting as a 2D texture for now
  274. coords.push_back(GetRegister(instr.gpr8.Value() + 0));
  275. coords.push_back(GetRegister(instr.gpr8.Value() + 1));
  276. texture_type = TextureType::Texture2D;
  277. }
  278. u32 indexer = 0;
  279. for (u32 element = 0; element < 2; ++element) {
  280. if (!instr.tmml.IsComponentEnabled(element)) {
  281. continue;
  282. }
  283. auto params = coords;
  284. MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
  285. const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
  286. SetTemporary(bb, indexer++, value);
  287. }
  288. for (u32 i = 0; i < indexer; ++i) {
  289. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  290. }
  291. break;
  292. }
  293. case OpCode::Id::TLD: {
  294. UNIMPLEMENTED_IF_MSG(instr.tld.aoffi, "AOFFI is not implemented");
  295. UNIMPLEMENTED_IF_MSG(instr.tld.ms, "MS is not implemented");
  296. UNIMPLEMENTED_IF_MSG(instr.tld.cl, "CL is not implemented");
  297. WriteTexInstructionFloat(bb, instr, GetTldCode(instr));
  298. break;
  299. }
  300. case OpCode::Id::TLDS: {
  301. const TextureType texture_type{instr.tlds.GetTextureType()};
  302. const bool is_array{instr.tlds.IsArrayTexture()};
  303. UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI),
  304. "AOFFI is not implemented");
  305. UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
  306. const Node4 components = GetTldsCode(instr, texture_type, is_array);
  307. if (instr.tlds.fp32_flag) {
  308. WriteTexsInstructionFloat(bb, instr, components);
  309. } else {
  310. WriteTexsInstructionHalfFloat(bb, instr, components);
  311. }
  312. break;
  313. }
  314. default:
  315. UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
  316. }
  317. return pc;
  318. }
  319. ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
  320. std::optional<u32> buffer) {
  321. if (sampler_info) {
  322. return *sampler_info;
  323. }
  324. const auto sampler =
  325. buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
  326. if (!sampler) {
  327. LOG_WARNING(HW_GPU, "Unknown sampler info");
  328. return SamplerInfo{TextureType::Texture2D, false, false, false};
  329. }
  330. return SamplerInfo{sampler->texture_type, sampler->is_array != 0, sampler->is_shadow != 0,
  331. sampler->is_buffer != 0};
  332. }
  333. const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
  334. std::optional<SamplerInfo> sampler_info) {
  335. const auto offset = static_cast<u32>(sampler.index.Value());
  336. const auto info = GetSamplerInfo(sampler_info, offset);
  337. // If this sampler has already been used, return the existing mapping.
  338. const auto it =
  339. std::find_if(used_samplers.begin(), used_samplers.end(),
  340. [offset](const Sampler& entry) { return entry.GetOffset() == offset; });
  341. if (it != used_samplers.end()) {
  342. ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
  343. it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer);
  344. return &*it;
  345. }
  346. // Otherwise create a new mapping for this sampler
  347. const auto next_index = static_cast<u32>(used_samplers.size());
  348. return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow,
  349. info.is_buffer, false);
  350. }
  351. const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
  352. std::optional<SamplerInfo> sampler_info) {
  353. const Node sampler_register = GetRegister(reg);
  354. const auto [base_node, tracked_sampler_info] =
  355. TrackSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
  356. ASSERT(base_node != nullptr);
  357. if (base_node == nullptr) {
  358. return nullptr;
  359. }
  360. if (const auto bindless_sampler_info =
  361. std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) {
  362. const u32 buffer = bindless_sampler_info->GetIndex();
  363. const u32 offset = bindless_sampler_info->GetOffset();
  364. const auto info = GetSamplerInfo(sampler_info, offset, buffer);
  365. // If this sampler has already been used, return the existing mapping.
  366. const auto it =
  367. std::find_if(used_samplers.begin(), used_samplers.end(),
  368. [buffer = buffer, offset = offset](const Sampler& entry) {
  369. return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
  370. });
  371. if (it != used_samplers.end()) {
  372. ASSERT(it->IsBindless() && it->GetType() == info.type &&
  373. it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow);
  374. return &*it;
  375. }
  376. // Otherwise create a new mapping for this sampler
  377. const auto next_index = static_cast<u32>(used_samplers.size());
  378. return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
  379. info.is_shadow, info.is_buffer, false);
  380. } else if (const auto array_sampler_info =
  381. std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) {
  382. const u32 base_offset = array_sampler_info->GetBaseOffset() / 4;
  383. const auto info = GetSamplerInfo(sampler_info, base_offset);
  384. // If this sampler has already been used, return the existing mapping.
  385. const auto it = std::find_if(
  386. used_samplers.begin(), used_samplers.end(),
  387. [base_offset](const Sampler& entry) { return entry.GetOffset() == base_offset; });
  388. if (it != used_samplers.end()) {
  389. ASSERT(!it->IsBindless() && it->GetType() == info.type &&
  390. it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow &&
  391. it->IsBuffer() == info.is_buffer && it->IsIndexed());
  392. return &*it;
  393. }
  394. uses_indexed_samplers = true;
  395. // Otherwise create a new mapping for this sampler
  396. const auto next_index = static_cast<u32>(used_samplers.size());
  397. return &used_samplers.emplace_back(next_index, base_offset, info.type, info.is_array,
  398. info.is_shadow, info.is_buffer, true);
  399. }
  400. return nullptr;
  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. SetTemporary(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, GetTemporary(i));
  414. }
  415. }
  416. void ShaderIR::WriteTexsInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components,
  417. bool ignore_mask) {
  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) && !ignore_mask)
  423. continue;
  424. SetTemporary(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, GetTemporary(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, GetTemporary(i));
  434. }
  435. }
  436. }
  437. void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr,
  438. const Node4& components, bool ignore_mask) {
  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) && !ignore_mask)
  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. SetTemporary(bb, 0, first_value);
  457. SetTemporary(bb, 1, Operation(OperationCode::HPack2, values[2], values[3]));
  458. SetRegister(bb, instr.gpr0, GetTemporary(0));
  459. SetRegister(bb, instr.gpr28, GetTemporary(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. std::vector<Node> aoffi,
  465. std::optional<Tegra::Shader::Register> bindless_reg) {
  466. const auto is_array = static_cast<bool>(array);
  467. const auto is_shadow = static_cast<bool>(depth_compare);
  468. const bool is_bindless = bindless_reg.has_value();
  469. UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
  470. (texture_type == TextureType::TextureCube && is_array && is_shadow),
  471. "This method is not supported.");
  472. const SamplerInfo info{texture_type, is_array, is_shadow, false};
  473. const Sampler* sampler =
  474. is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info);
  475. Node4 values;
  476. if (sampler == nullptr) {
  477. for (u32 element = 0; element < values.size(); ++element) {
  478. values[element] = Immediate(0);
  479. }
  480. return values;
  481. }
  482. const bool lod_needed = process_mode == TextureProcessMode::LZ ||
  483. process_mode == TextureProcessMode::LL ||
  484. process_mode == TextureProcessMode::LLA;
  485. // LOD selection (either via bias or explicit textureLod) not supported in GL for
  486. // sampler2DArrayShadow and samplerCubeArrayShadow.
  487. const bool gl_lod_supported =
  488. !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
  489. (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
  490. const OperationCode read_method =
  491. (lod_needed && gl_lod_supported) ? OperationCode::TextureLod : OperationCode::Texture;
  492. UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
  493. Node bias;
  494. Node lod;
  495. if (process_mode != TextureProcessMode::None && gl_lod_supported) {
  496. switch (process_mode) {
  497. case TextureProcessMode::LZ:
  498. lod = Immediate(0.0f);
  499. break;
  500. case TextureProcessMode::LB:
  501. // If present, lod or bias are always stored in the register
  502. // indexed by the gpr20 field with an offset depending on the
  503. // usage of the other registers
  504. bias = GetRegister(instr.gpr20.Value() + bias_offset);
  505. break;
  506. case TextureProcessMode::LL:
  507. lod = GetRegister(instr.gpr20.Value() + bias_offset);
  508. break;
  509. default:
  510. UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode));
  511. break;
  512. }
  513. }
  514. for (u32 element = 0; element < values.size(); ++element) {
  515. auto copy_coords = coords;
  516. MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element};
  517. values[element] = Operation(read_method, meta, std::move(copy_coords));
  518. }
  519. return values;
  520. }
  521. Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
  522. TextureProcessMode process_mode, bool depth_compare, bool is_array,
  523. bool is_aoffi, std::optional<Tegra::Shader::Register> bindless_reg) {
  524. const bool lod_bias_enabled{
  525. (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ)};
  526. const bool is_bindless = bindless_reg.has_value();
  527. u64 parameter_register = instr.gpr20.Value();
  528. if (is_bindless) {
  529. ++parameter_register;
  530. }
  531. const u32 bias_lod_offset = (is_bindless ? 1 : 0);
  532. if (lod_bias_enabled) {
  533. ++parameter_register;
  534. }
  535. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  536. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
  537. // If enabled arrays index is always stored in the gpr8 field
  538. const u64 array_register = instr.gpr8.Value();
  539. // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
  540. const u64 coord_register = array_register + (is_array ? 1 : 0);
  541. std::vector<Node> coords;
  542. for (std::size_t i = 0; i < coord_count; ++i) {
  543. coords.push_back(GetRegister(coord_register + i));
  544. }
  545. // 1D.DC in OpenGL the 2nd component is ignored.
  546. if (depth_compare && !is_array && texture_type == TextureType::Texture1D) {
  547. coords.push_back(Immediate(0.0f));
  548. }
  549. const Node array = is_array ? GetRegister(array_register) : nullptr;
  550. std::vector<Node> aoffi;
  551. if (is_aoffi) {
  552. aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false);
  553. }
  554. Node dc{};
  555. if (depth_compare) {
  556. // Depth is always stored in the register signaled by gpr20 or in the next register if lod
  557. // or bias are used
  558. dc = GetRegister(parameter_register++);
  559. }
  560. return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_lod_offset,
  561. aoffi, bindless_reg);
  562. }
  563. Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
  564. TextureProcessMode process_mode, bool depth_compare, bool is_array) {
  565. const bool lod_bias_enabled =
  566. (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ);
  567. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  568. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4);
  569. // If enabled arrays index is always stored in the gpr8 field
  570. const u64 array_register = instr.gpr8.Value();
  571. // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
  572. const u64 coord_register = array_register + (is_array ? 1 : 0);
  573. const u64 last_coord_register =
  574. (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
  575. ? static_cast<u64>(instr.gpr20.Value())
  576. : coord_register + 1;
  577. const u32 bias_offset = coord_count > 2 ? 1 : 0;
  578. std::vector<Node> coords;
  579. for (std::size_t i = 0; i < coord_count; ++i) {
  580. const bool last = (i == (coord_count - 1)) && (coord_count > 1);
  581. coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
  582. }
  583. const Node array = is_array ? GetRegister(array_register) : nullptr;
  584. Node dc{};
  585. if (depth_compare) {
  586. // Depth is always stored in the register signaled by gpr20 or in the next register if lod
  587. // or bias are used
  588. const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
  589. dc = GetRegister(depth_register);
  590. }
  591. return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset, {},
  592. {});
  593. }
  594. Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
  595. bool is_array, bool is_aoffi, bool is_ptp, bool is_bindless) {
  596. ASSERT_MSG(!(is_aoffi && is_ptp), "AOFFI and PTP can't be enabled at the same time");
  597. const std::size_t coord_count = GetCoordCount(texture_type);
  598. // If enabled arrays index is always stored in the gpr8 field
  599. const u64 array_register = instr.gpr8.Value();
  600. // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
  601. const u64 coord_register = array_register + (is_array ? 1 : 0);
  602. std::vector<Node> coords;
  603. for (std::size_t i = 0; i < coord_count; ++i) {
  604. coords.push_back(GetRegister(coord_register + i));
  605. }
  606. u64 parameter_register = instr.gpr20.Value();
  607. const SamplerInfo info{texture_type, is_array, depth_compare, false};
  608. const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info)
  609. : GetSampler(instr.sampler, info);
  610. Node4 values;
  611. if (sampler == nullptr) {
  612. for (u32 element = 0; element < values.size(); ++element) {
  613. values[element] = Immediate(0);
  614. }
  615. return values;
  616. }
  617. std::vector<Node> aoffi, ptp;
  618. if (is_aoffi) {
  619. aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, true);
  620. } else if (is_ptp) {
  621. ptp = GetPtpCoordinates(
  622. {GetRegister(parameter_register++), GetRegister(parameter_register++)});
  623. }
  624. Node dc;
  625. if (depth_compare) {
  626. dc = GetRegister(parameter_register++);
  627. }
  628. const Node component = is_bindless ? Immediate(static_cast<u32>(instr.tld4_b.component))
  629. : Immediate(static_cast<u32>(instr.tld4.component));
  630. for (u32 element = 0; element < values.size(); ++element) {
  631. auto coords_copy = coords;
  632. MetaTexture meta{
  633. *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element};
  634. values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
  635. }
  636. return values;
  637. }
  638. Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
  639. const auto texture_type{instr.tld.texture_type};
  640. const bool is_array{instr.tld.is_array};
  641. const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL};
  642. const std::size_t coord_count{GetCoordCount(texture_type)};
  643. u64 gpr8_cursor{instr.gpr8.Value()};
  644. const Node array_register{is_array ? GetRegister(gpr8_cursor++) : nullptr};
  645. std::vector<Node> coords;
  646. coords.reserve(coord_count);
  647. for (std::size_t i = 0; i < coord_count; ++i) {
  648. coords.push_back(GetRegister(gpr8_cursor++));
  649. }
  650. u64 gpr20_cursor{instr.gpr20.Value()};
  651. // const Node bindless_register{is_bindless ? GetRegister(gpr20_cursor++) : nullptr};
  652. const Node lod{lod_enabled ? GetRegister(gpr20_cursor++) : Immediate(0u)};
  653. // const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
  654. // const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr};
  655. const auto& sampler = *GetSampler(instr.sampler);
  656. Node4 values;
  657. for (u32 element = 0; element < values.size(); ++element) {
  658. auto coords_copy = coords;
  659. MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element};
  660. values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
  661. }
  662. return values;
  663. }
  664. Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is_array) {
  665. const Sampler& sampler = *GetSampler(instr.sampler);
  666. const std::size_t type_coord_count = GetCoordCount(texture_type);
  667. const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
  668. // If enabled arrays index is always stored in the gpr8 field
  669. const u64 array_register = instr.gpr8.Value();
  670. // if is array gpr20 is used
  671. const u64 coord_register = is_array ? instr.gpr20.Value() : instr.gpr8.Value();
  672. const u64 last_coord_register =
  673. ((type_coord_count > 2) || (type_coord_count == 2 && !lod_enabled)) && !is_array
  674. ? static_cast<u64>(instr.gpr20.Value())
  675. : coord_register + 1;
  676. std::vector<Node> coords;
  677. for (std::size_t i = 0; i < type_coord_count; ++i) {
  678. const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
  679. coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
  680. }
  681. const Node array = is_array ? GetRegister(array_register) : nullptr;
  682. // When lod is used always is in gpr20
  683. const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
  684. // Fill empty entries from the guest sampler
  685. const std::size_t entry_coord_count = GetCoordCount(sampler.GetType());
  686. if (type_coord_count != entry_coord_count) {
  687. LOG_WARNING(HW_GPU, "Bound and built texture types mismatch");
  688. // When the size is higher we insert zeroes
  689. for (std::size_t i = type_coord_count; i < entry_coord_count; ++i) {
  690. coords.push_back(GetRegister(Register::ZeroIndex));
  691. }
  692. // Then we ensure the size matches the number of entries (dropping unused values)
  693. coords.resize(entry_coord_count);
  694. }
  695. Node4 values;
  696. for (u32 element = 0; element < values.size(); ++element) {
  697. auto coords_copy = coords;
  698. MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element};
  699. values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
  700. }
  701. return values;
  702. }
  703. std::tuple<std::size_t, std::size_t> ShaderIR::ValidateAndGetCoordinateElement(
  704. TextureType texture_type, bool depth_compare, bool is_array, bool lod_bias_enabled,
  705. std::size_t max_coords, std::size_t max_inputs) {
  706. const std::size_t coord_count = GetCoordCount(texture_type);
  707. std::size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0);
  708. const std::size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0);
  709. if (total_coord_count > max_coords || total_reg_count > max_inputs) {
  710. UNIMPLEMENTED_MSG("Unsupported Texture operation");
  711. total_coord_count = std::min(total_coord_count, max_coords);
  712. }
  713. // 1D.DC OpenGL is using a vec3 but 2nd component is ignored later.
  714. total_coord_count +=
  715. (depth_compare && !is_array && texture_type == TextureType::Texture1D) ? 1 : 0;
  716. return {coord_count, total_coord_count};
  717. }
  718. std::vector<Node> ShaderIR::GetAoffiCoordinates(Node aoffi_reg, std::size_t coord_count,
  719. bool is_tld4) {
  720. const std::array coord_offsets = is_tld4 ? std::array{0U, 8U, 16U} : std::array{0U, 4U, 8U};
  721. const u32 size = is_tld4 ? 6 : 4;
  722. const s32 wrap_value = is_tld4 ? 32 : 8;
  723. const s32 diff_value = is_tld4 ? 64 : 16;
  724. const u32 mask = (1U << size) - 1;
  725. std::vector<Node> aoffi;
  726. aoffi.reserve(coord_count);
  727. const auto aoffi_immediate{
  728. TrackImmediate(aoffi_reg, global_code, static_cast<s64>(global_code.size()))};
  729. if (!aoffi_immediate) {
  730. // Variable access, not supported on AMD.
  731. LOG_WARNING(HW_GPU,
  732. "AOFFI constant folding failed, some hardware might have graphical issues");
  733. for (std::size_t coord = 0; coord < coord_count; ++coord) {
  734. const Node value = BitfieldExtract(aoffi_reg, coord_offsets[coord], size);
  735. const Node condition =
  736. Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(wrap_value));
  737. const Node negative = Operation(OperationCode::IAdd, value, Immediate(-diff_value));
  738. aoffi.push_back(Operation(OperationCode::Select, condition, negative, value));
  739. }
  740. return aoffi;
  741. }
  742. for (std::size_t coord = 0; coord < coord_count; ++coord) {
  743. s32 value = (*aoffi_immediate >> coord_offsets[coord]) & mask;
  744. if (value >= wrap_value) {
  745. value -= diff_value;
  746. }
  747. aoffi.push_back(Immediate(value));
  748. }
  749. return aoffi;
  750. }
  751. std::vector<Node> ShaderIR::GetPtpCoordinates(std::array<Node, 2> ptp_regs) {
  752. static constexpr u32 num_entries = 8;
  753. std::vector<Node> ptp;
  754. ptp.reserve(num_entries);
  755. const auto global_size = static_cast<s64>(global_code.size());
  756. const std::optional low = TrackImmediate(ptp_regs[0], global_code, global_size);
  757. const std::optional high = TrackImmediate(ptp_regs[1], global_code, global_size);
  758. if (!low || !high) {
  759. for (u32 entry = 0; entry < num_entries; ++entry) {
  760. const u32 reg = entry / 4;
  761. const u32 offset = entry % 4;
  762. const Node value = BitfieldExtract(ptp_regs[reg], offset * 8, 6);
  763. const Node condition =
  764. Operation(OperationCode::LogicalIGreaterEqual, value, Immediate(32));
  765. const Node negative = Operation(OperationCode::IAdd, value, Immediate(-64));
  766. ptp.push_back(Operation(OperationCode::Select, condition, negative, value));
  767. }
  768. return ptp;
  769. }
  770. const u64 immediate = (static_cast<u64>(*high) << 32) | static_cast<u64>(*low);
  771. for (u32 entry = 0; entry < num_entries; ++entry) {
  772. s32 value = (immediate >> (entry * 8)) & 0b111111;
  773. if (value >= 32) {
  774. value -= 64;
  775. }
  776. ptp.push_back(Immediate(value));
  777. }
  778. return ptp;
  779. }
  780. } // namespace VideoCommon::Shader