track.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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 <utility>
  6. #include <variant>
  7. #include "common/common_types.h"
  8. #include "video_core/shader/node.h"
  9. #include "video_core/shader/node_helper.h"
  10. #include "video_core/shader/shader_ir.h"
  11. namespace VideoCommon::Shader {
  12. namespace {
  13. std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
  14. OperationCode operation_code) {
  15. for (; cursor >= 0; --cursor) {
  16. Node node = code.at(cursor);
  17. if (const auto operation = std::get_if<OperationNode>(&*node)) {
  18. if (operation->GetCode() == operation_code) {
  19. return {std::move(node), cursor};
  20. }
  21. }
  22. if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
  23. const auto& conditional_code = conditional->GetCode();
  24. auto result = FindOperation(
  25. conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
  26. auto& found = result.first;
  27. if (found) {
  28. return {std::move(found), cursor};
  29. }
  30. }
  31. }
  32. return {};
  33. }
  34. std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) {
  35. if (operation.GetCode() != OperationCode::UAdd) {
  36. return std::nullopt;
  37. }
  38. Node gpr;
  39. Node offset;
  40. ASSERT(operation.GetOperandsCount() == 2);
  41. for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) {
  42. Node operand = operation[i];
  43. if (std::holds_alternative<ImmediateNode>(*operand)) {
  44. offset = operation[i];
  45. } else if (std::holds_alternative<GprNode>(*operand)) {
  46. gpr = operation[i];
  47. }
  48. }
  49. if (offset && gpr) {
  50. return std::make_pair(gpr, offset);
  51. }
  52. return std::nullopt;
  53. }
  54. bool AmendNodeCv(std::size_t amend_index, Node node) {
  55. if (const auto operation = std::get_if<OperationNode>(&*node)) {
  56. operation->SetAmendIndex(amend_index);
  57. return true;
  58. } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
  59. conditional->SetAmendIndex(amend_index);
  60. return true;
  61. }
  62. return false;
  63. }
  64. } // Anonymous namespace
  65. std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code,
  66. s64 cursor) {
  67. if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
  68. // Constant buffer found, test if it's an immediate
  69. const auto offset = cbuf->GetOffset();
  70. if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
  71. auto track =
  72. MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
  73. return {tracked, track};
  74. } else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
  75. const u32 bound_buffer = registry.GetBoundBuffer();
  76. if (bound_buffer != cbuf->GetIndex()) {
  77. return {};
  78. }
  79. const auto pair = DecoupleIndirectRead(*operation);
  80. if (!pair) {
  81. return {};
  82. }
  83. auto [gpr, base_offset] = *pair;
  84. const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
  85. const auto& gpu_driver = registry.AccessGuestDriverProfile();
  86. const u32 bindless_cv = NewCustomVariable();
  87. const Node op =
  88. Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize()));
  89. const Node cv_node = GetCustomVariable(bindless_cv);
  90. Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
  91. const std::size_t amend_index = DeclareAmend(amend_op);
  92. AmendNodeCv(amend_index, code[cursor]);
  93. // TODO Implement Bindless Index custom variable
  94. auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(),
  95. offset_inm->GetValue(), bindless_cv);
  96. return {tracked, track};
  97. }
  98. return {};
  99. }
  100. if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
  101. if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
  102. return {};
  103. }
  104. // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
  105. // register that it uses as operand
  106. const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1);
  107. if (!source) {
  108. return {};
  109. }
  110. return TrackBindlessSampler(source, code, new_cursor);
  111. }
  112. if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
  113. for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) {
  114. if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor);
  115. std::get<0>(found)) {
  116. // Cbuf found in operand.
  117. return found;
  118. }
  119. }
  120. return {};
  121. }
  122. if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
  123. const auto& conditional_code = conditional->GetCode();
  124. return TrackBindlessSampler(tracked, conditional_code,
  125. static_cast<s64>(conditional_code.size()));
  126. }
  127. return {};
  128. }
  129. std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code,
  130. s64 cursor) const {
  131. if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
  132. // Constant buffer found, test if it's an immediate
  133. const auto offset = cbuf->GetOffset();
  134. if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
  135. return {tracked, cbuf->GetIndex(), immediate->GetValue()};
  136. }
  137. return {};
  138. }
  139. if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
  140. if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
  141. return {};
  142. }
  143. s64 current_cursor = cursor;
  144. while (current_cursor > 0) {
  145. // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
  146. // register that it uses as operand
  147. const auto [source, new_cursor] = TrackRegister(gpr, code, current_cursor - 1);
  148. current_cursor = new_cursor;
  149. if (!source) {
  150. continue;
  151. }
  152. const auto [base_address, index, offset] = TrackCbuf(source, code, current_cursor);
  153. if (base_address != nullptr) {
  154. return {base_address, index, offset};
  155. }
  156. }
  157. return {};
  158. }
  159. if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
  160. for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) {
  161. if (auto found = TrackCbuf((*operation)[i - 1], code, cursor); std::get<0>(found)) {
  162. // Cbuf found in operand.
  163. return found;
  164. }
  165. }
  166. return {};
  167. }
  168. if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
  169. const auto& conditional_code = conditional->GetCode();
  170. return TrackCbuf(tracked, conditional_code, static_cast<s64>(conditional_code.size()));
  171. }
  172. return {};
  173. }
  174. std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const {
  175. // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
  176. // that it uses as operand
  177. const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
  178. const auto& found = result.first;
  179. if (!found) {
  180. return {};
  181. }
  182. if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
  183. return immediate->GetValue();
  184. }
  185. return {};
  186. }
  187. std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
  188. s64 cursor) const {
  189. for (; cursor >= 0; --cursor) {
  190. const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign);
  191. if (!found_node) {
  192. return {};
  193. }
  194. const auto operation = std::get_if<OperationNode>(&*found_node);
  195. ASSERT(operation);
  196. const auto& target = (*operation)[0];
  197. if (const auto gpr_target = std::get_if<GprNode>(&*target)) {
  198. if (gpr_target->GetIndex() == tracked->GetIndex()) {
  199. return {(*operation)[1], new_cursor};
  200. }
  201. }
  202. }
  203. return {};
  204. }
  205. } // namespace VideoCommon::Shader