freezer.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/logging/log.h"
  6. #include "core/core.h"
  7. #include "core/core_timing.h"
  8. #include "core/core_timing_util.h"
  9. #include "core/hardware_properties.h"
  10. #include "core/memory.h"
  11. #include "core/tools/freezer.h"
  12. namespace Tools {
  13. namespace {
  14. constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60};
  15. u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
  16. switch (width) {
  17. case 1:
  18. return memory.Read8(addr);
  19. case 2:
  20. return memory.Read16(addr);
  21. case 4:
  22. return memory.Read32(addr);
  23. case 8:
  24. return memory.Read64(addr);
  25. default:
  26. UNREACHABLE();
  27. return 0;
  28. }
  29. }
  30. void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
  31. switch (width) {
  32. case 1:
  33. memory.Write8(addr, static_cast<u8>(value));
  34. break;
  35. case 2:
  36. memory.Write16(addr, static_cast<u16>(value));
  37. break;
  38. case 4:
  39. memory.Write32(addr, static_cast<u32>(value));
  40. break;
  41. case 8:
  42. memory.Write64(addr, value);
  43. break;
  44. default:
  45. UNREACHABLE();
  46. }
  47. }
  48. } // Anonymous namespace
  49. Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
  50. : core_timing{core_timing_}, memory{memory_} {
  51. event = Core::Timing::CreateEvent(
  52. "MemoryFreezer::FrameCallback",
  53. [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
  54. FrameCallback(user_data, ns_late);
  55. });
  56. core_timing.ScheduleEvent(memory_freezer_ns, event);
  57. }
  58. Freezer::~Freezer() {
  59. core_timing.UnscheduleEvent(event, 0);
  60. }
  61. void Freezer::SetActive(bool is_active) {
  62. if (!active.exchange(is_active)) {
  63. FillEntryReads();
  64. core_timing.ScheduleEvent(memory_freezer_ns, event);
  65. LOG_DEBUG(Common_Memory, "Memory freezer activated!");
  66. } else {
  67. LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
  68. }
  69. }
  70. bool Freezer::IsActive() const {
  71. return active.load(std::memory_order_relaxed);
  72. }
  73. void Freezer::Clear() {
  74. std::lock_guard lock{entries_mutex};
  75. LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
  76. entries.clear();
  77. }
  78. u64 Freezer::Freeze(VAddr address, u32 width) {
  79. std::lock_guard lock{entries_mutex};
  80. const auto current_value = MemoryReadWidth(memory, width, address);
  81. entries.push_back({address, width, current_value});
  82. LOG_DEBUG(Common_Memory,
  83. "Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address,
  84. width, current_value);
  85. return current_value;
  86. }
  87. void Freezer::Unfreeze(VAddr address) {
  88. std::lock_guard lock{entries_mutex};
  89. LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
  90. std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
  91. }
  92. bool Freezer::IsFrozen(VAddr address) const {
  93. std::lock_guard lock{entries_mutex};
  94. return FindEntry(address) != entries.cend();
  95. }
  96. void Freezer::SetFrozenValue(VAddr address, u64 value) {
  97. std::lock_guard lock{entries_mutex};
  98. const auto iter = FindEntry(address);
  99. if (iter == entries.cend()) {
  100. LOG_ERROR(Common_Memory,
  101. "Tried to set freeze value for address={:016X} that is not frozen!", address);
  102. return;
  103. }
  104. LOG_DEBUG(Common_Memory,
  105. "Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}",
  106. iter->address, iter->width, value);
  107. iter->value = value;
  108. }
  109. std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
  110. std::lock_guard lock{entries_mutex};
  111. const auto iter = FindEntry(address);
  112. if (iter == entries.cend()) {
  113. return std::nullopt;
  114. }
  115. return *iter;
  116. }
  117. std::vector<Freezer::Entry> Freezer::GetEntries() const {
  118. std::lock_guard lock{entries_mutex};
  119. return entries;
  120. }
  121. Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
  122. return std::find_if(entries.begin(), entries.end(),
  123. [address](const Entry& entry) { return entry.address == address; });
  124. }
  125. Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
  126. return std::find_if(entries.begin(), entries.end(),
  127. [address](const Entry& entry) { return entry.address == address; });
  128. }
  129. void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
  130. if (!IsActive()) {
  131. LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
  132. return;
  133. }
  134. std::lock_guard lock{entries_mutex};
  135. for (const auto& entry : entries) {
  136. LOG_DEBUG(Common_Memory,
  137. "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
  138. entry.address, entry.value, entry.width);
  139. MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
  140. }
  141. core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event);
  142. }
  143. void Freezer::FillEntryReads() {
  144. std::lock_guard lock{entries_mutex};
  145. LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
  146. for (auto& entry : entries) {
  147. entry.value = MemoryReadWidth(memory, entry.width, entry.address);
  148. }
  149. }
  150. } // namespace Tools