memory_manager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include "common/alignment.h"
  6. #include "common/assert.h"
  7. #include "common/logging/log.h"
  8. #include "core/core.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/gpu.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. MemoryManager::MemoryManager(Core::System& system_)
  18. : system{system_}, page_table(page_table_size) {}
  19. MemoryManager::~MemoryManager() = default;
  20. void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  21. rasterizer = rasterizer_;
  22. }
  23. GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  24. u64 remaining_size{size};
  25. for (u64 offset{}; offset < size; offset += page_size) {
  26. if (remaining_size < page_size) {
  27. SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
  28. } else {
  29. SetPageEntry(gpu_addr + offset, page_entry + offset);
  30. }
  31. remaining_size -= page_size;
  32. }
  33. return gpu_addr;
  34. }
  35. GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
  36. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  37. if (it != map_ranges.end() && it->first == gpu_addr) {
  38. it->second = size;
  39. } else {
  40. map_ranges.insert(it, MapRange{gpu_addr, size});
  41. }
  42. return UpdateRange(gpu_addr, cpu_addr, size);
  43. }
  44. GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
  45. return Map(cpu_addr, *FindFreeRange(size, align), size);
  46. }
  47. GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
  48. const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true);
  49. ASSERT(gpu_addr);
  50. return Map(cpu_addr, *gpu_addr, size);
  51. }
  52. void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
  53. if (size == 0) {
  54. return;
  55. }
  56. const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  57. if (it != map_ranges.end()) {
  58. ASSERT(it->first == gpu_addr);
  59. map_ranges.erase(it);
  60. } else {
  61. UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
  62. }
  63. const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
  64. for (const auto& map : submapped_ranges) {
  65. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
  66. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map.first);
  67. ASSERT(cpu_addr);
  68. rasterizer->UnmapMemory(*cpu_addr, map.second);
  69. }
  70. UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
  71. }
  72. std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
  73. for (u64 offset{}; offset < size; offset += page_size) {
  74. if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
  75. return std::nullopt;
  76. }
  77. }
  78. return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
  79. }
  80. GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
  81. return *AllocateFixed(*FindFreeRange(size, align), size);
  82. }
  83. void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
  84. if (!page_entry.IsValid()) {
  85. return;
  86. }
  87. ASSERT(system.CurrentProcess()
  88. ->PageTable()
  89. .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
  90. .IsSuccess());
  91. }
  92. void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
  93. if (!page_entry.IsValid()) {
  94. return;
  95. }
  96. ASSERT(system.CurrentProcess()
  97. ->PageTable()
  98. .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
  99. .IsSuccess());
  100. }
  101. PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
  102. return page_table[PageEntryIndex(gpu_addr)];
  103. }
  104. void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  105. // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
  106. // improper tracking, but should be fixed in the future.
  107. //// Unlock the old page
  108. // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
  109. //// Lock the new page
  110. // TryLockPage(page_entry, size);
  111. auto& current_page = page_table[PageEntryIndex(gpu_addr)];
  112. if ((!current_page.IsValid() && page_entry.IsValid()) ||
  113. current_page.ToAddress() != page_entry.ToAddress()) {
  114. rasterizer->ModifyGPUMemory(gpu_addr, size);
  115. }
  116. current_page = page_entry;
  117. }
  118. std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
  119. bool start_32bit_address) const {
  120. if (!align) {
  121. align = page_size;
  122. } else {
  123. align = Common::AlignUp(align, page_size);
  124. }
  125. u64 available_size{};
  126. GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start};
  127. while (gpu_addr + available_size < address_space_size) {
  128. if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
  129. available_size += page_size;
  130. if (available_size >= size) {
  131. return gpu_addr;
  132. }
  133. } else {
  134. gpu_addr += available_size + page_size;
  135. available_size = 0;
  136. const auto remainder{gpu_addr % align};
  137. if (remainder) {
  138. gpu_addr = (gpu_addr - remainder) + align;
  139. }
  140. }
  141. }
  142. return std::nullopt;
  143. }
  144. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
  145. if (gpu_addr == 0) {
  146. return std::nullopt;
  147. }
  148. const auto page_entry{GetPageEntry(gpu_addr)};
  149. if (!page_entry.IsValid()) {
  150. return std::nullopt;
  151. }
  152. return page_entry.ToAddress() + (gpu_addr & page_mask);
  153. }
  154. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const {
  155. size_t page_index{addr >> page_bits};
  156. const size_t page_last{(addr + size + page_size - 1) >> page_bits};
  157. while (page_index < page_last) {
  158. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  159. if (page_addr && *page_addr != 0) {
  160. return page_addr;
  161. }
  162. ++page_index;
  163. }
  164. return std::nullopt;
  165. }
  166. template <typename T>
  167. T MemoryManager::Read(GPUVAddr addr) const {
  168. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  169. // NOTE: Avoid adding any extra logic to this fast-path block
  170. T value;
  171. std::memcpy(&value, page_pointer, sizeof(T));
  172. return value;
  173. }
  174. UNREACHABLE();
  175. return {};
  176. }
  177. template <typename T>
  178. void MemoryManager::Write(GPUVAddr addr, T data) {
  179. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  180. // NOTE: Avoid adding any extra logic to this fast-path block
  181. std::memcpy(page_pointer, &data, sizeof(T));
  182. return;
  183. }
  184. UNREACHABLE();
  185. }
  186. template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
  187. template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
  188. template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
  189. template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
  190. template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
  191. template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
  192. template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
  193. template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
  194. u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
  195. if (!GetPageEntry(gpu_addr).IsValid()) {
  196. return {};
  197. }
  198. const auto address{GpuToCpuAddress(gpu_addr)};
  199. if (!address) {
  200. return {};
  201. }
  202. return system.Memory().GetPointer(*address);
  203. }
  204. const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
  205. if (!GetPageEntry(gpu_addr).IsValid()) {
  206. return {};
  207. }
  208. const auto address{GpuToCpuAddress(gpu_addr)};
  209. if (!address) {
  210. return {};
  211. }
  212. return system.Memory().GetPointer(*address);
  213. }
  214. size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
  215. auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
  216. --it;
  217. return it->second - (gpu_addr - it->first);
  218. }
  219. void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
  220. std::size_t remaining_size{size};
  221. std::size_t page_index{gpu_src_addr >> page_bits};
  222. std::size_t page_offset{gpu_src_addr & page_mask};
  223. while (remaining_size > 0) {
  224. const std::size_t copy_amount{
  225. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  226. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  227. const auto src_addr{*page_addr + page_offset};
  228. // Flush must happen on the rasterizer interface, such that memory is always synchronous
  229. // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
  230. rasterizer->FlushRegion(src_addr, copy_amount);
  231. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  232. }
  233. page_index++;
  234. page_offset = 0;
  235. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  236. remaining_size -= copy_amount;
  237. }
  238. }
  239. void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
  240. const std::size_t size) const {
  241. std::size_t remaining_size{size};
  242. std::size_t page_index{gpu_src_addr >> page_bits};
  243. std::size_t page_offset{gpu_src_addr & page_mask};
  244. while (remaining_size > 0) {
  245. const std::size_t copy_amount{
  246. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  247. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  248. const auto src_addr{*page_addr + page_offset};
  249. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  250. } else {
  251. std::memset(dest_buffer, 0, copy_amount);
  252. }
  253. page_index++;
  254. page_offset = 0;
  255. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  256. remaining_size -= copy_amount;
  257. }
  258. }
  259. void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
  260. std::size_t remaining_size{size};
  261. std::size_t page_index{gpu_dest_addr >> page_bits};
  262. std::size_t page_offset{gpu_dest_addr & page_mask};
  263. while (remaining_size > 0) {
  264. const std::size_t copy_amount{
  265. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  266. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  267. const auto dest_addr{*page_addr + page_offset};
  268. // Invalidate must happen on the rasterizer interface, such that memory is always
  269. // synchronous when it is written (even when in asynchronous GPU mode).
  270. rasterizer->InvalidateRegion(dest_addr, copy_amount);
  271. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  272. }
  273. page_index++;
  274. page_offset = 0;
  275. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  276. remaining_size -= copy_amount;
  277. }
  278. }
  279. void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
  280. std::size_t size) {
  281. std::size_t remaining_size{size};
  282. std::size_t page_index{gpu_dest_addr >> page_bits};
  283. std::size_t page_offset{gpu_dest_addr & page_mask};
  284. while (remaining_size > 0) {
  285. const std::size_t copy_amount{
  286. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  287. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  288. const auto dest_addr{*page_addr + page_offset};
  289. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  290. }
  291. page_index++;
  292. page_offset = 0;
  293. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  294. remaining_size -= copy_amount;
  295. }
  296. }
  297. void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
  298. size_t remaining_size{size};
  299. size_t page_index{gpu_addr >> page_bits};
  300. size_t page_offset{gpu_addr & page_mask};
  301. while (remaining_size > 0) {
  302. const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
  303. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  304. rasterizer->FlushRegion(*page_addr + page_offset, num_bytes);
  305. }
  306. ++page_index;
  307. page_offset = 0;
  308. remaining_size -= num_bytes;
  309. }
  310. }
  311. void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
  312. std::vector<u8> tmp_buffer(size);
  313. ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
  314. // The output block must be flushed in case it has data modified from the GPU.
  315. // Fixes NPC geometry in Zombie Panic in Wonderland DX
  316. FlushRegion(gpu_dest_addr, size);
  317. WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
  318. }
  319. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
  320. const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
  321. if (!cpu_addr) {
  322. return false;
  323. }
  324. const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
  325. return page <= Core::Memory::PAGE_SIZE;
  326. }
  327. bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
  328. size_t page_index{gpu_addr >> page_bits};
  329. const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
  330. std::optional<VAddr> old_page_addr{};
  331. while (page_index != page_last) {
  332. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  333. if (!page_addr || *page_addr == 0) {
  334. return false;
  335. }
  336. if (old_page_addr) {
  337. if (*old_page_addr + page_size != *page_addr) {
  338. return false;
  339. }
  340. }
  341. old_page_addr = page_addr;
  342. ++page_index;
  343. }
  344. return true;
  345. }
  346. bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
  347. size_t page_index{gpu_addr >> page_bits};
  348. const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
  349. while (page_index < page_last) {
  350. if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) {
  351. return false;
  352. }
  353. ++page_index;
  354. }
  355. return true;
  356. }
  357. std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
  358. GPUVAddr gpu_addr, std::size_t size) const {
  359. std::vector<std::pair<GPUVAddr, std::size_t>> result{};
  360. size_t page_index{gpu_addr >> page_bits};
  361. size_t remaining_size{size};
  362. size_t page_offset{gpu_addr & page_mask};
  363. std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
  364. std::optional<VAddr> old_page_addr{};
  365. const auto extend_size = [this, &last_segment, &page_index](std::size_t bytes) {
  366. if (!last_segment) {
  367. GPUVAddr new_base_addr = page_index << page_bits;
  368. last_segment = {new_base_addr, bytes};
  369. } else {
  370. last_segment->second += bytes;
  371. }
  372. };
  373. const auto split = [this, &last_segment, &result] {
  374. if (last_segment) {
  375. result.push_back(*last_segment);
  376. last_segment = std::nullopt;
  377. }
  378. };
  379. while (remaining_size > 0) {
  380. const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
  381. const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
  382. if (!page_addr) {
  383. split();
  384. } else if (old_page_addr) {
  385. if (*old_page_addr + page_size != *page_addr) {
  386. split();
  387. }
  388. extend_size(num_bytes);
  389. } else {
  390. extend_size(num_bytes);
  391. }
  392. ++page_index;
  393. page_offset = 0;
  394. remaining_size -= num_bytes;
  395. old_page_addr = page_addr;
  396. }
  397. split();
  398. return result;
  399. }
  400. } // namespace Tegra