memory_manager.cpp 16 KB

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