memory_manager.cpp 33 KB

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