memory_manager.cpp 33 KB

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