diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | 3 | ||||
| -rw-r--r-- | src/video_core/control/channel_state_cache.h | 10 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 8 | ||||
| -rw-r--r-- | src/video_core/gpu.h | 2 | ||||
| -rw-r--r-- | src/video_core/memory_manager.cpp | 11 | ||||
| -rw-r--r-- | src/video_core/rasterizer_interface.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.h | 2 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 27 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache_base.h | 4 |
12 files changed, 63 insertions, 16 deletions
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 344ddfc90..db2a6c3b2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 16 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 17 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 18 | #include "video_core/control/channel_state.h" | 18 | #include "video_core/control/channel_state.h" |
| 19 | #include "video_core/gpu.h" | ||
| 19 | #include "video_core/memory_manager.h" | 20 | #include "video_core/memory_manager.h" |
| 20 | #include "video_core/rasterizer_interface.h" | 21 | #include "video_core/rasterizer_interface.h" |
| 21 | 22 | ||
| @@ -24,6 +25,7 @@ namespace Service::Nvidia::Devices { | |||
| 24 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) | 25 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) |
| 25 | : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, | 26 | : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, |
| 26 | gmmu{} {} | 27 | gmmu{} {} |
| 28 | |||
| 27 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 29 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| 28 | 30 | ||
| 29 | NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 31 | NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -132,6 +134,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& | |||
| 132 | vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); | 134 | vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); |
| 133 | 135 | ||
| 134 | gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, VM::PAGE_SIZE_BITS); | 136 | gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, VM::PAGE_SIZE_BITS); |
| 137 | system.GPU().InitAddressSpace(*gmmu); | ||
| 135 | vm.initialised = true; | 138 | vm.initialised = true; |
| 136 | 139 | ||
| 137 | return NvResult::Success; | 140 | return NvResult::Success; |
diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 9b1d7362b..31d80e8b7 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <deque> | 3 | #include <deque> |
| 4 | #include <limits> | 4 | #include <limits> |
| 5 | #include <mutex> | 5 | #include <mutex> |
| 6 | #include <optional> | ||
| 6 | #include <unordered_map> | 7 | #include <unordered_map> |
| 7 | 8 | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| @@ -59,6 +60,15 @@ public: | |||
| 59 | return ref->second.gpu_memory; | 60 | return ref->second.gpu_memory; |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 63 | std::optional<size_t> getStorageID(size_t id) const { | ||
| 64 | std::unique_lock<std::mutex> lk(config_mutex); | ||
| 65 | const auto ref = address_spaces.find(id); | ||
| 66 | if (ref == address_spaces.end()) { | ||
| 67 | return std::nullopt; | ||
| 68 | } | ||
| 69 | return ref->second.storage_id; | ||
| 70 | } | ||
| 71 | |||
| 62 | protected: | 72 | protected: |
| 63 | static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()}; | 73 | static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()}; |
| 64 | 74 | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8c0ff0094..eebd7f3ff 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -73,6 +73,10 @@ struct GPU::Impl { | |||
| 73 | rasterizer->InitializeChannel(to_init); | 73 | rasterizer->InitializeChannel(to_init); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void InitAddressSpace(Tegra::MemoryManager& memory_manager) { | ||
| 77 | memory_manager.BindRasterizer(rasterizer); | ||
| 78 | } | ||
| 79 | |||
| 76 | void ReleaseChannel(Control::ChannelState& to_release) { | 80 | void ReleaseChannel(Control::ChannelState& to_release) { |
| 77 | UNIMPLEMENTED(); | 81 | UNIMPLEMENTED(); |
| 78 | } | 82 | } |
| @@ -452,6 +456,10 @@ void GPU::ReleaseChannel(Control::ChannelState& to_release) { | |||
| 452 | impl->ReleaseChannel(to_release); | 456 | impl->ReleaseChannel(to_release); |
| 453 | } | 457 | } |
| 454 | 458 | ||
| 459 | void GPU::InitAddressSpace(Tegra::MemoryManager& memory_manager) { | ||
| 460 | impl->InitAddressSpace(memory_manager); | ||
| 461 | } | ||
| 462 | |||
| 455 | void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer) { | 463 | void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer) { |
| 456 | impl->BindRenderer(std::move(renderer)); | 464 | impl->BindRenderer(std::move(renderer)); |
| 457 | } | 465 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 74d55e074..7e84b0d2f 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -118,6 +118,8 @@ public: | |||
| 118 | 118 | ||
| 119 | void ReleaseChannel(Control::ChannelState& to_release); | 119 | void ReleaseChannel(Control::ChannelState& to_release); |
| 120 | 120 | ||
| 121 | void InitAddressSpace(Tegra::MemoryManager& memory_manager); | ||
| 122 | |||
| 121 | /// Request a host GPU memory flush from the CPU. | 123 | /// Request a host GPU memory flush from the CPU. |
| 122 | [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); | 124 | [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); |
| 123 | 125 | ||
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index d4c0dca78..b36067613 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -59,10 +59,19 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp | |||
| 59 | } | 59 | } |
| 60 | for (u64 offset{}; offset < size; offset += page_size) { | 60 | for (u64 offset{}; offset < size; offset += page_size) { |
| 61 | const GPUVAddr current_gpu_addr = gpu_addr + offset; | 61 | const GPUVAddr current_gpu_addr = gpu_addr + offset; |
| 62 | [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); | ||
| 62 | SetEntry(current_gpu_addr, entry_type); | 63 | SetEntry(current_gpu_addr, entry_type); |
| 64 | if (current_entry_type != entry_type) { | ||
| 65 | rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); | ||
| 66 | } | ||
| 63 | if constexpr (entry_type == EntryType::Mapped) { | 67 | if constexpr (entry_type == EntryType::Mapped) { |
| 64 | const VAddr current_cpu_addr = cpu_addr + offset; | 68 | const VAddr current_cpu_addr = cpu_addr + offset; |
| 65 | const auto index = PageEntryIndex(current_gpu_addr); | 69 | const auto index = PageEntryIndex(current_gpu_addr); |
| 70 | const u32 sub_value = static_cast<u32>(current_cpu_addr >> 12ULL); | ||
| 71 | if (current_entry_type == entry_type && sub_value != page_table[index]) { | ||
| 72 | rasterizer->InvalidateRegion(static_cast<VAddr>(page_table[index]) << 12ULL, | ||
| 73 | page_size); | ||
| 74 | } | ||
| 66 | page_table[index] = static_cast<u32>(current_cpu_addr >> 12ULL); | 75 | page_table[index] = static_cast<u32>(current_cpu_addr >> 12ULL); |
| 67 | } | 76 | } |
| 68 | remaining_size -= page_size; | 77 | remaining_size -= page_size; |
| @@ -168,7 +177,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t s | |||
| 168 | const size_t page_last{(addr + size + page_size - 1) >> page_bits}; | 177 | const size_t page_last{(addr + size + page_size - 1) >> page_bits}; |
| 169 | while (page_index < page_last) { | 178 | while (page_index < page_last) { |
| 170 | const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; | 179 | const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; |
| 171 | if (page_addr && *page_addr != 0) { | 180 | if (page_addr) { |
| 172 | return page_addr; | 181 | return page_addr; |
| 173 | } | 182 | } |
| 174 | ++page_index; | 183 | ++page_index; |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 8dacb2626..5362aafb6 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -95,7 +95,7 @@ public: | |||
| 95 | virtual void UnmapMemory(VAddr addr, u64 size) = 0; | 95 | virtual void UnmapMemory(VAddr addr, u64 size) = 0; |
| 96 | 96 | ||
| 97 | /// Remap GPU memory range. This means underneath backing memory changed | 97 | /// Remap GPU memory range. This means underneath backing memory changed |
| 98 | virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0; | 98 | virtual void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) = 0; |
| 99 | 99 | ||
| 100 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 100 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 101 | /// and invalidated | 101 | /// and invalidated |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4eb9bd116..f745fbf56 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -379,10 +379,10 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { | |||
| 379 | shader_cache.OnCPUWrite(addr, size); | 379 | shader_cache.OnCPUWrite(addr, size); |
| 380 | } | 380 | } |
| 381 | 381 | ||
| 382 | void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { | 382 | void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
| 383 | { | 383 | { |
| 384 | std::scoped_lock lock{texture_cache.mutex}; | 384 | std::scoped_lock lock{texture_cache.mutex}; |
| 385 | texture_cache.UnmapGPUMemory(addr, size); | 385 | texture_cache.UnmapGPUMemory(as_id, addr, size); |
| 386 | } | 386 | } |
| 387 | } | 387 | } |
| 388 | 388 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 39b20cfb2..d469075a1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -82,7 +82,7 @@ public: | |||
| 82 | void OnCPUWrite(VAddr addr, u64 size) override; | 82 | void OnCPUWrite(VAddr addr, u64 size) override; |
| 83 | void SyncGuestHost() override; | 83 | void SyncGuestHost() override; |
| 84 | void UnmapMemory(VAddr addr, u64 size) override; | 84 | void UnmapMemory(VAddr addr, u64 size) override; |
| 85 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; | 85 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
| 86 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | 86 | void SignalSemaphore(GPUVAddr addr, u32 value) override; |
| 87 | void SignalSyncPoint(u32 value) override; | 87 | void SignalSyncPoint(u32 value) override; |
| 88 | void SignalReference() override; | 88 | void SignalReference() override; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7e0805544..50fdf5e18 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -441,10 +441,10 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { | |||
| 441 | pipeline_cache.OnCPUWrite(addr, size); | 441 | pipeline_cache.OnCPUWrite(addr, size); |
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { | 444 | void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
| 445 | { | 445 | { |
| 446 | std::scoped_lock lock{texture_cache.mutex}; | 446 | std::scoped_lock lock{texture_cache.mutex}; |
| 447 | texture_cache.UnmapGPUMemory(addr, size); | 447 | texture_cache.UnmapGPUMemory(as_id, addr, size); |
| 448 | } | 448 | } |
| 449 | } | 449 | } |
| 450 | 450 | ||
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 642fe6576..c836158b8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -78,7 +78,7 @@ public: | |||
| 78 | void OnCPUWrite(VAddr addr, u64 size) override; | 78 | void OnCPUWrite(VAddr addr, u64 size) override; |
| 79 | void SyncGuestHost() override; | 79 | void SyncGuestHost() override; |
| 80 | void UnmapMemory(VAddr addr, u64 size) override; | 80 | void UnmapMemory(VAddr addr, u64 size) override; |
| 81 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; | 81 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
| 82 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | 82 | void SignalSemaphore(GPUVAddr addr, u32 value) override; |
| 83 | void SignalSyncPoint(u32 value) override; | 83 | void SignalSyncPoint(u32 value) override; |
| 84 | void SignalReference() override; | 84 | void SignalReference() override; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 5ef07d18f..a483892e4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -480,12 +480,20 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { | |||
| 480 | } | 480 | } |
| 481 | 481 | ||
| 482 | template <class P> | 482 | template <class P> |
| 483 | void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { | 483 | void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { |
| 484 | std::vector<ImageId> deleted_images; | 484 | std::vector<ImageId> deleted_images; |
| 485 | ForEachImageInRegionGPU(gpu_addr, size, | 485 | ForEachImageInRegionGPU(as_id, gpu_addr, size, |
| 486 | [&](ImageId id, Image&) { deleted_images.push_back(id); }); | 486 | [&](ImageId id, Image&) { deleted_images.push_back(id); }); |
| 487 | for (const ImageId id : deleted_images) { | 487 | for (const ImageId id : deleted_images) { |
| 488 | Image& image = slot_images[id]; | 488 | Image& image = slot_images[id]; |
| 489 | if (True(image.flags & ImageFlagBits::CpuModified)) { | ||
| 490 | return; | ||
| 491 | } | ||
| 492 | image.flags |= ImageFlagBits::CpuModified; | ||
| 493 | if (True(image.flags & ImageFlagBits::Tracked)) { | ||
| 494 | UntrackImage(image, id); | ||
| 495 | } | ||
| 496 | /* | ||
| 489 | if (True(image.flags & ImageFlagBits::Remapped)) { | 497 | if (True(image.flags & ImageFlagBits::Remapped)) { |
| 490 | continue; | 498 | continue; |
| 491 | } | 499 | } |
| @@ -493,6 +501,7 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { | |||
| 493 | if (True(image.flags & ImageFlagBits::Tracked)) { | 501 | if (True(image.flags & ImageFlagBits::Tracked)) { |
| 494 | UntrackImage(image, id); | 502 | UntrackImage(image, id); |
| 495 | } | 503 | } |
| 504 | */ | ||
| 496 | } | 505 | } |
| 497 | } | 506 | } |
| 498 | 507 | ||
| @@ -1322,13 +1331,19 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f | |||
| 1322 | 1331 | ||
| 1323 | template <class P> | 1332 | template <class P> |
| 1324 | template <typename Func> | 1333 | template <typename Func> |
| 1325 | void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) { | 1334 | void TextureCache<P>::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, |
| 1335 | Func&& func) { | ||
| 1326 | using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; | 1336 | using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; |
| 1327 | static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; | 1337 | static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; |
| 1328 | boost::container::small_vector<ImageId, 8> images; | 1338 | boost::container::small_vector<ImageId, 8> images; |
| 1329 | ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { | 1339 | auto storage_id = getStorageID(as_id); |
| 1330 | const auto it = channel_state->gpu_page_table->find(page); | 1340 | if (!storage_id) { |
| 1331 | if (it == channel_state->gpu_page_table->end()) { | 1341 | return; |
| 1342 | } | ||
| 1343 | auto& gpu_page_table = gpu_page_table_storage[*storage_id]; | ||
| 1344 | ForEachGPUPage(gpu_addr, size, [this, gpu_page_table, &images, gpu_addr, size, func](u64 page) { | ||
| 1345 | const auto it = gpu_page_table.find(page); | ||
| 1346 | if (it == gpu_page_table.end()) { | ||
| 1332 | if constexpr (BOOL_BREAK) { | 1347 | if constexpr (BOOL_BREAK) { |
| 1333 | return false; | 1348 | return false; |
| 1334 | } else { | 1349 | } else { |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index b24968b03..2f4db5047 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -173,7 +173,7 @@ public: | |||
| 173 | void UnmapMemory(VAddr cpu_addr, size_t size); | 173 | void UnmapMemory(VAddr cpu_addr, size_t size); |
| 174 | 174 | ||
| 175 | /// Remove images in a region | 175 | /// Remove images in a region |
| 176 | void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); | 176 | void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); |
| 177 | 177 | ||
| 178 | /// Blit an image with the given parameters | 178 | /// Blit an image with the given parameters |
| 179 | void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, | 179 | void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, |
| @@ -309,7 +309,7 @@ private: | |||
| 309 | void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); | 309 | void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); |
| 310 | 310 | ||
| 311 | template <typename Func> | 311 | template <typename Func> |
| 312 | void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); | 312 | void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); |
| 313 | 313 | ||
| 314 | template <typename Func> | 314 | template <typename Func> |
| 315 | void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); | 315 | void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); |