memory.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. // Copyright 2018 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <vector>
  6. #include "common/assert.h"
  7. #include "common/common_types.h"
  8. #include "video_core/engines/shader_bytecode.h"
  9. #include "video_core/shader/shader_ir.h"
  10. namespace VideoCommon::Shader {
  11. using Tegra::Shader::Attribute;
  12. using Tegra::Shader::Instruction;
  13. using Tegra::Shader::OpCode;
  14. using Tegra::Shader::Register;
  15. using Tegra::Shader::TextureMiscMode;
  16. using Tegra::Shader::TextureProcessMode;
  17. using Tegra::Shader::TextureType;
  18. static std::size_t GetCoordCount(TextureType texture_type) {
  19. switch (texture_type) {
  20. case TextureType::Texture1D:
  21. return 1;
  22. case TextureType::Texture2D:
  23. return 2;
  24. case TextureType::Texture3D:
  25. case TextureType::TextureCube:
  26. return 3;
  27. default:
  28. UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type));
  29. return 0;
  30. }
  31. }
  32. u32 ShaderIR::DecodeMemory(BasicBlock& bb, u32 pc) {
  33. const Instruction instr = {program_code[pc]};
  34. const auto opcode = OpCode::Decode(instr);
  35. switch (opcode->get().GetId()) {
  36. case OpCode::Id::LD_A: {
  37. // Note: Shouldn't this be interp mode flat? As in no interpolation made.
  38. UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
  39. "Indirect attribute loads are not supported");
  40. UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
  41. "Unaligned attribute loads are not supported");
  42. Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
  43. Tegra::Shader::IpaSampleMode::Default};
  44. u64 next_element = instr.attribute.fmt20.element;
  45. auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
  46. const auto LoadNextElement = [&](u32 reg_offset) {
  47. const Node buffer = GetRegister(instr.gpr39);
  48. const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index),
  49. next_element, input_mode, buffer);
  50. SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
  51. // Load the next attribute element into the following register. If the element
  52. // to load goes beyond the vec4 size, load the first element of the next
  53. // attribute.
  54. next_element = (next_element + 1) % 4;
  55. next_index = next_index + (next_element == 0 ? 1 : 0);
  56. };
  57. const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
  58. for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
  59. LoadNextElement(reg_offset);
  60. }
  61. break;
  62. }
  63. case OpCode::Id::ST_A: {
  64. UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
  65. "Indirect attribute loads are not supported");
  66. UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
  67. "Unaligned attribute loads are not supported");
  68. u64 next_element = instr.attribute.fmt20.element;
  69. auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
  70. const auto StoreNextElement = [&](u32 reg_offset) {
  71. const auto dest = GetOutputAttribute(static_cast<Attribute::Index>(next_index),
  72. next_element, GetRegister(instr.gpr39));
  73. const auto src = GetRegister(instr.gpr0.Value() + reg_offset);
  74. bb.push_back(Operation(OperationCode::Assign, dest, src));
  75. // Load the next attribute element into the following register. If the element
  76. // to load goes beyond the vec4 size, load the first element of the next
  77. // attribute.
  78. next_element = (next_element + 1) % 4;
  79. next_index = next_index + (next_element == 0 ? 1 : 0);
  80. };
  81. const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
  82. for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
  83. StoreNextElement(reg_offset);
  84. }
  85. break;
  86. }
  87. case OpCode::Id::TEX: {
  88. Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
  89. const bool is_array = instr.tex.array != 0;
  90. const bool depth_compare = instr.tex.UsesMiscMode(TextureMiscMode::DC);
  91. const auto process_mode = instr.tex.GetTextureProcessMode();
  92. UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(TextureMiscMode::AOFFI),
  93. "AOFFI is not implemented");
  94. if (instr.tex.UsesMiscMode(TextureMiscMode::NODEP)) {
  95. LOG_WARNING(HW_GPU, "TEX.NODEP is not implemented");
  96. }
  97. const Node texture = GetTexCode(instr, texture_type, process_mode, depth_compare, is_array);
  98. if (depth_compare) {
  99. SetRegister(bb, instr.gpr0, texture);
  100. } else {
  101. MetaComponents meta;
  102. std::array<Node, 4> dest;
  103. std::size_t dest_elem = 0;
  104. for (std::size_t elem = 0; elem < 4; ++elem) {
  105. if (!instr.tex.IsComponentEnabled(elem)) {
  106. // Skip disabled components
  107. continue;
  108. }
  109. meta.components_map[dest_elem] = static_cast<u32>(elem);
  110. dest[dest_elem] = GetRegister(instr.gpr0.Value() + dest_elem);
  111. ++dest_elem;
  112. }
  113. std::generate(dest.begin() + dest_elem, dest.end(), [&]() { return GetRegister(RZ); });
  114. bb.push_back(Operation(OperationCode::AssignComposite, std::move(meta), texture,
  115. dest[0], dest[1], dest[2], dest[3]));
  116. }
  117. break;
  118. }
  119. case OpCode::Id::TEXS: {
  120. Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
  121. const bool is_array{instr.texs.IsArrayTexture()};
  122. const bool depth_compare = instr.texs.UsesMiscMode(TextureMiscMode::DC);
  123. const auto process_mode = instr.texs.GetTextureProcessMode();
  124. if (instr.texs.UsesMiscMode(TextureMiscMode::NODEP)) {
  125. LOG_WARNING(HW_GPU, "TEXS.NODEP implementation is incomplete");
  126. }
  127. const Node texture =
  128. GetTexsCode(instr, texture_type, process_mode, depth_compare, is_array);
  129. if (instr.texs.fp32_flag) {
  130. WriteTexsInstructionFloat(bb, instr, texture);
  131. } else {
  132. UNIMPLEMENTED();
  133. // WriteTexsInstructionHalfFloat(bb, instr, texture);
  134. }
  135. break;
  136. }
  137. case OpCode::Id::TLD4: {
  138. ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
  139. ASSERT(instr.tld4.array == 0);
  140. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::AOFFI),
  141. "AOFFI is not implemented");
  142. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::NDV),
  143. "NDV is not implemented");
  144. UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(TextureMiscMode::PTP),
  145. "PTP is not implemented");
  146. if (instr.tld4.UsesMiscMode(TextureMiscMode::NODEP)) {
  147. LOG_WARNING(HW_GPU, "TLD4.NODEP implementation is incomplete");
  148. }
  149. const bool depth_compare = instr.tld4.UsesMiscMode(TextureMiscMode::DC);
  150. auto texture_type = instr.tld4.texture_type.Value();
  151. u32 num_coordinates = static_cast<u32>(GetCoordCount(texture_type));
  152. if (depth_compare)
  153. num_coordinates += 1;
  154. std::vector<Node> params;
  155. switch (num_coordinates) {
  156. case 2: {
  157. params.push_back(GetRegister(instr.gpr8));
  158. params.push_back(GetRegister(instr.gpr8.Value() + 1));
  159. break;
  160. }
  161. case 3: {
  162. params.push_back(GetRegister(instr.gpr8));
  163. params.push_back(GetRegister(instr.gpr8.Value() + 1));
  164. params.push_back(GetRegister(instr.gpr8.Value() + 2));
  165. break;
  166. }
  167. default:
  168. UNIMPLEMENTED_MSG("Unhandled coordinates number {}", static_cast<u32>(num_coordinates));
  169. params.push_back(GetRegister(instr.gpr8));
  170. params.push_back(GetRegister(instr.gpr8.Value() + 1));
  171. num_coordinates = 2;
  172. texture_type = Tegra::Shader::TextureType::Texture2D;
  173. }
  174. params.push_back(Immediate(static_cast<u32>(instr.tld4.component)));
  175. const auto& sampler = GetSampler(instr.sampler, texture_type, false, depth_compare);
  176. const MetaTexture meta{sampler, num_coordinates};
  177. const Node texture =
  178. Operation(OperationCode::F4TextureGather, std::move(meta), std::move(params));
  179. if (depth_compare) {
  180. SetRegister(bb, instr.gpr0, texture);
  181. } else {
  182. MetaComponents meta;
  183. std::array<Node, 4> dest;
  184. std::size_t dest_elem = 0;
  185. for (std::size_t elem = 0; elem < 4; ++elem) {
  186. if (!instr.tex.IsComponentEnabled(elem)) {
  187. // Skip disabled components
  188. continue;
  189. }
  190. meta.components_map[dest_elem] = static_cast<u32>(elem);
  191. dest[dest_elem] = GetRegister(instr.gpr0.Value() + dest_elem);
  192. ++dest_elem;
  193. }
  194. std::generate(dest.begin() + dest_elem, dest.end(), [&]() { return GetRegister(RZ); });
  195. bb.push_back(Operation(OperationCode::AssignComposite, std::move(meta), texture,
  196. dest[0], dest[1], dest[2], dest[3]));
  197. }
  198. break;
  199. }
  200. case OpCode::Id::TLD4S: {
  201. UNIMPLEMENTED_IF_MSG(instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI),
  202. "AOFFI is not implemented");
  203. if (instr.tld4s.UsesMiscMode(TextureMiscMode::NODEP)) {
  204. LOG_WARNING(HW_GPU, "TLD4S.NODEP is not implemented");
  205. }
  206. const bool depth_compare = instr.tld4s.UsesMiscMode(TextureMiscMode::DC);
  207. const Node op_a = GetRegister(instr.gpr8);
  208. const Node op_b = GetRegister(instr.gpr20);
  209. std::vector<Node> params;
  210. // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
  211. if (depth_compare) {
  212. // Note: TLD4S coordinate encoding works just like TEXS's
  213. const Node op_y = GetRegister(instr.gpr8.Value() + 1);
  214. params.push_back(op_a);
  215. params.push_back(op_y);
  216. params.push_back(op_b);
  217. } else {
  218. params.push_back(op_a);
  219. params.push_back(op_b);
  220. }
  221. const auto num_coords = static_cast<u32>(params.size());
  222. params.push_back(Immediate(static_cast<u32>(instr.tld4s.component)));
  223. const auto& sampler =
  224. GetSampler(instr.sampler, TextureType::Texture2D, false, depth_compare);
  225. const MetaTexture meta{sampler, num_coords};
  226. WriteTexsInstructionFloat(
  227. bb, instr, Operation(OperationCode::F4TextureGather, meta, std::move(params)));
  228. break;
  229. }
  230. case OpCode::Id::TXQ: {
  231. if (instr.txq.UsesMiscMode(TextureMiscMode::NODEP)) {
  232. LOG_WARNING(HW_GPU, "TXQ.NODEP is not implemented");
  233. }
  234. // TODO: The new commits on the texture refactor, change the way samplers work.
  235. // Sadly, not all texture instructions specify the type of texture their sampler
  236. // uses. This must be fixed at a later instance.
  237. const auto& sampler =
  238. GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
  239. switch (instr.txq.query_type) {
  240. case Tegra::Shader::TextureQueryType::Dimension: {
  241. const MetaTexture meta_texture{sampler};
  242. const MetaComponents meta_components{{0, 1, 2, 3}};
  243. const Node texture = Operation(OperationCode::F4TextureQueryDimensions, meta_texture,
  244. GetRegister(instr.gpr8));
  245. std::array<Node, 4> dest;
  246. for (std::size_t i = 0; i < dest.size(); ++i) {
  247. dest[i] = GetRegister(instr.gpr0.Value() + i);
  248. }
  249. bb.push_back(Operation(OperationCode::AssignComposite, meta_components, texture,
  250. dest[0], dest[1], dest[2], dest[3]));
  251. break;
  252. }
  253. default:
  254. UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
  255. static_cast<u32>(instr.txq.query_type.Value()));
  256. }
  257. break;
  258. }
  259. default:
  260. UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
  261. }
  262. return pc;
  263. }
  264. const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, TextureType type,
  265. bool is_array, bool is_shadow) {
  266. const auto offset = static_cast<std::size_t>(sampler.index.Value());
  267. // If this sampler has already been used, return the existing mapping.
  268. const auto itr =
  269. std::find_if(used_samplers.begin(), used_samplers.end(),
  270. [&](const Sampler& entry) { return entry.GetOffset() == offset; });
  271. if (itr != used_samplers.end()) {
  272. ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
  273. itr->IsShadow() == is_shadow);
  274. return *itr;
  275. }
  276. // Otherwise create a new mapping for this sampler
  277. const std::size_t next_index = used_samplers.size();
  278. const Sampler entry{offset, next_index, type, is_array, is_shadow};
  279. return *used_samplers.emplace(entry).first;
  280. }
  281. void ShaderIR::WriteTexsInstructionFloat(BasicBlock& bb, Tegra::Shader::Instruction instr,
  282. Node texture) {
  283. // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
  284. // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
  285. MetaComponents meta;
  286. std::array<Node, 4> dest;
  287. std::size_t written_components = 0;
  288. for (u32 component = 0; component < 4; ++component) {
  289. if (!instr.texs.IsComponentEnabled(component)) {
  290. continue;
  291. }
  292. meta.components_map[written_components] = static_cast<u32>(component);
  293. if (written_components < 2) {
  294. // Write the first two swizzle components to gpr0 and gpr0+1
  295. dest[written_components] = GetRegister(instr.gpr0.Value() + written_components % 2);
  296. } else {
  297. ASSERT(instr.texs.HasTwoDestinations());
  298. // Write the rest of the swizzle components to gpr28 and gpr28+1
  299. dest[written_components] = GetRegister(instr.gpr28.Value() + written_components % 2);
  300. }
  301. ++written_components;
  302. }
  303. std::generate(dest.begin() + written_components, dest.end(), [&]() { return GetRegister(RZ); });
  304. bb.push_back(Operation(OperationCode::AssignComposite, meta, texture, dest[0], dest[1], dest[2],
  305. dest[3]));
  306. }
  307. Node ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
  308. TextureProcessMode process_mode, bool depth_compare, bool is_array,
  309. std::size_t bias_offset, std::vector<Node>&& coords) {
  310. UNIMPLEMENTED_IF_MSG(
  311. (texture_type == TextureType::Texture3D && (is_array || depth_compare)) ||
  312. (texture_type == TextureType::TextureCube && is_array && depth_compare),
  313. "This method is not supported.");
  314. const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare);
  315. const bool lod_needed = process_mode == TextureProcessMode::LZ ||
  316. process_mode == TextureProcessMode::LL ||
  317. process_mode == TextureProcessMode::LLA;
  318. const bool gl_lod_supported =
  319. !((texture_type == TextureType::Texture2D && is_array && depth_compare) ||
  320. (texture_type == TextureType::TextureCube && !is_array && depth_compare));
  321. const OperationCode read_method =
  322. lod_needed && gl_lod_supported ? OperationCode::F4TextureLod : OperationCode::F4Texture;
  323. const MetaTexture meta{sampler, static_cast<u32>(coords.size())};
  324. std::vector<Node> params = std::move(coords);
  325. if (process_mode != TextureProcessMode::None) {
  326. if (process_mode == TextureProcessMode::LZ) {
  327. if (gl_lod_supported) {
  328. params.push_back(Immediate(0));
  329. } else {
  330. // Lod 0 is emulated by a big negative bias in scenarios that are not supported by
  331. // GLSL
  332. params.push_back(Immediate(-1000));
  333. }
  334. } else {
  335. // If present, lod or bias are always stored in the register indexed by the gpr20 field
  336. // with an offset depending on the usage of the other registers
  337. params.push_back(GetRegister(instr.gpr20.Value() + bias_offset));
  338. }
  339. }
  340. return Operation(read_method, meta, std::move(params));
  341. }
  342. Node ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
  343. TextureProcessMode process_mode, bool depth_compare, bool is_array) {
  344. const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
  345. process_mode != Tegra::Shader::TextureProcessMode::LZ);
  346. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  347. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
  348. // If enabled arrays index is always stored in the gpr8 field
  349. const u64 array_register = instr.gpr8.Value();
  350. // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
  351. const u64 coord_register = array_register + (is_array ? 1 : 0);
  352. std::vector<Node> coords;
  353. for (std::size_t i = 0; i < coord_count; ++i) {
  354. coords.push_back(GetRegister(coord_register + i));
  355. }
  356. // 1D.DC in opengl the 2nd component is ignored.
  357. if (depth_compare && !is_array && texture_type == TextureType::Texture1D) {
  358. coords.push_back(Immediate(0.0f));
  359. }
  360. if (depth_compare) {
  361. // Depth is always stored in the register signaled by gpr20
  362. // or in the next register if lod or bias are used
  363. const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
  364. coords.push_back(GetRegister(depth_register));
  365. }
  366. if (is_array) {
  367. coords.push_back(GetRegister(array_register));
  368. }
  369. // Fill ignored coordinates
  370. while (coords.size() < total_coord_count) {
  371. coords.push_back(Immediate(0));
  372. }
  373. return GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0,
  374. std::move(coords));
  375. }
  376. Node ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
  377. TextureProcessMode process_mode, bool depth_compare, bool is_array) {
  378. const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
  379. process_mode != Tegra::Shader::TextureProcessMode::LZ);
  380. const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement(
  381. texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4);
  382. // If enabled arrays index is always stored in the gpr8 field
  383. const u64 array_register = instr.gpr8.Value();
  384. // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
  385. const u64 coord_register = array_register + (is_array ? 1 : 0);
  386. const u64 last_coord_register =
  387. (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
  388. ? static_cast<u64>(instr.gpr20.Value())
  389. : coord_register + 1;
  390. std::vector<Node> coords;
  391. for (std::size_t i = 0; i < coord_count; ++i) {
  392. const bool last = (i == (coord_count - 1)) && (coord_count > 1);
  393. coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
  394. }
  395. if (depth_compare) {
  396. // Depth is always stored in the register signaled by gpr20
  397. // or in the next register if lod or bias are used
  398. const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
  399. coords.push_back(GetRegister(depth_register));
  400. }
  401. if (is_array) {
  402. coords.push_back(
  403. Operation(OperationCode::ICastFloat, NO_PRECISE, GetRegister(array_register)));
  404. }
  405. // Fill ignored coordinates
  406. while (coords.size() < total_coord_count) {
  407. coords.push_back(Immediate(0));
  408. }
  409. return GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array,
  410. (coord_count > 2 ? 1 : 0), std::move(coords));
  411. }
  412. std::tuple<std::size_t, std::size_t> ShaderIR::ValidateAndGetCoordinateElement(
  413. TextureType texture_type, bool depth_compare, bool is_array, bool lod_bias_enabled,
  414. std::size_t max_coords, std::size_t max_inputs) {
  415. const std::size_t coord_count = GetCoordCount(texture_type);
  416. std::size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0);
  417. const std::size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0);
  418. if (total_coord_count > max_coords || total_reg_count > max_inputs) {
  419. UNIMPLEMENTED_MSG("Unsupported Texture operation");
  420. total_coord_count = std::min(total_coord_count, max_coords);
  421. }
  422. // 1D.DC OpenGL is using a vec3 but 2nd component is ignored later.
  423. total_coord_count +=
  424. (depth_compare && !is_array && texture_type == TextureType::Texture1D) ? 1 : 0;
  425. return {coord_count, total_coord_count};
  426. }
  427. } // namespace VideoCommon::Shader