gpu_dirty_memory_manager.h 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. #pragma once
  4. #include <atomic>
  5. #include <bit>
  6. #include <functional>
  7. #include <mutex>
  8. #include <utility>
  9. #include <vector>
  10. #include "core/device_memory_manager.h"
  11. namespace Core {
  12. class GPUDirtyMemoryManager {
  13. public:
  14. GPUDirtyMemoryManager() : current{default_transform} {
  15. back_buffer.reserve(256);
  16. front_buffer.reserve(256);
  17. }
  18. ~GPUDirtyMemoryManager() = default;
  19. void Collect(PAddr address, size_t size) {
  20. TransformAddress t = BuildTransform(address, size);
  21. TransformAddress tmp, original;
  22. do {
  23. tmp = current.load(std::memory_order_acquire);
  24. original = tmp;
  25. if (tmp.address != t.address) {
  26. if (IsValid(tmp.address)) {
  27. std::scoped_lock lk(guard);
  28. back_buffer.emplace_back(tmp);
  29. current.exchange(t, std::memory_order_relaxed);
  30. return;
  31. }
  32. tmp.address = t.address;
  33. tmp.mask = 0;
  34. }
  35. if ((tmp.mask | t.mask) == tmp.mask) {
  36. return;
  37. }
  38. tmp.mask |= t.mask;
  39. } while (!current.compare_exchange_weak(original, tmp, std::memory_order_release,
  40. std::memory_order_relaxed));
  41. }
  42. void Gather(std::function<void(PAddr, size_t)>& callback) {
  43. {
  44. std::scoped_lock lk(guard);
  45. TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed);
  46. front_buffer.swap(back_buffer);
  47. if (IsValid(t.address)) {
  48. front_buffer.emplace_back(t);
  49. }
  50. }
  51. for (auto& transform : front_buffer) {
  52. size_t offset = 0;
  53. u64 mask = transform.mask;
  54. while (mask != 0) {
  55. const size_t empty_bits = std::countr_zero(mask);
  56. offset += empty_bits << align_bits;
  57. mask = mask >> empty_bits;
  58. const size_t continuous_bits = std::countr_one(mask);
  59. callback((static_cast<PAddr>(transform.address) << page_bits) + offset,
  60. continuous_bits << align_bits);
  61. mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0;
  62. offset += continuous_bits << align_bits;
  63. }
  64. }
  65. front_buffer.clear();
  66. }
  67. private:
  68. struct alignas(8) TransformAddress {
  69. u32 address;
  70. u32 mask;
  71. };
  72. constexpr static size_t page_bits = DEVICE_PAGEBITS - 1;
  73. constexpr static size_t page_size = 1ULL << page_bits;
  74. constexpr static size_t page_mask = page_size - 1;
  75. constexpr static size_t align_bits = 6U;
  76. constexpr static size_t align_size = 1U << align_bits;
  77. constexpr static size_t align_mask = align_size - 1;
  78. constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U};
  79. bool IsValid(PAddr address) {
  80. return address < (1ULL << 39);
  81. }
  82. template <typename T>
  83. T CreateMask(size_t top_bit, size_t minor_bit) {
  84. T mask = ~T(0);
  85. mask <<= (sizeof(T) * 8 - top_bit);
  86. mask >>= (sizeof(T) * 8 - top_bit);
  87. mask >>= minor_bit;
  88. mask <<= minor_bit;
  89. return mask;
  90. }
  91. TransformAddress BuildTransform(PAddr address, size_t size) {
  92. const size_t minor_address = address & page_mask;
  93. const size_t minor_bit = minor_address >> align_bits;
  94. const size_t top_bit = (minor_address + size + align_mask) >> align_bits;
  95. TransformAddress result{};
  96. result.address = static_cast<u32>(address >> page_bits);
  97. result.mask = CreateMask<u32>(top_bit, minor_bit);
  98. return result;
  99. }
  100. std::atomic<TransformAddress> current{};
  101. std::mutex guard;
  102. std::vector<TransformAddress> back_buffer;
  103. std::vector<TransformAddress> front_buffer;
  104. };
  105. } // namespace Core