texture.cpp 38 KB

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