memory_manager.cpp 34 KB

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