track.cpp 3.7 KB

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