memory_manager.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include "common/alignment.h"
  5. #include "common/assert.h"
  6. #include "common/logging/log.h"
  7. #include "core/core.h"
  8. #include "core/device_memory.h"
  9. #include "core/hle/kernel/k_page_table.h"
  10. #include "core/hle/kernel/k_process.h"
  11. #include "core/memory.h"
  12. #include "video_core/memory_manager.h"
  13. #include "video_core/rasterizer_interface.h"
  14. #include "video_core/renderer_base.h"
  15. namespace Tegra {
  16. std::atomic<size_t> MemoryManager::unique_identifier_generator{};
  17. MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_,
  18. u64 page_bits_)
  19. : system{system_}, memory{system.Memory()}, device_memory{system.DeviceMemory()},
  20. address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
  21. entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
  22. page_bits != big_page_bits ? page_bits : 0},
  23. unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} {
  24. address_space_size = 1ULL << address_space_bits;
  25. page_size = 1ULL << page_bits;
  26. page_mask = page_size - 1ULL;
  27. big_page_size = 1ULL << big_page_bits;
  28. big_page_mask = big_page_size - 1ULL;
  29. const u64 page_table_bits = address_space_bits - page_bits;
  30. const u64 big_page_table_bits = address_space_bits - big_page_bits;
  31. const u64 page_table_size = 1ULL << page_table_bits;
  32. const u64 big_page_table_size = 1ULL << big_page_table_bits;
  33. page_table_mask = page_table_size - 1;
  34. big_page_table_mask = big_page_table_size - 1;
  35. big_entries.resize(big_page_table_size / 32, 0);
  36. big_page_table_cpu.resize(big_page_table_size);
  37. big_page_continous.resize(big_page_table_size / continous_bits, 0);
  38. std::array<PTEKind, 32> kind_valus;
  39. kind_valus.fill(PTEKind::INVALID);
  40. big_kinds.resize(big_page_table_size / 32, kind_valus);
  41. entries.resize(page_table_size / 32, 0);
  42. kinds.resize(page_table_size / 32, kind_valus);
  43. }
  44. MemoryManager::~MemoryManager() = default;
  45. template <bool is_big_page>
  46. MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const {
  47. if constexpr (is_big_page) {
  48. position = position >> big_page_bits;
  49. const u64 entry_mask = big_entries[position / 32];
  50. const size_t sub_index = position % 32;
  51. return static_cast<EntryType>((entry_mask >> (2 * sub_index)) & 0x03ULL);
  52. } else {
  53. position = position >> page_bits;
  54. const u64 entry_mask = entries[position / 32];
  55. const size_t sub_index = position % 32;
  56. return static_cast<EntryType>((entry_mask >> (2 * sub_index)) & 0x03ULL);
  57. }
  58. }
  59. template <bool is_big_page>
  60. void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
  61. if constexpr (is_big_page) {
  62. position = position >> big_page_bits;
  63. const u64 entry_mask = big_entries[position / 32];
  64. const size_t sub_index = position % 32;
  65. big_entries[position / 32] =
  66. (~(3ULL << sub_index * 2) & entry_mask) | (static_cast<u64>(entry) << sub_index * 2);
  67. } else {
  68. position = position >> page_bits;
  69. const u64 entry_mask = entries[position / 32];
  70. const size_t sub_index = position % 32;
  71. entries[position / 32] =
  72. (~(3ULL << sub_index * 2) & entry_mask) | (static_cast<u64>(entry) << sub_index * 2);
  73. }
  74. }
  75. PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
  76. auto entry = GetEntry<true>(gpu_addr);
  77. if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] {
  78. return GetKind<true>(gpu_addr);
  79. } else {
  80. return GetKind<false>(gpu_addr);
  81. }
  82. }
  83. template <bool is_big_page>
  84. PTEKind MemoryManager::GetKind(size_t position) const {
  85. if constexpr (is_big_page) {
  86. position = position >> big_page_bits;
  87. const size_t sub_index = position % 32;
  88. return big_kinds[position / 32][sub_index];
  89. } else {
  90. position = position >> page_bits;
  91. const size_t sub_index = position % 32;
  92. return kinds[position / 32][sub_index];
  93. }
  94. }
  95. template <bool is_big_page>
  96. void MemoryManager::SetKind(size_t position, PTEKind kind) {
  97. if constexpr (is_big_page) {
  98. position = position >> big_page_bits;
  99. const size_t sub_index = position % 32;
  100. big_kinds[position / 32][sub_index] = kind;
  101. } else {
  102. position = position >> page_bits;
  103. const size_t sub_index = position % 32;
  104. kinds[position / 32][sub_index] = kind;
  105. }
  106. }
  107. inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
  108. const u64 entry_mask = big_page_continous[big_page_index / continous_bits];
  109. const size_t sub_index = big_page_index % continous_bits;
  110. return ((entry_mask >> sub_index) & 0x1ULL) != 0;
  111. }
  112. inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value) {
  113. const u64 continous_mask = big_page_continous[big_page_index / continous_bits];
  114. const size_t sub_index = big_page_index % continous_bits;
  115. big_page_continous[big_page_index / continous_bits] =
  116. (~(1ULL << sub_index) & continous_mask) | (value ? 1ULL << sub_index : 0);
  117. }
  118. template <MemoryManager::EntryType entry_type>
  119. GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size,
  120. PTEKind kind) {
  121. [[maybe_unused]] u64 remaining_size{size};
  122. if constexpr (entry_type == EntryType::Mapped) {
  123. page_table.ReserveRange(gpu_addr, size);
  124. }
  125. for (u64 offset{}; offset < size; offset += page_size) {
  126. const GPUVAddr current_gpu_addr = gpu_addr + offset;
  127. [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
  128. SetEntry<false>(current_gpu_addr, entry_type);
  129. SetKind<false>(current_gpu_addr, kind);
  130. if (current_entry_type != entry_type) {
  131. rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size);
  132. }
  133. if constexpr (entry_type == EntryType::Mapped) {
  134. const VAddr current_cpu_addr = cpu_addr + offset;
  135. const auto index = PageEntryIndex<false>(current_gpu_addr);
  136. const u32 sub_value = static_cast<u32>(current_cpu_addr >> cpu_page_bits);
  137. page_table[index] = sub_value;
  138. }
  139. remaining_size -= page_size;
  140. }
  141. return gpu_addr;
  142. }
  143. template <MemoryManager::EntryType entry_type>
  144. GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr,
  145. size_t size, PTEKind kind) {
  146. [[maybe_unused]] u64 remaining_size{size};
  147. for (u64 offset{}; offset < size; offset += big_page_size) {
  148. const GPUVAddr current_gpu_addr = gpu_addr + offset;
  149. [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
  150. SetEntry<true>(current_gpu_addr, entry_type);
  151. SetKind<true>(current_gpu_addr, kind);
  152. if (current_entry_type != entry_type) {
  153. rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size);
  154. }
  155. if constexpr (entry_type == EntryType::Mapped) {
  156. const VAddr current_cpu_addr = cpu_addr + offset;
  157. const auto index = PageEntryIndex<true>(current_gpu_addr);
  158. const u32 sub_value = static_cast<u32>(current_cpu_addr >> cpu_page_bits);
  159. big_page_table_cpu[index] = sub_value;
  160. const bool is_continous = ([&] {
  161. uintptr_t base_ptr{
  162. reinterpret_cast<uintptr_t>(memory.GetPointerSilent(current_cpu_addr))};
  163. if (base_ptr == 0) {
  164. return false;
  165. }
  166. for (VAddr start_cpu = current_cpu_addr + page_size;
  167. start_cpu < current_cpu_addr + big_page_size; start_cpu += page_size) {
  168. base_ptr += page_size;
  169. auto next_ptr = reinterpret_cast<uintptr_t>(memory.GetPointerSilent(start_cpu));
  170. if (next_ptr == 0 || base_ptr != next_ptr) {
  171. return false;
  172. }
  173. }
  174. return true;
  175. })();
  176. SetBigPageContinous(index, is_continous);
  177. }
  178. remaining_size -= big_page_size;
  179. }
  180. return gpu_addr;
  181. }
  182. void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  183. rasterizer = rasterizer_;
  184. }
  185. GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, PTEKind kind,
  186. bool is_big_pages) {
  187. if (is_big_pages) [[likely]] {
  188. return BigPageTableOp<EntryType::Mapped>(gpu_addr, cpu_addr, size, kind);
  189. }
  190. return PageTableOp<EntryType::Mapped>(gpu_addr, cpu_addr, size, kind);
  191. }
  192. GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) {
  193. if (is_big_pages) [[likely]] {
  194. return BigPageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
  195. }
  196. return PageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
  197. }
  198. void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
  199. if (size == 0) {
  200. return;
  201. }
  202. const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
  203. for (const auto& [map_addr, map_size] : submapped_ranges) {
  204. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
  205. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
  206. ASSERT(cpu_addr);
  207. rasterizer->UnmapMemory(*cpu_addr, map_size);
  208. }
  209. BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
  210. PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
  211. }
  212. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
  213. if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] {
  214. return std::nullopt;
  215. }
  216. if (GetEntry<true>(gpu_addr) != EntryType::Mapped) [[unlikely]] {
  217. if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
  218. return std::nullopt;
  219. }
  220. const VAddr cpu_addr_base = static_cast<VAddr>(page_table[PageEntryIndex<false>(gpu_addr)])
  221. << cpu_page_bits;
  222. return cpu_addr_base + (gpu_addr & page_mask);
  223. }
  224. const VAddr cpu_addr_base =
  225. static_cast<VAddr>(big_page_table_cpu[PageEntryIndex<true>(gpu_addr)]) << cpu_page_bits;
  226. return cpu_addr_base + (gpu_addr & big_page_mask);
  227. }
  228. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const {
  229. size_t page_index{addr >> page_bits};
  230. const size_t page_last{(addr + size + page_size - 1) >> page_bits};
  231. while (page_index < page_last) {
  232. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  233. if (page_addr) {
  234. return page_addr;
  235. }
  236. ++page_index;
  237. }
  238. return std::nullopt;
  239. }
  240. template <typename T>
  241. T MemoryManager::Read(GPUVAddr addr) const {
  242. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  243. // NOTE: Avoid adding any extra logic to this fast-path block
  244. T value;
  245. std::memcpy(&value, page_pointer, sizeof(T));
  246. return value;
  247. }
  248. ASSERT(false);
  249. return {};
  250. }
  251. template <typename T>
  252. void MemoryManager::Write(GPUVAddr addr, T data) {
  253. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  254. // NOTE: Avoid adding any extra logic to this fast-path block
  255. std::memcpy(page_pointer, &data, sizeof(T));
  256. return;
  257. }
  258. ASSERT(false);
  259. }
  260. template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
  261. template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
  262. template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
  263. template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
  264. template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
  265. template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
  266. template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
  267. template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
  268. u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
  269. const auto address{GpuToCpuAddress(gpu_addr)};
  270. if (!address) {
  271. return {};
  272. }
  273. return memory.GetPointer(*address);
  274. }
  275. const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
  276. const auto address{GpuToCpuAddress(gpu_addr)};
  277. if (!address) {
  278. return {};
  279. }
  280. return memory.GetPointer(*address);
  281. }
  282. #ifdef _MSC_VER // no need for gcc / clang but msvc's compiler is more conservative with inlining.
  283. #pragma inline_recursion(on)
  284. #endif
  285. template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
  286. inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
  287. FuncMapped&& func_mapped, FuncReserved&& func_reserved,
  288. FuncUnmapped&& func_unmapped) const {
  289. static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>;
  290. static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>;
  291. static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>;
  292. u64 used_page_size;
  293. u64 used_page_mask;
  294. u64 used_page_bits;
  295. if constexpr (is_big_pages) {
  296. used_page_size = big_page_size;
  297. used_page_mask = big_page_mask;
  298. used_page_bits = big_page_bits;
  299. } else {
  300. used_page_size = page_size;
  301. used_page_mask = page_mask;
  302. used_page_bits = page_bits;
  303. }
  304. std::size_t remaining_size{size};
  305. std::size_t page_index{gpu_src_addr >> used_page_bits};
  306. std::size_t page_offset{gpu_src_addr & used_page_mask};
  307. GPUVAddr current_address = gpu_src_addr;
  308. while (remaining_size > 0) {
  309. const std::size_t copy_amount{
  310. std::min(static_cast<std::size_t>(used_page_size) - page_offset, remaining_size)};
  311. auto entry = GetEntry<is_big_pages>(current_address);
  312. if (entry == EntryType::Mapped) [[likely]] {
  313. if constexpr (BOOL_BREAK_MAPPED) {
  314. if (func_mapped(page_index, page_offset, copy_amount)) {
  315. return;
  316. }
  317. } else {
  318. func_mapped(page_index, page_offset, copy_amount);
  319. }
  320. } else if (entry == EntryType::Reserved) {
  321. if constexpr (BOOL_BREAK_RESERVED) {
  322. if (func_reserved(page_index, page_offset, copy_amount)) {
  323. return;
  324. }
  325. } else {
  326. func_reserved(page_index, page_offset, copy_amount);
  327. }
  328. } else [[unlikely]] {
  329. if constexpr (BOOL_BREAK_UNMAPPED) {
  330. if (func_unmapped(page_index, page_offset, copy_amount)) {
  331. return;
  332. }
  333. } else {
  334. func_unmapped(page_index, page_offset, copy_amount);
  335. }
  336. }
  337. page_index++;
  338. page_offset = 0;
  339. remaining_size -= copy_amount;
  340. current_address += copy_amount;
  341. }
  342. }
  343. template <bool is_safe>
  344. void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
  345. std::size_t size) const {
  346. auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
  347. [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
  348. std::memset(dest_buffer, 0, copy_amount);
  349. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  350. };
  351. auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  352. const VAddr cpu_addr_base =
  353. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  354. if constexpr (is_safe) {
  355. rasterizer->FlushRegion(cpu_addr_base, copy_amount);
  356. }
  357. u8* physical = memory.GetPointer(cpu_addr_base);
  358. std::memcpy(dest_buffer, physical, copy_amount);
  359. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  360. };
  361. auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  362. const VAddr cpu_addr_base =
  363. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  364. if constexpr (is_safe) {
  365. rasterizer->FlushRegion(cpu_addr_base, copy_amount);
  366. }
  367. if (!IsBigPageContinous(page_index)) [[unlikely]] {
  368. memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
  369. } else {
  370. u8* physical = memory.GetPointer(cpu_addr_base);
  371. std::memcpy(dest_buffer, physical, copy_amount);
  372. }
  373. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  374. };
  375. auto read_short_pages = [&](std::size_t page_index, std::size_t offset,
  376. std::size_t copy_amount) {
  377. GPUVAddr base = (page_index << big_page_bits) + offset;
  378. MemoryOperation<false>(base, copy_amount, mapped_normal, set_to_zero, set_to_zero);
  379. };
  380. MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
  381. }
  382. void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
  383. ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size);
  384. }
  385. void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
  386. const std::size_t size) const {
  387. ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size);
  388. }
  389. template <bool is_safe>
  390. void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer,
  391. std::size_t size) {
  392. auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
  393. [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
  394. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  395. };
  396. auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  397. const VAddr cpu_addr_base =
  398. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  399. if constexpr (is_safe) {
  400. rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
  401. }
  402. u8* physical = memory.GetPointer(cpu_addr_base);
  403. std::memcpy(physical, src_buffer, copy_amount);
  404. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  405. };
  406. auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  407. const VAddr cpu_addr_base =
  408. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  409. if constexpr (is_safe) {
  410. rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
  411. }
  412. if (!IsBigPageContinous(page_index)) [[unlikely]] {
  413. memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
  414. } else {
  415. u8* physical = memory.GetPointer(cpu_addr_base);
  416. std::memcpy(physical, src_buffer, copy_amount);
  417. }
  418. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  419. };
  420. auto write_short_pages = [&](std::size_t page_index, std::size_t offset,
  421. std::size_t copy_amount) {
  422. GPUVAddr base = (page_index << big_page_bits) + offset;
  423. MemoryOperation<false>(base, copy_amount, mapped_normal, just_advance, just_advance);
  424. };
  425. MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
  426. }
  427. void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
  428. WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size);
  429. }
  430. void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
  431. std::size_t size) {
  432. WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size);
  433. }
  434. void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
  435. auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
  436. [[maybe_unused]] std::size_t offset,
  437. [[maybe_unused]] std::size_t copy_amount) {};
  438. auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  439. const VAddr cpu_addr_base =
  440. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  441. rasterizer->FlushRegion(cpu_addr_base, copy_amount);
  442. };
  443. auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  444. const VAddr cpu_addr_base =
  445. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  446. rasterizer->FlushRegion(cpu_addr_base, copy_amount);
  447. };
  448. auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
  449. std::size_t copy_amount) {
  450. GPUVAddr base = (page_index << big_page_bits) + offset;
  451. MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
  452. };
  453. MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
  454. }
  455. bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const {
  456. bool result = false;
  457. auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
  458. [[maybe_unused]] std::size_t offset,
  459. [[maybe_unused]] std::size_t copy_amount) { return false; };
  460. auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  461. const VAddr cpu_addr_base =
  462. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  463. result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount);
  464. return result;
  465. };
  466. auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  467. const VAddr cpu_addr_base =
  468. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  469. result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount);
  470. return result;
  471. };
  472. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  473. std::size_t copy_amount) {
  474. GPUVAddr base = (page_index << big_page_bits) + offset;
  475. MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
  476. return result;
  477. };
  478. MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, check_short_pages);
  479. return result;
  480. }
  481. size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
  482. std::optional<VAddr> old_page_addr{};
  483. size_t range_so_far = 0;
  484. bool result{false};
  485. auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  486. std::size_t copy_amount) {
  487. result = true;
  488. return true;
  489. };
  490. auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  491. const VAddr cpu_addr_base =
  492. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  493. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  494. result = true;
  495. return true;
  496. }
  497. range_so_far += copy_amount;
  498. old_page_addr = {cpu_addr_base + copy_amount};
  499. return false;
  500. };
  501. auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  502. const VAddr cpu_addr_base =
  503. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  504. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  505. return true;
  506. }
  507. range_so_far += copy_amount;
  508. old_page_addr = {cpu_addr_base + copy_amount};
  509. return false;
  510. };
  511. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  512. std::size_t copy_amount) {
  513. GPUVAddr base = (page_index << big_page_bits) + offset;
  514. MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
  515. return result;
  516. };
  517. MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
  518. return range_so_far;
  519. }
  520. void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
  521. auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
  522. [[maybe_unused]] std::size_t offset,
  523. [[maybe_unused]] std::size_t copy_amount) {};
  524. auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  525. const VAddr cpu_addr_base =
  526. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  527. rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
  528. };
  529. auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  530. const VAddr cpu_addr_base =
  531. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  532. rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
  533. };
  534. auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
  535. std::size_t copy_amount) {
  536. GPUVAddr base = (page_index << big_page_bits) + offset;
  537. MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
  538. };
  539. MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
  540. }
  541. void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
  542. std::vector<u8> tmp_buffer(size);
  543. ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
  544. // The output block must be flushed in case it has data modified from the GPU.
  545. // Fixes NPC geometry in Zombie Panic in Wonderland DX
  546. FlushRegion(gpu_dest_addr, size);
  547. WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
  548. }
  549. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
  550. if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
  551. size_t page_index = gpu_addr >> big_page_bits;
  552. if (IsBigPageContinous(page_index)) [[likely]] {
  553. const std::size_t page{(page_index & big_page_mask) + size};
  554. return page <= big_page_size;
  555. }
  556. const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
  557. return page <= Core::Memory::YUZU_PAGESIZE;
  558. }
  559. if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
  560. return false;
  561. }
  562. const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
  563. return page <= Core::Memory::YUZU_PAGESIZE;
  564. }
  565. bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
  566. std::optional<VAddr> old_page_addr{};
  567. bool result{true};
  568. auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  569. std::size_t copy_amount) {
  570. result = false;
  571. return true;
  572. };
  573. auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  574. const VAddr cpu_addr_base =
  575. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  576. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  577. result = false;
  578. return true;
  579. }
  580. old_page_addr = {cpu_addr_base + copy_amount};
  581. return false;
  582. };
  583. auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  584. const VAddr cpu_addr_base =
  585. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  586. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  587. result = false;
  588. return true;
  589. }
  590. old_page_addr = {cpu_addr_base + copy_amount};
  591. return false;
  592. };
  593. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  594. std::size_t copy_amount) {
  595. GPUVAddr base = (page_index << big_page_bits) + offset;
  596. MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
  597. return !result;
  598. };
  599. MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
  600. return result;
  601. }
  602. bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
  603. bool result{true};
  604. auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  605. [[maybe_unused]] std::size_t copy_amount) {
  606. result = false;
  607. return true;
  608. };
  609. auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  610. [[maybe_unused]] std::size_t copy_amount) { return false; };
  611. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  612. std::size_t copy_amount) {
  613. GPUVAddr base = (page_index << big_page_bits) + offset;
  614. MemoryOperation<false>(base, copy_amount, pass, pass, fail);
  615. return !result;
  616. };
  617. MemoryOperation<true>(gpu_addr, size, pass, fail, check_short_pages);
  618. return result;
  619. }
  620. std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
  621. GPUVAddr gpu_addr, std::size_t size) const {
  622. std::vector<std::pair<GPUVAddr, std::size_t>> result{};
  623. std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
  624. std::optional<VAddr> old_page_addr{};
  625. const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
  626. [[maybe_unused]] std::size_t offset,
  627. [[maybe_unused]] std::size_t copy_amount) {
  628. if (last_segment) {
  629. result.push_back(*last_segment);
  630. last_segment = std::nullopt;
  631. }
  632. };
  633. const auto extend_size_big = [this, &split, &old_page_addr,
  634. &last_segment](std::size_t page_index, std::size_t offset,
  635. std::size_t copy_amount) {
  636. const VAddr cpu_addr_base =
  637. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  638. if (old_page_addr) {
  639. if (*old_page_addr != cpu_addr_base) {
  640. split(0, 0, 0);
  641. }
  642. }
  643. old_page_addr = {cpu_addr_base + copy_amount};
  644. if (!last_segment) {
  645. const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset;
  646. last_segment = {new_base_addr, copy_amount};
  647. } else {
  648. last_segment->second += copy_amount;
  649. }
  650. };
  651. const auto extend_size_short = [this, &split, &old_page_addr,
  652. &last_segment](std::size_t page_index, std::size_t offset,
  653. std::size_t copy_amount) {
  654. const VAddr cpu_addr_base =
  655. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  656. if (old_page_addr) {
  657. if (*old_page_addr != cpu_addr_base) {
  658. split(0, 0, 0);
  659. }
  660. }
  661. old_page_addr = {cpu_addr_base + copy_amount};
  662. if (!last_segment) {
  663. const GPUVAddr new_base_addr = (page_index << page_bits) + offset;
  664. last_segment = {new_base_addr, copy_amount};
  665. } else {
  666. last_segment->second += copy_amount;
  667. }
  668. };
  669. auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  670. GPUVAddr base = (page_index << big_page_bits) + offset;
  671. MemoryOperation<false>(base, copy_amount, extend_size_short, split, split);
  672. };
  673. MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
  674. split(0, 0, 0);
  675. return result;
  676. }
  677. } // namespace Tegra