diff options
Diffstat (limited to 'src')
28 files changed, 550 insertions, 257 deletions
diff --git a/src/common/common_types.h b/src/common/common_types.h index 6b1766dca..4cec89fbd 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -40,10 +40,9 @@ using s64 = std::int64_t; ///< 64-bit signed int | |||
| 40 | using f32 = float; ///< 32-bit floating point | 40 | using f32 = float; ///< 32-bit floating point |
| 41 | using f64 = double; ///< 64-bit floating point | 41 | using f64 = double; ///< 64-bit floating point |
| 42 | 42 | ||
| 43 | // TODO: It would be nice to eventually replace these with strong types that prevent accidental | 43 | using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. |
| 44 | // conversion between each other. | 44 | using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space. |
| 45 | using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. | 45 | using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space. |
| 46 | using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space. | ||
| 47 | 46 | ||
| 48 | using u128 = std::array<std::uint64_t, 2>; | 47 | using u128 = std::array<std::uint64_t, 2>; |
| 49 | static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); | 48 | static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); |
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 8eba1c3f1..69b7abc54 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp | |||
| @@ -16,6 +16,7 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) { | |||
| 16 | 16 | ||
| 17 | pointers.resize(num_page_table_entries); | 17 | pointers.resize(num_page_table_entries); |
| 18 | attributes.resize(num_page_table_entries); | 18 | attributes.resize(num_page_table_entries); |
| 19 | backing_addr.resize(num_page_table_entries); | ||
| 19 | 20 | ||
| 20 | // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the | 21 | // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the |
| 21 | // vector size is subsequently decreased (via resize), the vector might not automatically | 22 | // vector size is subsequently decreased (via resize), the vector might not automatically |
| @@ -24,6 +25,7 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) { | |||
| 24 | 25 | ||
| 25 | pointers.shrink_to_fit(); | 26 | pointers.shrink_to_fit(); |
| 26 | attributes.shrink_to_fit(); | 27 | attributes.shrink_to_fit(); |
| 28 | backing_addr.shrink_to_fit(); | ||
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | } // namespace Common | 31 | } // namespace Common |
diff --git a/src/common/page_table.h b/src/common/page_table.h index 8339f2890..8b8ff0bb8 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -21,6 +21,8 @@ enum class PageType : u8 { | |||
| 21 | RasterizerCachedMemory, | 21 | RasterizerCachedMemory, |
| 22 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. | 22 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. |
| 23 | Special, | 23 | Special, |
| 24 | /// Page is allocated for use. | ||
| 25 | Allocated, | ||
| 24 | }; | 26 | }; |
| 25 | 27 | ||
| 26 | struct SpecialRegion { | 28 | struct SpecialRegion { |
| @@ -66,7 +68,7 @@ struct PageTable { | |||
| 66 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is | 68 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is |
| 67 | * of type `Special`. | 69 | * of type `Special`. |
| 68 | */ | 70 | */ |
| 69 | boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions; | 71 | boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions; |
| 70 | 72 | ||
| 71 | /** | 73 | /** |
| 72 | * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then | 74 | * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then |
| @@ -74,6 +76,8 @@ struct PageTable { | |||
| 74 | */ | 76 | */ |
| 75 | std::vector<PageType> attributes; | 77 | std::vector<PageType> attributes; |
| 76 | 78 | ||
| 79 | std::vector<u64> backing_addr; | ||
| 80 | |||
| 77 | const std::size_t page_size_in_bits{}; | 81 | const std::size_t page_size_in_bits{}; |
| 78 | }; | 82 | }; |
| 79 | 83 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index b031ebc66..af62d33d2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -89,7 +89,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 89 | for (const auto& entry : entries) { | 89 | for (const auto& entry : entries) { |
| 90 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", | 90 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", |
| 91 | entry.offset, entry.nvmap_handle, entry.pages); | 91 | entry.offset, entry.nvmap_handle, entry.pages); |
| 92 | Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; | 92 | GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10; |
| 93 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); | 93 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); |
| 94 | if (!object) { | 94 | if (!object) { |
| 95 | LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); | 95 | LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); |
| @@ -102,7 +102,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 102 | u64 size = static_cast<u64>(entry.pages) << 0x10; | 102 | u64 size = static_cast<u64>(entry.pages) << 0x10; |
| 103 | ASSERT(size <= object->size); | 103 | ASSERT(size <= object->size); |
| 104 | 104 | ||
| 105 | Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); | 105 | GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); |
| 106 | ASSERT(returned == offset); | 106 | ASSERT(returned == offset); |
| 107 | } | 107 | } |
| 108 | std::memcpy(output.data(), entries.data(), output.size()); | 108 | std::memcpy(output.data(), entries.data(), output.size()); |
| @@ -173,16 +173,8 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 173 | return 0; | 173 | return 0; |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | auto& system_instance = Core::System::GetInstance(); | 176 | params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset, |
| 177 | 177 | itr->second.size); | |
| 178 | // Remove this memory region from the rasterizer cache. | ||
| 179 | auto& gpu = system_instance.GPU(); | ||
| 180 | auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); | ||
| 181 | ASSERT(cpu_addr); | ||
| 182 | gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size); | ||
| 183 | |||
| 184 | params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); | ||
| 185 | |||
| 186 | buffer_mappings.erase(itr->second.offset); | 178 | buffer_mappings.erase(itr->second.offset); |
| 187 | 179 | ||
| 188 | std::memcpy(output.data(), ¶ms, output.size()); | 180 | std::memcpy(output.data(), ¶ms, output.size()); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 365ac82b4..332c1037c 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -48,7 +48,7 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me | |||
| 48 | (base + size) * PAGE_SIZE); | 48 | (base + size) * PAGE_SIZE); |
| 49 | 49 | ||
| 50 | // During boot, current_page_table might not be set yet, in which case we need not flush | 50 | // During boot, current_page_table might not be set yet, in which case we need not flush |
| 51 | if (current_page_table) { | 51 | if (Core::System::GetInstance().IsPoweredOn()) { |
| 52 | Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, | 52 | Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, |
| 53 | size * PAGE_SIZE); | 53 | size * PAGE_SIZE); |
| 54 | } | 54 | } |
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 27a36348c..6ab06518f 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | 12 | ||
| 14 | namespace Tegra { | 13 | namespace Tegra { |
| 15 | 14 | ||
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 0931b9626..e259bf46b 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -46,7 +46,7 @@ void KeplerMemory::ProcessData(u32 data) { | |||
| 46 | // contain a dirty surface that will have to be written back to memory. | 46 | // contain a dirty surface that will have to be written back to memory. |
| 47 | const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)}; | 47 | const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)}; |
| 48 | rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32)); | 48 | rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32)); |
| 49 | memory_manager.Write32(address, data); | 49 | memory_manager.Write<u32>(address, data); |
| 50 | 50 | ||
| 51 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | 51 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); |
| 52 | 52 | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index c5d5be4ef..defcfbd3f 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -307,7 +307,7 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 307 | // Write the current query sequence to the sequence address. | 307 | // Write the current query sequence to the sequence address. |
| 308 | // TODO(Subv): Find out what happens if you use a long query type but mark it as a short | 308 | // TODO(Subv): Find out what happens if you use a long query type but mark it as a short |
| 309 | // query. | 309 | // query. |
| 310 | memory_manager.Write32(sequence_address, sequence); | 310 | memory_manager.Write<u32>(sequence_address, sequence); |
| 311 | } else { | 311 | } else { |
| 312 | // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast | 312 | // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast |
| 313 | // GPU, this command may actually take a while to complete in real hardware due to GPU | 313 | // GPU, this command may actually take a while to complete in real hardware due to GPU |
| @@ -395,7 +395,7 @@ void Maxwell3D::ProcessCBData(u32 value) { | |||
| 395 | 395 | ||
| 396 | u8* ptr{memory_manager.GetPointer(address)}; | 396 | u8* ptr{memory_manager.GetPointer(address)}; |
| 397 | rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32)); | 397 | rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32)); |
| 398 | memory_manager.Write32(address, value); | 398 | memory_manager.Write<u32>(address, value); |
| 399 | 399 | ||
| 400 | dirty_flags.OnMemoryWrite(); | 400 | dirty_flags.OnMemoryWrite(); |
| 401 | 401 | ||
| @@ -447,7 +447,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt | |||
| 447 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; | 447 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; |
| 448 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { | 448 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { |
| 449 | 449 | ||
| 450 | const Texture::TextureHandle tex_handle{memory_manager.Read32(current_texture)}; | 450 | const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(current_texture)}; |
| 451 | 451 | ||
| 452 | Texture::FullTextureInfo tex_info{}; | 452 | Texture::FullTextureInfo tex_info{}; |
| 453 | // TODO(Subv): Use the shader to determine which textures are actually accessed. | 453 | // TODO(Subv): Use the shader to determine which textures are actually accessed. |
| @@ -482,7 +482,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, | |||
| 482 | 482 | ||
| 483 | ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); | 483 | ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); |
| 484 | 484 | ||
| 485 | const Texture::TextureHandle tex_handle{memory_manager.Read32(tex_info_address)}; | 485 | const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)}; |
| 486 | 486 | ||
| 487 | Texture::FullTextureInfo tex_info{}; | 487 | Texture::FullTextureInfo tex_info{}; |
| 488 | tex_info.index = static_cast<u32>(offset); | 488 | tex_info.index = static_cast<u32>(offset); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a0ded4c25..5cca5c29a 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -88,6 +88,16 @@ void MaxwellDMA::HandleCopy() { | |||
| 88 | auto source_ptr{memory_manager.GetPointer(source)}; | 88 | auto source_ptr{memory_manager.GetPointer(source)}; |
| 89 | auto dst_ptr{memory_manager.GetPointer(dest)}; | 89 | auto dst_ptr{memory_manager.GetPointer(dest)}; |
| 90 | 90 | ||
| 91 | if (!source_ptr) { | ||
| 92 | LOG_ERROR(HW_GPU, "source_ptr is invalid"); | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | |||
| 96 | if (!dst_ptr) { | ||
| 97 | LOG_ERROR(HW_GPU, "dst_ptr is invalid"); | ||
| 98 | return; | ||
| 99 | } | ||
| 100 | |||
| 91 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { | 101 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { |
| 92 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated | 102 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated |
| 93 | // copying. | 103 | // copying. |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 66c690494..267a03f2d 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 13 | #include "video_core/engines/maxwell_dma.h" | 13 | #include "video_core/engines/maxwell_dma.h" |
| 14 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 15 | #include "video_core/memory_manager.h" | ||
| 15 | #include "video_core/renderer_base.h" | 16 | #include "video_core/renderer_base.h" |
| 16 | 17 | ||
| 17 | namespace Tegra { | 18 | namespace Tegra { |
| @@ -287,7 +288,7 @@ void GPU::ProcessSemaphoreTriggerMethod() { | |||
| 287 | block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); | 288 | block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); |
| 288 | memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block)); | 289 | memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block)); |
| 289 | } else { | 290 | } else { |
| 290 | const u32 word{memory_manager->Read32(regs.smaphore_address.SmaphoreAddress())}; | 291 | const u32 word{memory_manager->Read<u32>(regs.smaphore_address.SmaphoreAddress())}; |
| 291 | if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || | 292 | if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || |
| 292 | (op == GpuSemaphoreOperation::AcquireGequal && | 293 | (op == GpuSemaphoreOperation::AcquireGequal && |
| 293 | static_cast<s32>(word - regs.semaphore_sequence) > 0) || | 294 | static_cast<s32>(word - regs.semaphore_sequence) > 0) || |
| @@ -314,11 +315,11 @@ void GPU::ProcessSemaphoreTriggerMethod() { | |||
| 314 | } | 315 | } |
| 315 | 316 | ||
| 316 | void GPU::ProcessSemaphoreRelease() { | 317 | void GPU::ProcessSemaphoreRelease() { |
| 317 | memory_manager->Write32(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release); | 318 | memory_manager->Write<u32>(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release); |
| 318 | } | 319 | } |
| 319 | 320 | ||
| 320 | void GPU::ProcessSemaphoreAcquire() { | 321 | void GPU::ProcessSemaphoreAcquire() { |
| 321 | const u32 word = memory_manager->Read32(regs.smaphore_address.SmaphoreAddress()); | 322 | const u32 word = memory_manager->Read<u32>(regs.smaphore_address.SmaphoreAddress()); |
| 322 | const auto value = regs.semaphore_acquire; | 323 | const auto value = regs.semaphore_acquire; |
| 323 | if (word != value) { | 324 | if (word != value) { |
| 324 | regs.acquire_active = true; | 325 | regs.acquire_active = true; |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index a14b95c30..c1830ac8d 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/service/nvflinger/buffer_queue.h" | 10 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 11 | #include "video_core/dma_pusher.h" | 11 | #include "video_core/dma_pusher.h" |
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | 12 | ||
| 14 | using CacheAddr = std::uintptr_t; | 13 | using CacheAddr = std::uintptr_t; |
| 15 | inline CacheAddr ToCacheAddr(const void* host_ptr) { | 14 | inline CacheAddr ToCacheAddr(const void* host_ptr) { |
| @@ -124,6 +123,8 @@ enum class EngineID { | |||
| 124 | MAXWELL_DMA_COPY_A = 0xB0B5, | 123 | MAXWELL_DMA_COPY_A = 0xB0B5, |
| 125 | }; | 124 | }; |
| 126 | 125 | ||
| 126 | class MemoryManager; | ||
| 127 | |||
| 127 | class GPU { | 128 | class GPU { |
| 128 | public: | 129 | public: |
| 129 | explicit GPU(Core::System& system, VideoCore::RendererBase& renderer); | 130 | explicit GPU(Core::System& system, VideoCore::RendererBase& renderer); |
| @@ -244,9 +245,8 @@ protected: | |||
| 244 | private: | 245 | private: |
| 245 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 246 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
| 246 | 247 | ||
| 247 | /// Mapping of command subchannels to their bound engine ids. | 248 | /// Mapping of command subchannels to their bound engine ids |
| 248 | std::array<EngineID, 8> bound_engines = {}; | 249 | std::array<EngineID, 8> bound_engines = {}; |
| 249 | |||
| 250 | /// 3D engine | 250 | /// 3D engine |
| 251 | std::unique_ptr<Engines::Maxwell3D> maxwell_3d; | 251 | std::unique_ptr<Engines::Maxwell3D> maxwell_3d; |
| 252 | /// 2D engine | 252 | /// 2D engine |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 8e8f36f28..e76b59842 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -5,218 +5,446 @@ | |||
| 5 | #include "common/alignment.h" | 5 | #include "common/alignment.h" |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | ||
| 8 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| 10 | #include "video_core/gpu.h" | ||
| 9 | #include "video_core/memory_manager.h" | 11 | #include "video_core/memory_manager.h" |
| 12 | #include "video_core/rasterizer_interface.h" | ||
| 13 | #include "video_core/renderer_base.h" | ||
| 10 | 14 | ||
| 11 | namespace Tegra { | 15 | namespace Tegra { |
| 12 | 16 | ||
| 13 | MemoryManager::MemoryManager() { | 17 | MemoryManager::MemoryManager() { |
| 14 | // Mark the first page as reserved, so that 0 is not a valid GPUVAddr. Otherwise, games might | 18 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); |
| 15 | // try to use 0 as a valid address, which is also used to mean nullptr. This fixes a bug with | 19 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), |
| 16 | // Undertale using 0 for a render target. | 20 | Common::PageType::Unmapped); |
| 17 | PageSlot(0) = static_cast<u64>(PageStatus::Reserved); | 21 | page_table.Resize(address_space_width); |
| 18 | } | ||
| 19 | |||
| 20 | GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { | ||
| 21 | const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)}; | ||
| 22 | 22 | ||
| 23 | ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); | 23 | // Initialize the map with a single free region covering the entire managed space. |
| 24 | VirtualMemoryArea initial_vma; | ||
| 25 | initial_vma.size = address_space_end; | ||
| 26 | vma_map.emplace(initial_vma.base, initial_vma); | ||
| 24 | 27 | ||
| 25 | for (u64 offset{}; offset < size; offset += PAGE_SIZE) { | 28 | UpdatePageTableForVMA(initial_vma); |
| 26 | VAddr& slot{PageSlot(*gpu_addr + offset)}; | 29 | } |
| 27 | 30 | ||
| 28 | ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); | 31 | GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { |
| 32 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | ||
| 33 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | ||
| 29 | 34 | ||
| 30 | slot = static_cast<u64>(PageStatus::Allocated); | 35 | AllocateMemory(gpu_addr, 0, aligned_size); |
| 31 | } | ||
| 32 | 36 | ||
| 33 | return *gpu_addr; | 37 | return gpu_addr; |
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { | 40 | GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { |
| 37 | for (u64 offset{}; offset < size; offset += PAGE_SIZE) { | 41 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 38 | VAddr& slot{PageSlot(gpu_addr + offset)}; | ||
| 39 | 42 | ||
| 40 | ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); | 43 | AllocateMemory(gpu_addr, 0, aligned_size); |
| 41 | |||
| 42 | slot = static_cast<u64>(PageStatus::Allocated); | ||
| 43 | } | ||
| 44 | 44 | ||
| 45 | return gpu_addr; | 45 | return gpu_addr; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { | 48 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { |
| 49 | const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, PAGE_SIZE, PageStatus::Unmapped)}; | 49 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 50 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | ||
| 50 | 51 | ||
| 51 | ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); | 52 | MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); |
| 52 | 53 | ||
| 53 | for (u64 offset{}; offset < size; offset += PAGE_SIZE) { | 54 | return gpu_addr; |
| 54 | VAddr& slot{PageSlot(*gpu_addr + offset)}; | 55 | } |
| 55 | 56 | ||
| 56 | ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); | 57 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { |
| 58 | ASSERT((gpu_addr & page_mask) == 0); | ||
| 57 | 59 | ||
| 58 | slot = cpu_addr + offset; | 60 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 59 | } | ||
| 60 | 61 | ||
| 61 | const MappedRegion region{cpu_addr, *gpu_addr, size}; | 62 | MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); |
| 62 | mapped_regions.push_back(region); | ||
| 63 | 63 | ||
| 64 | return *gpu_addr; | 64 | return gpu_addr; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { | 67 | GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { |
| 68 | ASSERT((gpu_addr & PAGE_MASK) == 0); | 68 | ASSERT((gpu_addr & page_mask) == 0); |
| 69 | 69 | ||
| 70 | if (PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Allocated)) { | 70 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 71 | // Page has been already mapped. In this case, we must find a new area of memory to use that | 71 | const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; |
| 72 | // is different than the specified one. Super Mario Odyssey hits this scenario when changing | ||
| 73 | // areas, but we do not want to overwrite the old pages. | ||
| 74 | // TODO(bunnei): We need to write a hardware test to confirm this behavior. | ||
| 75 | 72 | ||
| 76 | LOG_ERROR(HW_GPU, "attempting to map addr 0x{:016X}, which is not available!", gpu_addr); | 73 | Core::System::GetInstance().Renderer().Rasterizer().FlushAndInvalidateRegion(cache_addr, |
| 74 | aligned_size); | ||
| 75 | UnmapRange(gpu_addr, aligned_size); | ||
| 77 | 76 | ||
| 78 | const std::optional<GPUVAddr> new_gpu_addr{ | 77 | return gpu_addr; |
| 79 | FindFreeBlock(gpu_addr, size, PAGE_SIZE, PageStatus::Allocated)}; | 78 | } |
| 80 | 79 | ||
| 81 | ASSERT_MSG(new_gpu_addr, "unable to find available GPU memory"); | 80 | GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) { |
| 81 | // Find the first Free VMA. | ||
| 82 | const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { | ||
| 83 | if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { | ||
| 84 | return false; | ||
| 85 | } | ||
| 82 | 86 | ||
| 83 | gpu_addr = *new_gpu_addr; | 87 | const VAddr vma_end{vma.second.base + vma.second.size}; |
| 88 | return vma_end > region_start && vma_end >= region_start + size; | ||
| 89 | })}; | ||
| 90 | |||
| 91 | if (vma_handle == vma_map.end()) { | ||
| 92 | return {}; | ||
| 84 | } | 93 | } |
| 85 | 94 | ||
| 86 | for (u64 offset{}; offset < size; offset += PAGE_SIZE) { | 95 | return std::max(region_start, vma_handle->second.base); |
| 87 | VAddr& slot{PageSlot(gpu_addr + offset)}; | 96 | } |
| 88 | 97 | ||
| 89 | ASSERT(slot == static_cast<u64>(PageStatus::Allocated)); | 98 | bool MemoryManager::IsAddressValid(GPUVAddr addr) const { |
| 99 | return (addr >> page_bits) < page_table.pointers.size(); | ||
| 100 | } | ||
| 90 | 101 | ||
| 91 | slot = cpu_addr + offset; | 102 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { |
| 103 | if (!IsAddressValid(addr)) { | ||
| 104 | return {}; | ||
| 92 | } | 105 | } |
| 93 | 106 | ||
| 94 | const MappedRegion region{cpu_addr, gpu_addr, size}; | 107 | VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; |
| 95 | mapped_regions.push_back(region); | 108 | if (cpu_addr) { |
| 109 | return cpu_addr + (addr & page_mask); | ||
| 110 | } | ||
| 96 | 111 | ||
| 97 | return gpu_addr; | 112 | return {}; |
| 98 | } | 113 | } |
| 99 | 114 | ||
| 100 | GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | 115 | template <typename T> |
| 101 | ASSERT((gpu_addr & PAGE_MASK) == 0); | 116 | T MemoryManager::Read(GPUVAddr addr) { |
| 117 | if (!IsAddressValid(addr)) { | ||
| 118 | return {}; | ||
| 119 | } | ||
| 102 | 120 | ||
| 103 | for (u64 offset{}; offset < size; offset += PAGE_SIZE) { | 121 | const u8* page_pointer{page_table.pointers[addr >> page_bits]}; |
| 104 | VAddr& slot{PageSlot(gpu_addr + offset)}; | 122 | if (page_pointer) { |
| 123 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 124 | T value; | ||
| 125 | std::memcpy(&value, &page_pointer[addr & page_mask], sizeof(T)); | ||
| 126 | return value; | ||
| 127 | } | ||
| 105 | 128 | ||
| 106 | ASSERT(slot != static_cast<u64>(PageStatus::Allocated) && | 129 | switch (page_table.attributes[addr >> page_bits]) { |
| 107 | slot != static_cast<u64>(PageStatus::Unmapped)); | 130 | case Common::PageType::Unmapped: |
| 131 | LOG_ERROR(HW_GPU, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, addr); | ||
| 132 | return 0; | ||
| 133 | case Common::PageType::Memory: | ||
| 134 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr); | ||
| 135 | break; | ||
| 136 | default: | ||
| 137 | UNREACHABLE(); | ||
| 138 | } | ||
| 139 | return {}; | ||
| 140 | } | ||
| 108 | 141 | ||
| 109 | slot = static_cast<u64>(PageStatus::Unmapped); | 142 | template <typename T> |
| 143 | void MemoryManager::Write(GPUVAddr addr, T data) { | ||
| 144 | if (!IsAddressValid(addr)) { | ||
| 145 | return; | ||
| 110 | } | 146 | } |
| 111 | 147 | ||
| 112 | // Delete the region mappings that are contained within the unmapped region | 148 | u8* page_pointer{page_table.pointers[addr >> page_bits]}; |
| 113 | mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(), | 149 | if (page_pointer) { |
| 114 | [&](const MappedRegion& region) { | 150 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 115 | return region.gpu_addr <= gpu_addr && | 151 | std::memcpy(&page_pointer[addr & page_mask], &data, sizeof(T)); |
| 116 | region.gpu_addr + region.size < gpu_addr + size; | 152 | return; |
| 117 | }), | 153 | } |
| 118 | mapped_regions.end()); | ||
| 119 | return gpu_addr; | ||
| 120 | } | ||
| 121 | 154 | ||
| 122 | GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const { | 155 | switch (page_table.attributes[addr >> page_bits]) { |
| 123 | for (const auto& region : mapped_regions) { | 156 | case Common::PageType::Unmapped: |
| 124 | const GPUVAddr region_end{region.gpu_addr + region.size}; | 157 | LOG_ERROR(HW_GPU, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, |
| 125 | if (region_start >= region.gpu_addr && region_start < region_end) { | 158 | static_cast<u32>(data), addr); |
| 126 | return region_end; | 159 | return; |
| 127 | } | 160 | case Common::PageType::Memory: |
| 161 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr); | ||
| 162 | break; | ||
| 163 | default: | ||
| 164 | UNREACHABLE(); | ||
| 128 | } | 165 | } |
| 129 | return {}; | ||
| 130 | } | 166 | } |
| 131 | 167 | ||
| 132 | std::optional<GPUVAddr> MemoryManager::FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, | 168 | template u8 MemoryManager::Read<u8>(GPUVAddr addr); |
| 133 | PageStatus status) { | 169 | template u16 MemoryManager::Read<u16>(GPUVAddr addr); |
| 134 | GPUVAddr gpu_addr{region_start}; | 170 | template u32 MemoryManager::Read<u32>(GPUVAddr addr); |
| 135 | u64 free_space{}; | 171 | template u64 MemoryManager::Read<u64>(GPUVAddr addr); |
| 136 | align = (align + PAGE_MASK) & ~PAGE_MASK; | 172 | template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data); |
| 137 | 173 | template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); | |
| 138 | while (gpu_addr + free_space < MAX_ADDRESS) { | 174 | template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); |
| 139 | if (PageSlot(gpu_addr + free_space) == static_cast<u64>(status)) { | 175 | template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data); |
| 140 | free_space += PAGE_SIZE; | 176 | |
| 141 | if (free_space >= size) { | 177 | u8* MemoryManager::GetPointer(GPUVAddr addr) { |
| 142 | return gpu_addr; | 178 | if (!IsAddressValid(addr)) { |
| 143 | } | 179 | return {}; |
| 144 | } else { | ||
| 145 | gpu_addr += free_space + PAGE_SIZE; | ||
| 146 | free_space = 0; | ||
| 147 | gpu_addr = Common::AlignUp(gpu_addr, align); | ||
| 148 | } | ||
| 149 | } | 180 | } |
| 150 | 181 | ||
| 182 | u8* page_pointer{page_table.pointers[addr >> page_bits]}; | ||
| 183 | if (page_pointer) { | ||
| 184 | return page_pointer + (addr & page_mask); | ||
| 185 | } | ||
| 186 | |||
| 187 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | ||
| 151 | return {}; | 188 | return {}; |
| 152 | } | 189 | } |
| 153 | 190 | ||
| 154 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { | 191 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { |
| 155 | const VAddr base_addr{PageSlot(gpu_addr)}; | 192 | std::memcpy(dest_buffer, GetPointer(src_addr), size); |
| 193 | } | ||
| 194 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { | ||
| 195 | std::memcpy(GetPointer(dest_addr), src_buffer, size); | ||
| 196 | } | ||
| 197 | |||
| 198 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { | ||
| 199 | std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size); | ||
| 200 | } | ||
| 156 | 201 | ||
| 157 | if (base_addr == static_cast<u64>(PageStatus::Allocated) || | 202 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, |
| 158 | base_addr == static_cast<u64>(PageStatus::Unmapped) || | 203 | VAddr backing_addr) { |
| 159 | base_addr == static_cast<u64>(PageStatus::Reserved)) { | 204 | LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, |
| 160 | return {}; | 205 | (base + size) * page_size); |
| 206 | |||
| 207 | const VAddr end{base + size}; | ||
| 208 | ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", | ||
| 209 | base + page_table.pointers.size()); | ||
| 210 | |||
| 211 | std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); | ||
| 212 | |||
| 213 | if (memory == nullptr) { | ||
| 214 | std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory); | ||
| 215 | std::fill(page_table.backing_addr.begin() + base, page_table.backing_addr.begin() + end, | ||
| 216 | backing_addr); | ||
| 217 | } else { | ||
| 218 | while (base != end) { | ||
| 219 | page_table.pointers[base] = memory; | ||
| 220 | page_table.backing_addr[base] = backing_addr; | ||
| 221 | |||
| 222 | base += 1; | ||
| 223 | memory += page_size; | ||
| 224 | backing_addr += page_size; | ||
| 225 | } | ||
| 161 | } | 226 | } |
| 227 | } | ||
| 162 | 228 | ||
| 163 | return base_addr + (gpu_addr & PAGE_MASK); | 229 | void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) { |
| 230 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size); | ||
| 231 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base); | ||
| 232 | MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr); | ||
| 164 | } | 233 | } |
| 165 | 234 | ||
| 166 | u8 MemoryManager::Read8(GPUVAddr addr) { | 235 | void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) { |
| 167 | return Memory::Read8(*GpuToCpuAddress(addr)); | 236 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size); |
| 237 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base); | ||
| 238 | MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped); | ||
| 168 | } | 239 | } |
| 169 | 240 | ||
| 170 | u16 MemoryManager::Read16(GPUVAddr addr) { | 241 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { |
| 171 | return Memory::Read16(*GpuToCpuAddress(addr)); | 242 | ASSERT(base + size == next.base); |
| 243 | if (type != next.type) { | ||
| 244 | return {}; | ||
| 245 | } | ||
| 246 | if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) { | ||
| 247 | return {}; | ||
| 248 | } | ||
| 249 | if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) { | ||
| 250 | return {}; | ||
| 251 | } | ||
| 252 | return true; | ||
| 172 | } | 253 | } |
| 173 | 254 | ||
| 174 | u32 MemoryManager::Read32(GPUVAddr addr) { | 255 | MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const { |
| 175 | return Memory::Read32(*GpuToCpuAddress(addr)); | 256 | if (target >= address_space_end) { |
| 257 | return vma_map.end(); | ||
| 258 | } else { | ||
| 259 | return std::prev(vma_map.upper_bound(target)); | ||
| 260 | } | ||
| 176 | } | 261 | } |
| 177 | 262 | ||
| 178 | u64 MemoryManager::Read64(GPUVAddr addr) { | 263 | MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) { |
| 179 | return Memory::Read64(*GpuToCpuAddress(addr)); | 264 | VirtualMemoryArea& vma{vma_handle->second}; |
| 265 | |||
| 266 | vma.type = VirtualMemoryArea::Type::Allocated; | ||
| 267 | vma.backing_addr = 0; | ||
| 268 | vma.backing_memory = {}; | ||
| 269 | UpdatePageTableForVMA(vma); | ||
| 270 | |||
| 271 | return MergeAdjacent(vma_handle); | ||
| 180 | } | 272 | } |
| 181 | 273 | ||
| 182 | void MemoryManager::Write8(GPUVAddr addr, u8 data) { | 274 | MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset, |
| 183 | Memory::Write8(*GpuToCpuAddress(addr), data); | 275 | u64 size) { |
| 276 | |||
| 277 | // This is the appropriately sized VMA that will turn into our allocation. | ||
| 278 | VMAIter vma_handle{CarveVMA(target, size)}; | ||
| 279 | VirtualMemoryArea& vma{vma_handle->second}; | ||
| 280 | |||
| 281 | ASSERT(vma.size == size); | ||
| 282 | |||
| 283 | vma.offset = offset; | ||
| 284 | |||
| 285 | return Allocate(vma_handle); | ||
| 184 | } | 286 | } |
| 185 | 287 | ||
| 186 | void MemoryManager::Write16(GPUVAddr addr, u16 data) { | 288 | MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size, |
| 187 | Memory::Write16(*GpuToCpuAddress(addr), data); | 289 | VAddr backing_addr) { |
| 290 | // This is the appropriately sized VMA that will turn into our allocation. | ||
| 291 | VMAIter vma_handle{CarveVMA(target, size)}; | ||
| 292 | VirtualMemoryArea& vma{vma_handle->second}; | ||
| 293 | |||
| 294 | ASSERT(vma.size == size); | ||
| 295 | |||
| 296 | vma.type = VirtualMemoryArea::Type::Mapped; | ||
| 297 | vma.backing_memory = memory; | ||
| 298 | vma.backing_addr = backing_addr; | ||
| 299 | UpdatePageTableForVMA(vma); | ||
| 300 | |||
| 301 | return MergeAdjacent(vma_handle); | ||
| 188 | } | 302 | } |
| 189 | 303 | ||
| 190 | void MemoryManager::Write32(GPUVAddr addr, u32 data) { | 304 | void MemoryManager::UnmapRange(GPUVAddr target, u64 size) { |
| 191 | Memory::Write32(*GpuToCpuAddress(addr), data); | 305 | VMAIter vma{CarveVMARange(target, size)}; |
| 306 | const VAddr target_end{target + size}; | ||
| 307 | const VMAIter end{vma_map.end()}; | ||
| 308 | |||
| 309 | // The comparison against the end of the range must be done using addresses since VMAs can be | ||
| 310 | // merged during this process, causing invalidation of the iterators. | ||
| 311 | while (vma != end && vma->second.base < target_end) { | ||
| 312 | // Unmapped ranges return to allocated state and can be reused | ||
| 313 | // This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games | ||
| 314 | vma = std::next(Allocate(vma)); | ||
| 315 | } | ||
| 316 | |||
| 317 | ASSERT(FindVMA(target)->second.size >= size); | ||
| 192 | } | 318 | } |
| 193 | 319 | ||
| 194 | void MemoryManager::Write64(GPUVAddr addr, u64 data) { | 320 | MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) { |
| 195 | Memory::Write64(*GpuToCpuAddress(addr), data); | 321 | // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given |
| 322 | // non-const access to its container. | ||
| 323 | return vma_map.erase(iter, iter); // Erases an empty range of elements | ||
| 196 | } | 324 | } |
| 197 | 325 | ||
| 198 | u8* MemoryManager::GetPointer(GPUVAddr addr) { | 326 | MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) { |
| 199 | return Memory::GetPointer(*GpuToCpuAddress(addr)); | 327 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size); |
| 328 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base); | ||
| 329 | |||
| 330 | VMAIter vma_handle{StripIterConstness(FindVMA(base))}; | ||
| 331 | if (vma_handle == vma_map.end()) { | ||
| 332 | // Target address is outside the managed range | ||
| 333 | return {}; | ||
| 334 | } | ||
| 335 | |||
| 336 | const VirtualMemoryArea& vma{vma_handle->second}; | ||
| 337 | if (vma.type == VirtualMemoryArea::Type::Mapped) { | ||
| 338 | // Region is already allocated | ||
| 339 | return {}; | ||
| 340 | } | ||
| 341 | |||
| 342 | const VAddr start_in_vma{base - vma.base}; | ||
| 343 | const VAddr end_in_vma{start_in_vma + size}; | ||
| 344 | |||
| 345 | ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}", | ||
| 346 | vma.size, end_in_vma); | ||
| 347 | |||
| 348 | if (end_in_vma < vma.size) { | ||
| 349 | // Split VMA at the end of the allocated region | ||
| 350 | SplitVMA(vma_handle, end_in_vma); | ||
| 351 | } | ||
| 352 | if (start_in_vma != 0) { | ||
| 353 | // Split VMA at the start of the allocated region | ||
| 354 | vma_handle = SplitVMA(vma_handle, start_in_vma); | ||
| 355 | } | ||
| 356 | |||
| 357 | return vma_handle; | ||
| 200 | } | 358 | } |
| 201 | 359 | ||
| 202 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { | 360 | MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) { |
| 203 | std::memcpy(dest_buffer, GetPointer(src_addr), size); | 361 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size); |
| 362 | ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target); | ||
| 363 | |||
| 364 | const VAddr target_end{target + size}; | ||
| 365 | ASSERT(target_end >= target); | ||
| 366 | ASSERT(size > 0); | ||
| 367 | |||
| 368 | VMAIter begin_vma{StripIterConstness(FindVMA(target))}; | ||
| 369 | const VMAIter i_end{vma_map.lower_bound(target_end)}; | ||
| 370 | if (std::any_of(begin_vma, i_end, [](const auto& entry) { | ||
| 371 | return entry.second.type == VirtualMemoryArea::Type::Unmapped; | ||
| 372 | })) { | ||
| 373 | return {}; | ||
| 374 | } | ||
| 375 | |||
| 376 | if (target != begin_vma->second.base) { | ||
| 377 | begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); | ||
| 378 | } | ||
| 379 | |||
| 380 | VMAIter end_vma{StripIterConstness(FindVMA(target_end))}; | ||
| 381 | if (end_vma != vma_map.end() && target_end != end_vma->second.base) { | ||
| 382 | end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); | ||
| 383 | } | ||
| 384 | |||
| 385 | return begin_vma; | ||
| 204 | } | 386 | } |
| 205 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { | 387 | |
| 206 | std::memcpy(GetPointer(dest_addr), src_buffer, size); | 388 | MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { |
| 389 | VirtualMemoryArea& old_vma{vma_handle->second}; | ||
| 390 | VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA | ||
| 391 | |||
| 392 | // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably | ||
| 393 | // a bug. This restriction might be removed later. | ||
| 394 | ASSERT(offset_in_vma < old_vma.size); | ||
| 395 | ASSERT(offset_in_vma > 0); | ||
| 396 | |||
| 397 | old_vma.size = offset_in_vma; | ||
| 398 | new_vma.base += offset_in_vma; | ||
| 399 | new_vma.size -= offset_in_vma; | ||
| 400 | |||
| 401 | switch (new_vma.type) { | ||
| 402 | case VirtualMemoryArea::Type::Unmapped: | ||
| 403 | break; | ||
| 404 | case VirtualMemoryArea::Type::Allocated: | ||
| 405 | new_vma.offset += offset_in_vma; | ||
| 406 | break; | ||
| 407 | case VirtualMemoryArea::Type::Mapped: | ||
| 408 | new_vma.backing_memory += offset_in_vma; | ||
| 409 | break; | ||
| 410 | } | ||
| 411 | |||
| 412 | ASSERT(old_vma.CanBeMergedWith(new_vma)); | ||
| 413 | |||
| 414 | return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma); | ||
| 207 | } | 415 | } |
| 208 | 416 | ||
| 209 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { | 417 | MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) { |
| 210 | std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size); | 418 | const VMAIter next_vma{std::next(iter)}; |
| 419 | if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { | ||
| 420 | iter->second.size += next_vma->second.size; | ||
| 421 | vma_map.erase(next_vma); | ||
| 422 | } | ||
| 423 | |||
| 424 | if (iter != vma_map.begin()) { | ||
| 425 | VMAIter prev_vma{std::prev(iter)}; | ||
| 426 | if (prev_vma->second.CanBeMergedWith(iter->second)) { | ||
| 427 | prev_vma->second.size += iter->second.size; | ||
| 428 | vma_map.erase(iter); | ||
| 429 | iter = prev_vma; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | return iter; | ||
| 211 | } | 434 | } |
| 212 | 435 | ||
| 213 | VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { | 436 | void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { |
| 214 | auto& block{page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]}; | 437 | switch (vma.type) { |
| 215 | if (!block) { | 438 | case VirtualMemoryArea::Type::Unmapped: |
| 216 | block = std::make_unique<PageBlock>(); | 439 | UnmapRegion(vma.base, vma.size); |
| 217 | block->fill(static_cast<VAddr>(PageStatus::Unmapped)); | 440 | break; |
| 441 | case VirtualMemoryArea::Type::Allocated: | ||
| 442 | MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr); | ||
| 443 | break; | ||
| 444 | case VirtualMemoryArea::Type::Mapped: | ||
| 445 | MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr); | ||
| 446 | break; | ||
| 218 | } | 447 | } |
| 219 | return (*block)[(gpu_addr >> PAGE_BITS) & PAGE_BLOCK_MASK]; | ||
| 220 | } | 448 | } |
| 221 | 449 | ||
| 222 | } // namespace Tegra | 450 | } // namespace Tegra |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 425e2f31c..34744bb27 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -1,82 +1,148 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <map> |
| 8 | #include <memory> | ||
| 9 | #include <optional> | 8 | #include <optional> |
| 10 | #include <vector> | ||
| 11 | 9 | ||
| 12 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/page_table.h" | ||
| 13 | 12 | ||
| 14 | namespace Tegra { | 13 | namespace Tegra { |
| 15 | 14 | ||
| 16 | /// Virtual addresses in the GPU's memory map are 64 bit. | 15 | /** |
| 17 | using GPUVAddr = u64; | 16 | * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space |
| 17 | * with homogeneous attributes across its extents. In this particular implementation each VMA is | ||
| 18 | * also backed by a single host memory allocation. | ||
| 19 | */ | ||
| 20 | struct VirtualMemoryArea { | ||
| 21 | enum class Type : u8 { | ||
| 22 | Unmapped, | ||
| 23 | Allocated, | ||
| 24 | Mapped, | ||
| 25 | }; | ||
| 26 | |||
| 27 | /// Virtual base address of the region. | ||
| 28 | GPUVAddr base{}; | ||
| 29 | /// Size of the region. | ||
| 30 | u64 size{}; | ||
| 31 | /// Memory area mapping type. | ||
| 32 | Type type{Type::Unmapped}; | ||
| 33 | /// CPU memory mapped address corresponding to this memory area. | ||
| 34 | VAddr backing_addr{}; | ||
| 35 | /// Offset into the backing_memory the mapping starts from. | ||
| 36 | std::size_t offset{}; | ||
| 37 | /// Pointer backing this VMA. | ||
| 38 | u8* backing_memory{}; | ||
| 39 | |||
| 40 | /// Tests if this area can be merged to the right with `next`. | ||
| 41 | bool CanBeMergedWith(const VirtualMemoryArea& next) const; | ||
| 42 | }; | ||
| 18 | 43 | ||
| 19 | class MemoryManager final { | 44 | class MemoryManager final { |
| 20 | public: | 45 | public: |
| 21 | MemoryManager(); | 46 | MemoryManager(); |
| 22 | 47 | ||
| 23 | GPUVAddr AllocateSpace(u64 size, u64 align); | 48 | GPUVAddr AllocateSpace(u64 size, u64 align); |
| 24 | GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); | 49 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); |
| 25 | GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); | 50 | GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); |
| 26 | GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); | 51 | GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); |
| 27 | GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); | 52 | GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); |
| 28 | GPUVAddr GetRegionEnd(GPUVAddr region_start) const; | 53 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr); |
| 29 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); | ||
| 30 | |||
| 31 | static constexpr u64 PAGE_BITS = 16; | ||
| 32 | static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; | ||
| 33 | static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; | ||
| 34 | 54 | ||
| 35 | u8 Read8(GPUVAddr addr); | 55 | template <typename T> |
| 36 | u16 Read16(GPUVAddr addr); | 56 | T Read(GPUVAddr addr); |
| 37 | u32 Read32(GPUVAddr addr); | ||
| 38 | u64 Read64(GPUVAddr addr); | ||
| 39 | 57 | ||
| 40 | void Write8(GPUVAddr addr, u8 data); | 58 | template <typename T> |
| 41 | void Write16(GPUVAddr addr, u16 data); | 59 | void Write(GPUVAddr addr, T data); |
| 42 | void Write32(GPUVAddr addr, u32 data); | ||
| 43 | void Write64(GPUVAddr addr, u64 data); | ||
| 44 | 60 | ||
| 45 | u8* GetPointer(GPUVAddr vaddr); | 61 | u8* GetPointer(GPUVAddr addr); |
| 46 | 62 | ||
| 47 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); | 63 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); |
| 48 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 64 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); |
| 49 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); | 65 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); |
| 50 | 66 | ||
| 51 | private: | 67 | private: |
| 52 | enum class PageStatus : u64 { | 68 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; |
| 53 | Unmapped = 0xFFFFFFFFFFFFFFFFULL, | 69 | using VMAHandle = VMAMap::const_iterator; |
| 54 | Allocated = 0xFFFFFFFFFFFFFFFEULL, | 70 | using VMAIter = VMAMap::iterator; |
| 55 | Reserved = 0xFFFFFFFFFFFFFFFDULL, | 71 | |
| 56 | }; | 72 | bool IsAddressValid(GPUVAddr addr) const; |
| 57 | 73 | void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | |
| 58 | std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, | 74 | VAddr backing_addr = 0); |
| 59 | PageStatus status); | 75 | void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr); |
| 60 | VAddr& PageSlot(GPUVAddr gpu_addr); | 76 | void UnmapRegion(GPUVAddr base, u64 size); |
| 61 | 77 | ||
| 62 | static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; | 78 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. |
| 63 | static constexpr u64 PAGE_TABLE_BITS{10}; | 79 | VMAHandle FindVMA(GPUVAddr target) const; |
| 64 | static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; | 80 | |
| 65 | static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1}; | 81 | VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size); |
| 66 | static constexpr u64 PAGE_BLOCK_BITS{14}; | 82 | |
| 67 | static constexpr u64 PAGE_BLOCK_SIZE{1 << PAGE_BLOCK_BITS}; | 83 | /** |
| 68 | static constexpr u64 PAGE_BLOCK_MASK{PAGE_BLOCK_SIZE - 1}; | 84 | * Maps an unmanaged host memory pointer at a given address. |
| 69 | 85 | * | |
| 70 | using PageBlock = std::array<VAddr, PAGE_BLOCK_SIZE>; | 86 | * @param target The guest address to start the mapping at. |
| 71 | std::array<std::unique_ptr<PageBlock>, PAGE_TABLE_SIZE> page_table{}; | 87 | * @param memory The memory to be mapped. |
| 72 | 88 | * @param size Size of the mapping. | |
| 73 | struct MappedRegion { | 89 | * @param state MemoryState tag to attach to the VMA. |
| 74 | VAddr cpu_addr; | 90 | */ |
| 75 | GPUVAddr gpu_addr; | 91 | VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr); |
| 76 | u64 size; | 92 | |
| 77 | }; | 93 | /// Unmaps a range of addresses, splitting VMAs as necessary. |
| 94 | void UnmapRange(GPUVAddr target, u64 size); | ||
| 95 | |||
| 96 | /// Converts a VMAHandle to a mutable VMAIter. | ||
| 97 | VMAIter StripIterConstness(const VMAHandle& iter); | ||
| 98 | |||
| 99 | /// Marks as the specfied VMA as allocated. | ||
| 100 | VMAIter Allocate(VMAIter vma); | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing | ||
| 104 | * the appropriate error checking. | ||
| 105 | */ | ||
| 106 | VMAIter CarveVMA(GPUVAddr base, u64 size); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each | ||
| 110 | * end of the range. | ||
| 111 | */ | ||
| 112 | VMAIter CarveVMARange(GPUVAddr base, u64 size); | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Splits a VMA in two, at the specified offset. | ||
| 116 | * @returns the right side of the split, with the original iterator becoming the left side. | ||
| 117 | */ | ||
| 118 | VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); | ||
| 119 | |||
| 120 | /** | ||
| 121 | * Checks for and merges the specified VMA with adjacent ones if possible. | ||
| 122 | * @returns the merged VMA or the original if no merging was possible. | ||
| 123 | */ | ||
| 124 | VMAIter MergeAdjacent(VMAIter vma); | ||
| 125 | |||
| 126 | /// Updates the pages corresponding to this VMA so they match the VMA's attributes. | ||
| 127 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); | ||
| 128 | |||
| 129 | /// Finds a free (unmapped region) of the specified size starting at the specified address. | ||
| 130 | GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size); | ||
| 78 | 131 | ||
| 79 | std::vector<MappedRegion> mapped_regions; | 132 | private: |
| 133 | static constexpr u64 page_bits{16}; | ||
| 134 | static constexpr u64 page_size{1 << page_bits}; | ||
| 135 | static constexpr u64 page_mask{page_size - 1}; | ||
| 136 | |||
| 137 | /// Address space in bits, this is fairly arbitrary but sufficiently large. | ||
| 138 | static constexpr u32 address_space_width{39}; | ||
| 139 | /// Start address for mapping, this is fairly arbitrary but must be non-zero. | ||
| 140 | static constexpr GPUVAddr address_space_base{0x100000}; | ||
| 141 | /// End of address space, based on address space in bits. | ||
| 142 | static constexpr GPUVAddr address_space_end{1ULL << address_space_width}; | ||
| 143 | |||
| 144 | Common::PageTable page_table{page_bits}; | ||
| 145 | VMAMap vma_map; | ||
| 80 | }; | 146 | }; |
| 81 | 147 | ||
| 82 | } // namespace Tegra | 148 | } // namespace Tegra |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 76e292e87..d7b86df38 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/fermi_2d.h" | 10 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/gpu.h" | 11 | #include "video_core/gpu.h" |
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | 12 | ||
| 14 | namespace VideoCore { | 13 | namespace VideoCore { |
| 15 | 14 | ||
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 5048ed6ce..f75c65825 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -21,8 +21,8 @@ CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr | |||
| 21 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) | 21 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) |
| 22 | : RasterizerCache{rasterizer}, stream_buffer(size, true) {} | 22 | : RasterizerCache{rasterizer}, stream_buffer(size, true) {} |
| 23 | 23 | ||
| 24 | GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, | 24 | GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment, |
| 25 | std::size_t alignment, bool cache) { | 25 | bool cache) { |
| 26 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); | 26 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); |
| 27 | 27 | ||
| 28 | // Cache management is a big overhead, so only cache entries with a given size. | 28 | // Cache management is a big overhead, so only cache entries with a given size. |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 1de1f84ae..fc33aa433 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -58,7 +58,7 @@ public: | |||
| 58 | 58 | ||
| 59 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been | 59 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been |
| 60 | /// allocated. | 60 | /// allocated. |
| 61 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | 61 | GLintptr UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, |
| 62 | bool cache = true); | 62 | bool cache = true); |
| 63 | 63 | ||
| 64 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. | 64 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. |
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp index c8dbcacbd..0fbfbad55 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.cpp +++ b/src/video_core/renderer_opengl/gl_global_cache.cpp | |||
| @@ -46,7 +46,7 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, | |||
| 46 | return search->second; | 46 | return search->second; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, | 49 | GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size, |
| 50 | u8* host_ptr) { | 50 | u8* host_ptr) { |
| 51 | GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; | 51 | GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; |
| 52 | if (!region) { | 52 | if (!region) { |
| @@ -76,8 +76,8 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( | |||
| 76 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; | 76 | const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; |
| 77 | const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + | 77 | const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + |
| 78 | global_region.GetCbufOffset()}; | 78 | global_region.GetCbufOffset()}; |
| 79 | const auto actual_addr{memory_manager.Read64(addr)}; | 79 | const auto actual_addr{memory_manager.Read<u64>(addr)}; |
| 80 | const auto size{memory_manager.Read32(addr + 8)}; | 80 | const auto size{memory_manager.Read<u32>(addr + 8)}; |
| 81 | 81 | ||
| 82 | // Look up global region in the cache based on address | 82 | // Look up global region in the cache based on address |
| 83 | const auto& host_ptr{memory_manager.GetPointer(actual_addr)}; | 83 | const auto& host_ptr{memory_manager.GetPointer(actual_addr)}; |
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h index a840491f7..5a21ab66f 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.h +++ b/src/video_core/renderer_opengl/gl_global_cache.h | |||
| @@ -66,7 +66,7 @@ public: | |||
| 66 | 66 | ||
| 67 | private: | 67 | private: |
| 68 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; | 68 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; |
| 69 | GlobalRegion GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, u8* host_ptr); | 69 | GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr); |
| 70 | void ReserveGlobalRegion(GlobalRegion region); | 70 | void ReserveGlobalRegion(GlobalRegion region); |
| 71 | 71 | ||
| 72 | std::unordered_map<CacheAddr, GlobalRegion> reserve; | 72 | std::unordered_map<CacheAddr, GlobalRegion> reserve; |
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp index 75d816795..2bcbd3da2 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp | |||
| @@ -40,8 +40,7 @@ GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) { | |||
| 40 | return index_offset; | 40 | return index_offset; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, | 43 | GLintptr PrimitiveAssembler::MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count) { |
| 44 | u32 count) { | ||
| 45 | const std::size_t map_size{CalculateQuadSize(count)}; | 44 | const std::size_t map_size{CalculateQuadSize(count)}; |
| 46 | auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); | 45 | auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); |
| 47 | 46 | ||
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h index a8cb88eb5..0e2e7dc36 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.h +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h | |||
| @@ -24,7 +24,7 @@ public: | |||
| 24 | 24 | ||
| 25 | GLintptr MakeQuadArray(u32 first, u32 count); | 25 | GLintptr MakeQuadArray(u32 first, u32 count); |
| 26 | 26 | ||
| 27 | GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count); | 27 | GLintptr MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count); |
| 28 | 28 | ||
| 29 | private: | 29 | private: |
| 30 | OGLBufferCache& buffer_cache; | 30 | OGLBufferCache& buffer_cache; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 198c54872..e06dfe43f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -225,8 +225,8 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) { | |||
| 225 | if (!vertex_array.IsEnabled()) | 225 | if (!vertex_array.IsEnabled()) |
| 226 | continue; | 226 | continue; |
| 227 | 227 | ||
| 228 | const Tegra::GPUVAddr start = vertex_array.StartAddress(); | 228 | const GPUVAddr start = vertex_array.StartAddress(); |
| 229 | const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 229 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 230 | 230 | ||
| 231 | ASSERT(end > start); | 231 | ASSERT(end > start); |
| 232 | const u64 size = end - start + 1; | 232 | const u64 size = end - start + 1; |
| @@ -421,8 +421,8 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | |||
| 421 | if (!regs.vertex_array[index].IsEnabled()) | 421 | if (!regs.vertex_array[index].IsEnabled()) |
| 422 | continue; | 422 | continue; |
| 423 | 423 | ||
| 424 | const Tegra::GPUVAddr start = regs.vertex_array[index].StartAddress(); | 424 | const GPUVAddr start = regs.vertex_array[index].StartAddress(); |
| 425 | const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 425 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 426 | 426 | ||
| 427 | ASSERT(end > start); | 427 | ASSERT(end > start); |
| 428 | size += end - start + 1; | 428 | size += end - start + 1; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 7bd0daa57..0235317c0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -55,7 +55,7 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) { | |||
| 55 | } | 55 | } |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | 58 | void SurfaceParams::InitCacheParameters(GPUVAddr gpu_addr_) { |
| 59 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; | 59 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; |
| 60 | 60 | ||
| 61 | gpu_addr = gpu_addr_; | 61 | gpu_addr = gpu_addr_; |
| @@ -222,7 +222,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | /*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( | 224 | /*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( |
| 225 | u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, | 225 | u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format, |
| 226 | u32 block_width, u32 block_height, u32 block_depth, | 226 | u32 block_width, u32 block_height, u32 block_depth, |
| 227 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { | 227 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { |
| 228 | SurfaceParams params{}; | 228 | SurfaceParams params{}; |
| @@ -564,6 +564,12 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac | |||
| 564 | CachedSurface::CachedSurface(const SurfaceParams& params) | 564 | CachedSurface::CachedSurface(const SurfaceParams& params) |
| 565 | : params{params}, gl_target{SurfaceTargetToGL(params.target)}, | 565 | : params{params}, gl_target{SurfaceTargetToGL(params.target)}, |
| 566 | cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} { | 566 | cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} { |
| 567 | |||
| 568 | const auto optional_cpu_addr{ | ||
| 569 | Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(params.gpu_addr)}; | ||
| 570 | ASSERT_MSG(optional_cpu_addr, "optional_cpu_addr is invalid"); | ||
| 571 | cpu_addr = *optional_cpu_addr; | ||
| 572 | |||
| 567 | texture.Create(gl_target); | 573 | texture.Create(gl_target); |
| 568 | 574 | ||
| 569 | // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) | 575 | // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) |
| @@ -603,20 +609,6 @@ CachedSurface::CachedSurface(const SurfaceParams& params) | |||
| 603 | ApplyTextureDefaults(texture.handle, params.max_mip_level); | 609 | ApplyTextureDefaults(texture.handle, params.max_mip_level); |
| 604 | 610 | ||
| 605 | OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString()); | 611 | OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString()); |
| 606 | |||
| 607 | // Clamp size to mapped GPU memory region | ||
| 608 | // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000 | ||
| 609 | // R32F render buffer. We do not yet know if this is a game bug or something else, but this | ||
| 610 | // check is necessary to prevent flushing from overwriting unmapped memory. | ||
| 611 | |||
| 612 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; | ||
| 613 | const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr}; | ||
| 614 | if (cached_size_in_bytes > max_size) { | ||
| 615 | LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size); | ||
| 616 | cached_size_in_bytes = max_size; | ||
| 617 | } | ||
| 618 | |||
| 619 | cpu_addr = *memory_manager.GpuToCpuAddress(params.gpu_addr); | ||
| 620 | } | 612 | } |
| 621 | 613 | ||
| 622 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); | 614 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); |
| @@ -925,7 +917,7 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { | |||
| 925 | } | 917 | } |
| 926 | 918 | ||
| 927 | Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { | 919 | Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { |
| 928 | if (params.gpu_addr == 0 || params.height * params.width == 0) { | 920 | if (!params.IsValid()) { |
| 929 | return {}; | 921 | return {}; |
| 930 | } | 922 | } |
| 931 | 923 | ||
| @@ -980,11 +972,11 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface, | |||
| 980 | const auto& init_params{src_surface->GetSurfaceParams()}; | 972 | const auto& init_params{src_surface->GetSurfaceParams()}; |
| 981 | const auto& dst_params{dst_surface->GetSurfaceParams()}; | 973 | const auto& dst_params{dst_surface->GetSurfaceParams()}; |
| 982 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; | 974 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; |
| 983 | Tegra::GPUVAddr address{init_params.gpu_addr}; | 975 | GPUVAddr address{init_params.gpu_addr}; |
| 984 | const std::size_t layer_size{dst_params.LayerMemorySize()}; | 976 | const std::size_t layer_size{dst_params.LayerMemorySize()}; |
| 985 | for (u32 layer = 0; layer < dst_params.depth; layer++) { | 977 | for (u32 layer = 0; layer < dst_params.depth; layer++) { |
| 986 | for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { | 978 | for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { |
| 987 | const Tegra::GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)}; | 979 | const GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)}; |
| 988 | const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))}; | 980 | const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))}; |
| 989 | if (!copy) { | 981 | if (!copy) { |
| 990 | continue; | 982 | continue; |
| @@ -1244,10 +1236,9 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar | |||
| 1244 | return {}; | 1236 | return {}; |
| 1245 | } | 1237 | } |
| 1246 | 1238 | ||
| 1247 | static std::optional<u32> TryFindBestLayer(Tegra::GPUVAddr addr, const SurfaceParams params, | 1239 | static std::optional<u32> TryFindBestLayer(GPUVAddr addr, const SurfaceParams params, u32 mipmap) { |
| 1248 | u32 mipmap) { | ||
| 1249 | const std::size_t size{params.LayerMemorySize()}; | 1240 | const std::size_t size{params.LayerMemorySize()}; |
| 1250 | Tegra::GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)}; | 1241 | GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)}; |
| 1251 | for (u32 i = 0; i < params.depth; i++) { | 1242 | for (u32 i = 0; i < params.depth; i++) { |
| 1252 | if (start == addr) { | 1243 | if (start == addr) { |
| 1253 | return {i}; | 1244 | return {i}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index c919dd29b..c644271d0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -109,6 +109,11 @@ struct SurfaceParams { | |||
| 109 | return size; | 109 | return size; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | /// Returns true if the parameters constitute a valid rasterizer surface. | ||
| 113 | bool IsValid() const { | ||
| 114 | return gpu_addr && host_ptr && height && width; | ||
| 115 | } | ||
| 116 | |||
| 112 | /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including | 117 | /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including |
| 113 | /// mipmaps. | 118 | /// mipmaps. |
| 114 | std::size_t LayerMemorySize() const { | 119 | std::size_t LayerMemorySize() const { |
| @@ -210,7 +215,7 @@ struct SurfaceParams { | |||
| 210 | 215 | ||
| 211 | /// Creates SurfaceParams for a depth buffer configuration | 216 | /// Creates SurfaceParams for a depth buffer configuration |
| 212 | static SurfaceParams CreateForDepthBuffer( | 217 | static SurfaceParams CreateForDepthBuffer( |
| 213 | u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, | 218 | u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format, |
| 214 | u32 block_width, u32 block_height, u32 block_depth, | 219 | u32 block_width, u32 block_height, u32 block_depth, |
| 215 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | 220 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); |
| 216 | 221 | ||
| @@ -232,7 +237,7 @@ struct SurfaceParams { | |||
| 232 | } | 237 | } |
| 233 | 238 | ||
| 234 | /// Initializes parameters for caching, should be called after everything has been initialized | 239 | /// Initializes parameters for caching, should be called after everything has been initialized |
| 235 | void InitCacheParameters(Tegra::GPUVAddr gpu_addr); | 240 | void InitCacheParameters(GPUVAddr gpu_addr); |
| 236 | 241 | ||
| 237 | std::string TargetName() const { | 242 | std::string TargetName() const { |
| 238 | switch (target) { | 243 | switch (target) { |
| @@ -297,7 +302,7 @@ struct SurfaceParams { | |||
| 297 | bool srgb_conversion; | 302 | bool srgb_conversion; |
| 298 | // Parameters used for caching | 303 | // Parameters used for caching |
| 299 | u8* host_ptr; | 304 | u8* host_ptr; |
| 300 | Tegra::GPUVAddr gpu_addr; | 305 | GPUVAddr gpu_addr; |
| 301 | std::size_t size_in_bytes; | 306 | std::size_t size_in_bytes; |
| 302 | std::size_t size_in_bytes_gl; | 307 | std::size_t size_in_bytes_gl; |
| 303 | 308 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1ed740877..1f8eca6f0 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -32,7 +32,7 @@ struct UnspecializedShader { | |||
| 32 | namespace { | 32 | namespace { |
| 33 | 33 | ||
| 34 | /// Gets the address for the specified shader stage program | 34 | /// Gets the address for the specified shader stage program |
| 35 | Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { | 35 | GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { |
| 36 | const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()}; | 36 | const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()}; |
| 37 | const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]}; | 37 | const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]}; |
| 38 | return gpu.regs.code_address.CodeAddress() + shader_config.offset; | 38 | return gpu.regs.code_address.CodeAddress() + shader_config.offset; |
| @@ -486,7 +486,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; | 488 | auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; |
| 489 | const Tegra::GPUVAddr program_addr{GetShaderAddress(program)}; | 489 | const GPUVAddr program_addr{GetShaderAddress(program)}; |
| 490 | 490 | ||
| 491 | // Look up shader in the cache based on address | 491 | // Look up shader in the cache based on address |
| 492 | const auto& host_ptr{memory_manager.GetPointer(program_addr)}; | 492 | const auto& host_ptr{memory_manager.GetPointer(program_addr)}; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 95eab3fec..eac51ecb3 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -39,8 +39,7 @@ VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, | |||
| 39 | 39 | ||
| 40 | VKBufferCache::~VKBufferCache() = default; | 40 | VKBufferCache::~VKBufferCache() = default; |
| 41 | 41 | ||
| 42 | u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, | 42 | u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment, bool cache) { |
| 43 | bool cache) { | ||
| 44 | const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; | 43 | const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; |
| 45 | ASSERT_MSG(cpu_addr, "Invalid GPU address"); | 44 | ASSERT_MSG(cpu_addr, "Invalid GPU address"); |
| 46 | 45 | ||
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 8b415744b..08b786aad 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -68,8 +68,7 @@ public: | |||
| 68 | 68 | ||
| 69 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been | 69 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been |
| 70 | /// allocated. | 70 | /// allocated. |
| 71 | u64 UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, | 71 | u64 UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, bool cache = true); |
| 72 | bool cache = true); | ||
| 73 | 72 | ||
| 74 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. | 73 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. |
| 75 | u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); | 74 | u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); |
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 29f01dfb2..11023ed63 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp | |||
| @@ -261,7 +261,7 @@ void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | |||
| 261 | 261 | ||
| 262 | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | 262 | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { |
| 263 | if (surface_address != new_value) { | 263 | if (surface_address != new_value) { |
| 264 | surface_address = static_cast<Tegra::GPUVAddr>(new_value); | 264 | surface_address = static_cast<GPUVAddr>(new_value); |
| 265 | 265 | ||
| 266 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | 266 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); |
| 267 | emit Update(); | 267 | emit Update(); |
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h index 323e39d94..89445b18f 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ b/src/yuzu/debugger/graphics/graphics_surface.h | |||
| @@ -87,7 +87,7 @@ private: | |||
| 87 | QPushButton* save_surface; | 87 | QPushButton* save_surface; |
| 88 | 88 | ||
| 89 | Source surface_source; | 89 | Source surface_source; |
| 90 | Tegra::GPUVAddr surface_address; | 90 | GPUVAddr surface_address; |
| 91 | unsigned surface_width; | 91 | unsigned surface_width; |
| 92 | unsigned surface_height; | 92 | unsigned surface_height; |
| 93 | Tegra::Texture::TextureFormat surface_format; | 93 | Tegra::Texture::TextureFormat surface_format; |