track.cpp 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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/shader_ir.h"
  9. namespace VideoCommon::Shader {
  10. namespace {
  11. std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
  12. OperationCode operation_code) {
  13. for (; cursor >= 0; --cursor) {
  14. const Node node = code.at(cursor);
  15. if (const auto operation = std::get_if<OperationNode>(node)) {
  16. if (operation->GetCode() == operation_code)
  17. return {node, cursor};
  18. }
  19. if (const auto conditional = std::get_if<ConditionalNode>(node)) {
  20. const auto& conditional_code = conditional->GetCode();
  21. const auto [found, internal_cursor] = FindOperation(
  22. conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
  23. if (found)
  24. return {found, cursor};
  25. }
  26. }
  27. return {};
  28. }
  29. } // namespace
  30. Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) {
  31. if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
  32. // Cbuf found, but it has to be immediate
  33. return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
  34. }
  35. if (const auto gpr = std::get_if<GprNode>(tracked)) {
  36. if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
  37. return nullptr;
  38. }
  39. // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
  40. // register that it uses as operand
  41. const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1);
  42. if (!source) {
  43. return nullptr;
  44. }
  45. return TrackCbuf(source, code, new_cursor);
  46. }
  47. if (const auto operation = std::get_if<OperationNode>(tracked)) {
  48. for (std::size_t i = 0; i < operation->GetOperandsCount(); ++i) {
  49. if (const auto found = TrackCbuf((*operation)[i], code, cursor)) {
  50. // Cbuf found in operand
  51. return found;
  52. }
  53. }
  54. return nullptr;
  55. }
  56. if (const auto conditional = std::get_if<ConditionalNode>(tracked)) {
  57. const auto& conditional_code = conditional->GetCode();
  58. return TrackCbuf(tracked, conditional_code, static_cast<s64>(conditional_code.size()));
  59. }
  60. return nullptr;
  61. }
  62. std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) {
  63. // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
  64. // that it uses as operand
  65. const auto [found, found_cursor] =
  66. TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
  67. if (!found) {
  68. return {};
  69. }
  70. if (const auto immediate = std::get_if<ImmediateNode>(found)) {
  71. return immediate->GetValue();
  72. }
  73. return {};
  74. }
  75. std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
  76. s64 cursor) {
  77. for (; cursor >= 0; --cursor) {
  78. const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign);
  79. if (!found_node) {
  80. return {};
  81. }
  82. const auto operation = std::get_if<OperationNode>(found_node);
  83. ASSERT(operation);
  84. const auto& target = (*operation)[0];
  85. if (const auto gpr_target = std::get_if<GprNode>(target)) {
  86. if (gpr_target->GetIndex() == tracked->GetIndex()) {
  87. return {(*operation)[1], new_cursor};
  88. }
  89. }
  90. }
  91. return {};
  92. }
  93. } // namespace VideoCommon::Shader