memory_manager.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  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 "video_core/invalidation_accumulator.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. using Core::Memory::GuestMemoryFlags;
  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, current_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, current_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. Core::Memory::GpuGuestMemoryScoped<u8, GuestMemoryFlags::SafeReadWrite> data(
  536. *this, gpu_src_addr, size);
  537. data.SetAddressAndSize(gpu_dest_addr, size);
  538. FlushRegion(gpu_dest_addr, size, which);
  539. }
  540. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
  541. if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
  542. size_t page_index = gpu_addr >> big_page_bits;
  543. if (IsBigPageContinuous(page_index)) [[likely]] {
  544. const std::size_t page{(page_index & big_page_mask) + size};
  545. return page <= big_page_size;
  546. }
  547. const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
  548. return page <= Core::Memory::YUZU_PAGESIZE;
  549. }
  550. if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
  551. return false;
  552. }
  553. const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
  554. return page <= Core::Memory::YUZU_PAGESIZE;
  555. }
  556. bool MemoryManager::IsContinuousRange(GPUVAddr gpu_addr, std::size_t size) const {
  557. std::optional<VAddr> old_page_addr{};
  558. bool result{true};
  559. auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  560. std::size_t copy_amount) {
  561. result = false;
  562. return true;
  563. };
  564. auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  565. const VAddr cpu_addr_base =
  566. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  567. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  568. result = false;
  569. return true;
  570. }
  571. old_page_addr = {cpu_addr_base + copy_amount};
  572. return false;
  573. };
  574. auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  575. const VAddr cpu_addr_base =
  576. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  577. if (old_page_addr && *old_page_addr != cpu_addr_base) {
  578. result = false;
  579. return true;
  580. }
  581. old_page_addr = {cpu_addr_base + copy_amount};
  582. return false;
  583. };
  584. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  585. std::size_t copy_amount) {
  586. GPUVAddr base = (page_index << big_page_bits) + offset;
  587. MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
  588. return !result;
  589. };
  590. MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
  591. return result;
  592. }
  593. bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
  594. bool result{true};
  595. auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  596. [[maybe_unused]] std::size_t copy_amount) {
  597. result = false;
  598. return true;
  599. };
  600. auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
  601. [[maybe_unused]] std::size_t copy_amount) { return false; };
  602. auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
  603. std::size_t copy_amount) {
  604. GPUVAddr base = (page_index << big_page_bits) + offset;
  605. MemoryOperation<false>(base, copy_amount, pass, pass, fail);
  606. return !result;
  607. };
  608. MemoryOperation<true>(gpu_addr, size, pass, fail, check_short_pages);
  609. return result;
  610. }
  611. boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32>
  612. MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const {
  613. boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> result{};
  614. GetSubmappedRangeImpl<true>(gpu_addr, size, result);
  615. return result;
  616. }
  617. template <bool is_gpu_address>
  618. void MemoryManager::GetSubmappedRangeImpl(
  619. GPUVAddr gpu_addr, std::size_t size,
  620. boost::container::small_vector<
  621. std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result)
  622. const {
  623. std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>
  624. last_segment{};
  625. std::optional<VAddr> old_page_addr{};
  626. const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
  627. [[maybe_unused]] std::size_t offset,
  628. [[maybe_unused]] std::size_t copy_amount) {
  629. if (last_segment) {
  630. result.push_back(*last_segment);
  631. last_segment = std::nullopt;
  632. }
  633. };
  634. const auto extend_size_big = [this, &split, &old_page_addr,
  635. &last_segment](std::size_t page_index, std::size_t offset,
  636. std::size_t copy_amount) {
  637. const VAddr cpu_addr_base =
  638. (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
  639. if (old_page_addr) {
  640. if (*old_page_addr != cpu_addr_base) {
  641. split(0, 0, 0);
  642. }
  643. }
  644. old_page_addr = {cpu_addr_base + copy_amount};
  645. if (!last_segment) {
  646. if constexpr (is_gpu_address) {
  647. const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset;
  648. last_segment = {new_base_addr, copy_amount};
  649. } else {
  650. last_segment = {cpu_addr_base, copy_amount};
  651. }
  652. } else {
  653. last_segment->second += copy_amount;
  654. }
  655. };
  656. const auto extend_size_short = [this, &split, &old_page_addr,
  657. &last_segment](std::size_t page_index, std::size_t offset,
  658. std::size_t copy_amount) {
  659. const VAddr cpu_addr_base =
  660. (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
  661. if (old_page_addr) {
  662. if (*old_page_addr != cpu_addr_base) {
  663. split(0, 0, 0);
  664. }
  665. }
  666. old_page_addr = {cpu_addr_base + copy_amount};
  667. if (!last_segment) {
  668. if constexpr (is_gpu_address) {
  669. const GPUVAddr new_base_addr = (page_index << page_bits) + offset;
  670. last_segment = {new_base_addr, copy_amount};
  671. } else {
  672. last_segment = {cpu_addr_base, copy_amount};
  673. }
  674. } else {
  675. last_segment->second += copy_amount;
  676. }
  677. };
  678. auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
  679. GPUVAddr base = (page_index << big_page_bits) + offset;
  680. MemoryOperation<false>(base, copy_amount, extend_size_short, split, split);
  681. };
  682. MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
  683. split(0, 0, 0);
  684. }
  685. void MemoryManager::FlushCaching() {
  686. if (!accumulator->AnyAccumulated()) {
  687. return;
  688. }
  689. accumulator->Callback([this](GPUVAddr addr, size_t size) {
  690. GetSubmappedRangeImpl<false>(addr, size, page_stash2);
  691. });
  692. rasterizer->InnerInvalidation(page_stash2);
  693. page_stash2.clear();
  694. accumulator->Clear();
  695. }
  696. const u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) const {
  697. auto cpu_addr = GpuToCpuAddress(src_addr);
  698. if (cpu_addr) {
  699. return memory.GetSpan(*cpu_addr, size);
  700. }
  701. return nullptr;
  702. }
  703. u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) {
  704. if (!IsContinuousRange(src_addr, size)) {
  705. return nullptr;
  706. }
  707. auto cpu_addr = GpuToCpuAddress(src_addr);
  708. if (cpu_addr) {
  709. return memory.GetSpan(*cpu_addr, size);
  710. }
  711. return nullptr;
  712. }
  713. } // namespace Tegra