diff options
Diffstat (limited to 'src/video_core/buffer_cache')
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 160 | ||||
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache_base.h | 26 | ||||
| -rw-r--r-- | src/video_core/buffer_cache/virtual_range_cache.h | 173 |
3 files changed, 331 insertions, 28 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e40aad1fb5..4c345dd213 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -113,6 +113,13 @@ void BufferCache<P>::TickFrame() { } template <class P> +void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { + if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) { + virtual_ranges.Unmap(as_id, gpu_addr, size); + } +} + +template <class P> void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { if (memory_tracker.IsRegionGpuModified(device_addr, size)) { ClearDownload(device_addr, size); @@ -208,8 +215,8 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am BufferId buffer_b; do { channel_state->has_deleted_buffers = false; - buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount)); - buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount)); + buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount), false); + buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount), false); } while (channel_state->has_deleted_buffers); auto& src_buffer = slot_buffers[buffer_a]; auto& dest_buffer = slot_buffers[buffer_b]; @@ -265,7 +272,7 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { ClearDownload(*cpu_dst_address, size); gpu_modified_ranges.Subtract(*cpu_dst_address, size); - const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); + const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size), false); Buffer& dest_buffer = slot_buffers[buffer]; const u32 offset = dest_buffer.Offset(*cpu_dst_address); runtime.ClearBuffer(dest_buffer, offset, size, value); @@ -287,7 +294,7 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad template <class P> std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer( DAddr device_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) { - const BufferId buffer_id = FindBuffer(device_addr, size); + const BufferId buffer_id = FindBuffer(device_addr, size, false); Buffer& buffer = slot_buffers[buffer_id]; // synchronize op @@ -999,10 +1006,84 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } template <class P> +void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written, + std::vector<MultiRangeSegment>& pool) { + binding.segment_first = 0; + binding.segment_count = 0; + if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) { + if (binding.gpu_addr == 0 || binding.size == 0) { + return; + } + if (is_written && !runtime.PrefersSparseSources()) { + return; + } + const VirtualSegments* found = + virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size); + if (!found || found->size() < 2) { + return; + } + const VirtualSegments segments = *found; + const u32 first = static_cast<u32>(pool.size()); + const bool prefer_sparse = runtime.PrefersSparseSources(); + for (const VirtualSegment& segment : segments) { + const BufferId buffer_id = + FindBuffer(segment.device_addr, segment.size, prefer_sparse); + if (!buffer_id) { + pool.resize(first); + return; + } + pool.push_back(MultiRangeSegment{ + .buffer_id = buffer_id, + .device_addr = segment.device_addr, + .size = segment.size, + }); + } + binding.segment_first = first; + binding.segment_count = static_cast<u32>(segments.size()); + } +} + +template <class P> +bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_written, + std::span<const MultiRangeSegment> pool) { + if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) { + if (binding.segment_count < 2) { + return false; + } + if (binding.segment_first + binding.segment_count > pool.size()) { + return false; + } + const u64 key = (static_cast<u64>(gpu_memory->GetID()) << 48) ^ binding.gpu_addr; + runtime.ResetMultiRange(); + for (u32 index = 0; index < binding.segment_count; ++index) { + const MultiRangeSegment& segment = pool[binding.segment_first + index]; + Buffer& buffer = slot_buffers[segment.buffer_id]; + TouchBuffer(buffer, segment.buffer_id); + if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) { + runtime.InvalidateMultiRange(key); + } + const u32 offset = buffer.Offset(segment.device_addr); + buffer.MarkUsage(offset, segment.size); + if (is_written) { + MarkWrittenBuffer(segment.buffer_id, segment.device_addr, segment.size); + } + runtime.PushMultiRangeSource(buffer, offset, segment.size); + } + return runtime.BindMultiRangeStorageBuffer(key, is_written); + } else { + return false; + } +} + +template <class P> void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { u32 binding_index = 0; ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { const Binding& binding = channel_state->storage_buffers[stage][index]; + const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; + if (BindMultiRangeStorage(binding, is_written, graphics_segments)) { + return; + } Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; @@ -1010,7 +1091,6 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, size); - const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; if (is_written) { MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); @@ -1139,6 +1219,11 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { u32 binding_index = 0; ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { const Binding& binding = channel_state->compute_storage_buffers[index]; + const bool is_written = + ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; + if (BindMultiRangeStorage(binding, is_written, compute_segments)) { + return; + } Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; @@ -1146,8 +1231,6 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, size); - const bool is_written = - ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; if (is_written) { MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); @@ -1193,6 +1276,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { template <class P> void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { + graphics_segments.clear(); BufferOperations([&]() { if (is_indexed) { UpdateIndexBuffer(); @@ -1212,6 +1296,7 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { template <class P> void BufferCache<P>::DoUpdateComputeBuffers() { + compute_segments.clear(); BufferOperations([&]() { UpdateComputeUniformBuffers(); UpdateComputeStorageBuffers(); @@ -1234,11 +1319,11 @@ void BufferCache<P>::UpdateIndexBuffer() { auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size()); u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE); if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] { - inline_buffer_id = CreateBuffer(0, buffer_size); + inline_buffer_id = CreateBuffer(0, buffer_size, false); } if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] { slot_buffers.erase(inline_buffer_id); - inline_buffer_id = CreateBuffer(0, buffer_size); + inline_buffer_id = CreateBuffer(0, buffer_size, false); } channel_state->index_buffer = Binding{ .device_addr = 0, @@ -1261,7 +1346,7 @@ void BufferCache<P>::UpdateIndexBuffer() { channel_state->index_buffer = Binding{ .device_addr = *device_addr, .size = size, - .buffer_id = FindBuffer(*device_addr, size), + .buffer_id = FindBuffer(*device_addr, size, false), }; } @@ -1298,7 +1383,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) { size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); } - const BufferId buffer_id = FindBuffer(*device_addr, size); + const BufferId buffer_id = FindBuffer(*device_addr, size, false); const Binding binding{ .device_addr = *device_addr, .size = size, @@ -1319,7 +1404,7 @@ void BufferCache<P>::UpdateDrawIndirect() { binding = Binding{ .device_addr = *device_addr, .size = static_cast<u32>(size), - .buffer_id = FindBuffer(*device_addr, static_cast<u32>(size)), + .buffer_id = FindBuffer(*device_addr, static_cast<u32>(size), false), }; }; if (current_draw_indirect->include_count) { @@ -1343,7 +1428,7 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) { channel_state->dirty_uniform_buffers[stage] |= 1U << index; } // Resolve buffer - binding.buffer_id = FindBuffer(binding.device_addr, binding.size); + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); }); } @@ -1352,8 +1437,10 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) { ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { // Resolve buffer Binding& binding = channel_state->storage_buffers[stage][index]; - const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size); + const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size, false); binding.buffer_id = buffer_id; + const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; + ResolveMultiRangeStorage(binding, is_written, graphics_segments); }); } @@ -1361,7 +1448,7 @@ template <class P> void BufferCache<P>::UpdateTextureBuffers(size_t stage) { ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { Binding& binding = channel_state->texture_buffers[stage][index]; - binding.buffer_id = FindBuffer(binding.device_addr, binding.size); + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); }); } @@ -1385,7 +1472,7 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) { channel_state->transform_feedback_buffers[index] = NULL_BINDING; return; } - const BufferId buffer_id = FindBuffer(*device_addr, size); + const BufferId buffer_id = FindBuffer(*device_addr, size, false); channel_state->transform_feedback_buffers[index] = Binding{ .device_addr = *device_addr, .size = size, @@ -1407,7 +1494,7 @@ void BufferCache<P>::UpdateComputeUniformBuffers() { binding.size = cbuf.size; } } - binding.buffer_id = FindBuffer(binding.device_addr, binding.size); + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); }); } @@ -1416,7 +1503,10 @@ void BufferCache<P>::UpdateComputeStorageBuffers() { ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { // Resolve buffer Binding& binding = channel_state->compute_storage_buffers[index]; - binding.buffer_id = FindBuffer(binding.device_addr, binding.size); + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); + const bool is_written = + ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; + ResolveMultiRangeStorage(binding, is_written, compute_segments); }); } @@ -1424,7 +1514,7 @@ template <class P> void BufferCache<P>::UpdateComputeTextureBuffers() { ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { Binding& binding = channel_state->compute_texture_buffers[index]; - binding.buffer_id = FindBuffer(binding.device_addr, binding.size); + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); }); } @@ -1440,7 +1530,7 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3 } template <class P> -BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) { +BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible) { if (device_addr == 0) { return NULL_BUFFER_ID; } @@ -1450,10 +1540,18 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) { Buffer& buffer = slot_buffers[buffer_id]; WaitForGpuFenceIfNeeded(buffer); if (buffer.IsInBounds(device_addr, size)) { - return buffer_id; + bool usable = true; + if constexpr (requires { buffer.IsSparseCompatible(); }) { + if (sparse_compatible && !buffer.IsSparseCompatible()) { + usable = false; + } + } + if (usable) { + return buffer_id; + } } } - return CreateBuffer(device_addr, size); + return CreateBuffer(device_addr, size, sparse_compatible); } template <class P> @@ -1575,13 +1673,15 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, } template <class P> -BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) { +BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size, + bool sparse_compatible) { DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE); device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE); wanted_size = static_cast<u32>(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast<u32>(overlap.end - overlap.begin); - const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size); + const BufferId new_buffer_id = + slot_buffers.insert(runtime, overlap.begin, size, sparse_compatible); auto& new_buffer = slot_buffers[new_buffer_id]; const size_t size_bytes = new_buffer.SizeBytes(); runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); @@ -1745,7 +1845,7 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_ ClearDownload(dest_address, copy_size); gpu_modified_ranges.Subtract(dest_address, copy_size); - BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); + BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size), false); auto& buffer = slot_buffers[buffer_id]; SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size)); @@ -1831,6 +1931,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64 template <class P> void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { + if constexpr (requires { runtime.OnBufferDeleted(slot_buffers[buffer_id]); }) { + runtime.OnBufferDeleted(slot_buffers[buffer_id]); + } bool dirty_index{false}; boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers; const auto scalar_replace = [buffer_id](Binding& binding) { @@ -1934,9 +2037,14 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, // The end address used for size calculation does not need to be aligned const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE); + u32 binding_size = static_cast<u32>(cpu_end - *aligned_device_addr); + if (is_written) { + binding_size = aligned_size; + } const Binding binding{ .device_addr = *aligned_device_addr, - .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_device_addr), + .gpu_addr = aligned_gpu_addr, + .size = binding_size, .buffer_id = BufferId{}, }; return binding; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 0a521192c6..0dbb368c47 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -29,6 +29,7 @@ #include "common/settings.h" #include "common/slot_vector.h" #include "video_core/buffer_cache/buffer_base.h" +#include "video_core/buffer_cache/virtual_range_cache.h" #include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" @@ -81,8 +82,17 @@ static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB); struct Binding { DAddr device_addr{}; + GPUVAddr gpu_addr{}; u32 size{}; BufferId buffer_id; + u32 segment_first{}; + u32 segment_count{}; +}; + +struct MultiRangeSegment { + BufferId buffer_id; + DAddr device_addr{}; + u32 size{}; }; struct TextureBufferBinding : Binding { @@ -215,6 +225,14 @@ public: void TickFrame(); + bool BindMultiRangeStorage(const Binding& binding, bool is_written, + std::span<const MultiRangeSegment> pool); + + void ResolveMultiRangeStorage(Binding& binding, bool is_written, + std::vector<MultiRangeSegment>& pool); + + void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); + void WriteMemory(DAddr device_addr, u64 size); void CachedWriteMemory(DAddr device_addr, u64 size); @@ -414,7 +432,7 @@ private: void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size); - [[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size); + [[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible); void WaitForGpuFenceIfNeeded(Buffer& buffer); @@ -422,7 +440,8 @@ private: void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score); - [[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size); + [[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size, + bool sparse_compatible); void Register(BufferId buffer_id); @@ -513,6 +532,9 @@ private: using TickType = u64; }; Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; + VirtualRangeCache virtual_ranges; + std::vector<MultiRangeSegment> graphics_segments; + std::vector<MultiRangeSegment> compute_segments; u64 frame_tick = 0; u64 total_used_memory = 0; u64 minimum_memory = 0; diff --git a/src/video_core/buffer_cache/virtual_range_cache.h b/src/video_core/buffer_cache/virtual_range_cache.h new file mode 100644 index 0000000000..f4284d6abb --- /dev/null +++ b/src/video_core/buffer_cache/virtual_range_cache.h @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <atomic> +#include <limits> +#include <mutex> +#include <optional> +#include <vector> + +#include <boost/container/small_vector.hpp> + +#include "common/common_types.h" +#include "common/container/unordered_map.h" +#include "video_core/memory_manager.h" + +namespace VideoCommon { + +struct VirtualSegment { + GPUVAddr gpu_addr; + DAddr device_addr; + u32 size; +}; + +using VirtualSegments = boost::container::small_vector<VirtualSegment, 8>; + +class VirtualRangeCache { +public: + static constexpr size_t MAX_ENTRIES = 8192; + static constexpr size_t MAX_DEFERRED = 4096; + + const VirtualSegments* Query(Tegra::MemoryManager& memory, GPUVAddr gpu_addr, u32 size) { + if (has_deferred.load(std::memory_order_acquire)) { + ApplyDeferred(); + } + if (entries.size() > MAX_ENTRIES) { + entries.clear(); + } + const size_t as_id = memory.GetID(); + const u64 key = MakeKey(as_id, gpu_addr); + const auto it = entries.find(key); + if (it != entries.end() && it->second.as_id == as_id && + it->second.gpu_addr == gpu_addr && it->second.size == size) { + return &it->second.segments; + } + Entry entry{}; + entry.as_id = as_id; + entry.gpu_addr = gpu_addr; + entry.size = size; + const auto ranges = memory.GetSubmappedRange(gpu_addr, size); + GPUVAddr expected = gpu_addr; + bool contiguous = true; + for (const auto& [range_addr, range_size] : ranges) { + if (range_addr != expected || range_size == 0) { + contiguous = false; + break; + } + const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr); + if (!device_addr || *device_addr == 0) { + contiguous = false; + break; + } + if (range_size > static_cast<size_t>((std::numeric_limits<u32>::max)())) { + contiguous = false; + break; + } + entry.segments.push_back(VirtualSegment{ + .gpu_addr = range_addr, + .device_addr = *device_addr, + .size = static_cast<u32>(range_size), + }); + expected += range_size; + } + if (!contiguous || expected != gpu_addr + size) { + entry.segments.clear(); + } + const auto result = entries.insert_or_assign(key, std::move(entry)); + return &result.first->second.segments; + } + + void Unmap(size_t as_id, GPUVAddr gpu_addr, u64 size) { + if (size == 0) { + return; + } + { + std::scoped_lock lock{deferred_mutex}; + if (!deferred.empty()) { + DeferredUnmap& last = deferred.back(); + if (last.as_id == as_id && last.gpu_addr + last.size == gpu_addr) { + last.size += size; + has_deferred.store(true, std::memory_order_release); + return; + } + } + if (deferred.size() >= MAX_DEFERRED) { + deferred.clear(); + deferred_overflow = true; + } else { + deferred.push_back(DeferredUnmap{ + .as_id = as_id, + .gpu_addr = gpu_addr, + .size = size, + }); + } + } + has_deferred.store(true, std::memory_order_release); + } + +private: + struct Entry { + VirtualSegments segments; + size_t as_id{}; + GPUVAddr gpu_addr{}; + u32 size{}; + }; + + struct DeferredUnmap { + size_t as_id; + GPUVAddr gpu_addr; + u64 size; + }; + + static u64 MakeKey(size_t as_id, GPUVAddr gpu_addr) { + return (static_cast<u64>(as_id) << 48) ^ gpu_addr; + } + + void ApplyDeferred() { + std::vector<DeferredUnmap> pending; + bool overflow = false; + { + std::scoped_lock lock{deferred_mutex}; + has_deferred.store(false, std::memory_order_release); + pending.swap(deferred); + overflow = deferred_overflow; + deferred_overflow = false; + } + if (overflow) { + entries.clear(); + return; + } + if (pending.empty() || entries.empty()) { + return; + } + for (auto it = entries.begin(); it != entries.end();) { + const Entry& entry = it->second; + const GPUVAddr entry_end = entry.gpu_addr + entry.size; + bool overlaps = false; + for (const DeferredUnmap& unmap : pending) { + if (unmap.as_id != entry.as_id) { + continue; + } + if (entry.gpu_addr < unmap.gpu_addr + unmap.size && unmap.gpu_addr < entry_end) { + overlaps = true; + break; + } + } + if (overlaps) { + it = entries.erase(it); + } else { + ++it; + } + } + } + + ::Common::unordered_map<u64, Entry> entries; + std::vector<DeferredUnmap> deferred; + std::mutex deferred_mutex; + std::atomic<bool> has_deferred{false}; + bool deferred_overflow{}; +}; + +} // namespace VideoCommon |
