memory_manager.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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 "core/core.h"
  7. #include "core/hle/kernel/memory/page_table.h"
  8. #include "core/hle/kernel/process.h"
  9. #include "core/memory.h"
  10. #include "video_core/gpu.h"
  11. #include "video_core/memory_manager.h"
  12. #include "video_core/rasterizer_interface.h"
  13. namespace Tegra {
  14. MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
  15. : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {}
  16. MemoryManager::~MemoryManager() = default;
  17. GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  18. u64 remaining_size{size};
  19. for (u64 offset{}; offset < size; offset += page_size) {
  20. if (remaining_size < page_size) {
  21. SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
  22. } else {
  23. SetPageEntry(gpu_addr + offset, page_entry + offset);
  24. }
  25. remaining_size -= page_size;
  26. }
  27. return gpu_addr;
  28. }
  29. GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
  30. return UpdateRange(gpu_addr, cpu_addr, size);
  31. }
  32. GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
  33. return Map(cpu_addr, *FindFreeRange(size, align), size);
  34. }
  35. void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
  36. if (!size) {
  37. return;
  38. }
  39. // Flush and invalidate through the GPU interface, to be asynchronous if possible.
  40. system.GPU().FlushAndInvalidateRegion(*GpuToCpuAddress(gpu_addr), size);
  41. UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
  42. }
  43. std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
  44. for (u64 offset{}; offset < size; offset += page_size) {
  45. if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
  46. return {};
  47. }
  48. }
  49. return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
  50. }
  51. GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
  52. return *AllocateFixed(*FindFreeRange(size, align), size);
  53. }
  54. void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
  55. if (!page_entry.IsValid()) {
  56. return;
  57. }
  58. ASSERT(system.CurrentProcess()
  59. ->PageTable()
  60. .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
  61. .IsSuccess());
  62. }
  63. void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
  64. if (!page_entry.IsValid()) {
  65. return;
  66. }
  67. ASSERT(system.CurrentProcess()
  68. ->PageTable()
  69. .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
  70. .IsSuccess());
  71. }
  72. PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
  73. return page_table[PageEntryIndex(gpu_addr)];
  74. }
  75. void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
  76. // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
  77. // improper tracking, but should be fixed in the future.
  78. //// Unlock the old page
  79. // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
  80. //// Lock the new page
  81. // TryLockPage(page_entry, size);
  82. page_table[PageEntryIndex(gpu_addr)] = page_entry;
  83. }
  84. std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const {
  85. if (!align) {
  86. align = page_size;
  87. } else {
  88. align = Common::AlignUp(align, page_size);
  89. }
  90. u64 available_size{};
  91. GPUVAddr gpu_addr{address_space_start};
  92. while (gpu_addr + available_size < address_space_size) {
  93. if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
  94. available_size += page_size;
  95. if (available_size >= size) {
  96. return gpu_addr;
  97. }
  98. } else {
  99. gpu_addr += available_size + page_size;
  100. available_size = 0;
  101. const auto remainder{gpu_addr % align};
  102. if (remainder) {
  103. gpu_addr = (gpu_addr - remainder) + align;
  104. }
  105. }
  106. }
  107. return {};
  108. }
  109. std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
  110. const auto page_entry{GetPageEntry(gpu_addr)};
  111. if (!page_entry.IsValid()) {
  112. return {};
  113. }
  114. return page_entry.ToAddress() + (gpu_addr & page_mask);
  115. }
  116. template <typename T>
  117. T MemoryManager::Read(GPUVAddr addr) const {
  118. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  119. // NOTE: Avoid adding any extra logic to this fast-path block
  120. T value;
  121. std::memcpy(&value, page_pointer, sizeof(T));
  122. return value;
  123. }
  124. UNREACHABLE();
  125. return {};
  126. }
  127. template <typename T>
  128. void MemoryManager::Write(GPUVAddr addr, T data) {
  129. if (auto page_pointer{GetPointer(addr)}; page_pointer) {
  130. // NOTE: Avoid adding any extra logic to this fast-path block
  131. std::memcpy(page_pointer, &data, sizeof(T));
  132. return;
  133. }
  134. UNREACHABLE();
  135. }
  136. template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
  137. template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
  138. template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
  139. template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
  140. template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
  141. template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
  142. template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
  143. template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
  144. u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
  145. if (!GetPageEntry(gpu_addr).IsValid()) {
  146. return {};
  147. }
  148. const auto address{GpuToCpuAddress(gpu_addr)};
  149. if (!address) {
  150. return {};
  151. }
  152. return system.Memory().GetPointer(*address);
  153. }
  154. const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
  155. if (!GetPageEntry(gpu_addr).IsValid()) {
  156. return {};
  157. }
  158. const auto address{GpuToCpuAddress(gpu_addr)};
  159. if (!address) {
  160. return {};
  161. }
  162. return system.Memory().GetPointer(*address);
  163. }
  164. void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
  165. std::size_t remaining_size{size};
  166. std::size_t page_index{gpu_src_addr >> page_bits};
  167. std::size_t page_offset{gpu_src_addr & page_mask};
  168. while (remaining_size > 0) {
  169. const std::size_t copy_amount{
  170. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  171. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  172. const auto src_addr{*page_addr + page_offset};
  173. // Flush must happen on the rasterizer interface, such that memory is always synchronous
  174. // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
  175. rasterizer.FlushRegion(src_addr, copy_amount);
  176. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  177. }
  178. page_index++;
  179. page_offset = 0;
  180. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  181. remaining_size -= copy_amount;
  182. }
  183. }
  184. void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
  185. const std::size_t size) const {
  186. std::size_t remaining_size{size};
  187. std::size_t page_index{gpu_src_addr >> page_bits};
  188. std::size_t page_offset{gpu_src_addr & page_mask};
  189. while (remaining_size > 0) {
  190. const std::size_t copy_amount{
  191. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  192. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  193. const auto src_addr{*page_addr + page_offset};
  194. system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
  195. } else {
  196. std::memset(dest_buffer, 0, copy_amount);
  197. }
  198. page_index++;
  199. page_offset = 0;
  200. dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
  201. remaining_size -= copy_amount;
  202. }
  203. }
  204. void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
  205. std::size_t remaining_size{size};
  206. std::size_t page_index{gpu_dest_addr >> page_bits};
  207. std::size_t page_offset{gpu_dest_addr & page_mask};
  208. while (remaining_size > 0) {
  209. const std::size_t copy_amount{
  210. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  211. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  212. const auto dest_addr{*page_addr + page_offset};
  213. // Invalidate must happen on the rasterizer interface, such that memory is always
  214. // synchronous when it is written (even when in asynchronous GPU mode).
  215. rasterizer.InvalidateRegion(dest_addr, copy_amount);
  216. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  217. }
  218. page_index++;
  219. page_offset = 0;
  220. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  221. remaining_size -= copy_amount;
  222. }
  223. }
  224. void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
  225. std::size_t size) {
  226. std::size_t remaining_size{size};
  227. std::size_t page_index{gpu_dest_addr >> page_bits};
  228. std::size_t page_offset{gpu_dest_addr & page_mask};
  229. while (remaining_size > 0) {
  230. const std::size_t copy_amount{
  231. std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
  232. if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
  233. const auto dest_addr{*page_addr + page_offset};
  234. system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
  235. }
  236. page_index++;
  237. page_offset = 0;
  238. src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
  239. remaining_size -= copy_amount;
  240. }
  241. }
  242. void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
  243. std::vector<u8> tmp_buffer(size);
  244. ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
  245. WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
  246. }
  247. void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
  248. std::size_t size) {
  249. std::vector<u8> tmp_buffer(size);
  250. ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size);
  251. WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
  252. }
  253. bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
  254. const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
  255. if (!cpu_addr) {
  256. return {};
  257. }
  258. const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
  259. return page <= Core::Memory::PAGE_SIZE;
  260. }
  261. } // namespace Tegra