diff --git a/src/yuzu_video_core/CMakeLists.txt b/src/yuzu_video_core/CMakeLists.txt index b46d78fc..a623c411 100644 --- a/src/yuzu_video_core/CMakeLists.txt +++ b/src/yuzu_video_core/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(video_core STATIC buffer_cache/usage_tracker.h buffer_cache/word_manager.h cache_types.h + flush_region_cache_filter.h capture.h cdma_pusher.cpp cdma_pusher.h diff --git a/src/yuzu_video_core/buffer_cache/buffer_cache.h b/src/yuzu_video_core/buffer_cache/buffer_cache.h index 9d56a7bf..b6df4dcd 100644 --- a/src/yuzu_video_core/buffer_cache/buffer_cache.h +++ b/src/yuzu_video_core/buffer_cache/buffer_cache.h @@ -1411,6 +1411,7 @@ void BufferCache

::ChangeRegister(BufferId buffer_id) { Buffer& buffer = slot_buffers[buffer_id]; const auto size = buffer.SizeBytes(); if (insert) { + device_memory.RegisterFlushCacheRegion(buffer.CpuAddr(), size, CacheType::BufferCache); total_used_memory += Common::AlignUp(size, 1024); buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick)); } else { diff --git a/src/yuzu_video_core/device_memory_manager.h b/src/yuzu_video_core/device_memory_manager.h index b96e6905..078c338b 100644 --- a/src/yuzu_video_core/device_memory_manager.h +++ b/src/yuzu_video_core/device_memory_manager.h @@ -15,6 +15,7 @@ #include "yuzu_common/range_mutex.h" #include "yuzu_common/scratch_buffer.h" #include "yuzu_common/virtual_buffer.h" +#include "yuzu_video_core/flush_region_cache_filter.h" #include #include @@ -111,6 +112,16 @@ public: void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta); + // Presence remains conservative across deletion, remapping and address reuse. + void RegisterFlushCacheRegion(DAddr addr, u64 size, VideoCommon::CacheType types) { + flush_cache_filter.Register(addr, size, types); + } + + [[nodiscard]] VideoCommon::CacheType FilterFlushCaches( + DAddr addr, u64 size, VideoCommon::CacheType requested) const { + return flush_cache_filter.Filter(addr, size, requested); + } + static constexpr size_t AS_BITS = Traits::device_virtual_bits; private: @@ -208,6 +219,7 @@ private: (1ULL << (device_virtual_bits - page_bits)) / subentries; using CachedPages = std::array; std::unique_ptr cached_pages; + VideoCommon::FlushRegionCacheFilter flush_cache_filter; Common::RangeMutex counter_guard; std::mutex mapping_guard; }; diff --git a/src/yuzu_video_core/flush_region_cache_filter.h b/src/yuzu_video_core/flush_region_cache_filter.h new file mode 100644 index 00000000..1e6e1156 --- /dev/null +++ b/src/yuzu_video_core/flush_region_cache_filter.h @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include +#include +#include "yuzu_video_core/cache_types.h" + +namespace VideoCommon { + +// Conservative presence filter, not a dirty-memory tracker. Bits are never cleared: +// deletion, remapping and address reuse can only produce extra cache consultations. +// Register each resource before publishing it in the cache's lookup tables. +template +class FlushRegionCacheFilter { + static_assert(PageBits < AddressBits && AddressBits < 64); + static constexpr u64 AddressLimit = u64{1} << AddressBits; + static constexpr size_t WordCount = ((AddressLimit >> PageBits) + 63) / 64; + static constexpr std::array Types{CacheType::TextureCache, CacheType::QueryCache, + CacheType::BufferCache}; + +public: + void Register(DAddr addr, u64 size, CacheType types) noexcept { + if (size == 0) { + return; + } + if (addr >= AddressLimit || size > AddressLimit - addr) { + // An invalid/unsupported registration must not leave a false negative. + unbounded.fetch_or(static_cast(types), std::memory_order_release); + return; + } + const u64 first = addr >> PageBits; + const u64 last = (addr + size - 1) >> PageBits; + for (size_t cache = 0; cache < Types.size(); ++cache) { + if (False(types & Types[cache])) { + continue; + } + for (u64 page = first; page <= last;) { + const unsigned begin = static_cast(page & 63); + const u64 word = page >> 6; + const unsigned end = static_cast( + (last >> 6) == word ? (last & 63) + 1 : 64); + const u64 mask = (~u64{0} << begin) & + (end == 64 ? ~u64{0} : (u64{1} << end) - 1); + pages[cache][word].fetch_or(mask, std::memory_order_release); + page = (word + 1) << 6; + } + } + } + + [[nodiscard]] CacheType Filter(DAddr addr, u64 size, CacheType requested) const noexcept { + if (size == 0) { + return CacheType::None; + } + if (addr >= AddressLimit || size > AddressLimit - addr) { + return requested; + } + const auto fallback = static_cast(unbounded.load(std::memory_order_acquire)); + const u64 first = addr >> PageBits; + const u64 last = (addr + size - 1) >> PageBits; + for (size_t cache = 0; cache < Types.size(); ++cache) { + if (False(requested & Types[cache]) || True(fallback & Types[cache])) { + continue; + } + bool present = false; + for (u64 page = first; page <= last; ++page) { + if ((pages[cache][page >> 6].load(std::memory_order_acquire) & + (u64{1} << (page & 63))) != 0) { + present = true; + break; + } + } + if (!present) { + requested &= ~Types[cache]; + } + } + return requested; + } + +private: + std::array, WordCount>, Types.size()> pages{}; + std::atomic unbounded{}; +}; + +} // namespace VideoCommon diff --git a/src/yuzu_video_core/query_cache/query_cache.h b/src/yuzu_video_core/query_cache/query_cache.h index 33c65373..bee36a0c 100644 --- a/src/yuzu_video_core/query_cache/query_cache.h +++ b/src/yuzu_video_core/query_cache/query_cache.h @@ -244,6 +244,8 @@ void QueryCacheBase::CounterReport(GPUVAddr addr, QueryType counter_type return; } DAddr cpu_addr = *cpu_addr_opt; + impl->device_memory.RegisterFlushCacheRegion(cpu_addr, has_timestamp ? 16 : 4, + CacheType::QueryCache); const size_t new_query_id = streamer->WriteCounter(cpu_addr, has_timestamp, payload, subreport); auto* query = streamer->GetQuery(new_query_id); if (is_fence) { diff --git a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp index 111e8e3c..130c9c3f 100644 --- a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp @@ -534,6 +534,13 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType if (addr == 0 || size == 0) { return; } + // Registered pages retain the existing lock/download/fence path. + which = device_memory.FilterFlushCaches(addr, size, which); + if (False(which & (VideoCommon::CacheType::TextureCache | + VideoCommon::CacheType::BufferCache | + VideoCommon::CacheType::QueryCache))) { + return; + } if (True(which & VideoCommon::CacheType::TextureCache)) { std::scoped_lock lock{texture_cache.mutex}; texture_cache.DownloadMemory(addr, size); diff --git a/src/yuzu_video_core/texture_cache/texture_cache.h b/src/yuzu_video_core/texture_cache/texture_cache.h index 329fbbca..d53d49bd 100644 --- a/src/yuzu_video_core/texture_cache/texture_cache.h +++ b/src/yuzu_video_core/texture_cache/texture_cache.h @@ -2053,6 +2053,8 @@ void TextureCache

::RegisterImage(ImageId image_id) { (*channel_state->gpu_page_table)[page].push_back(image_id); }); if (False(image.flags & ImageFlagBits::Sparse)) { + device_memory.RegisterFlushCacheRegion(image.cpu_addr, image.guest_size_bytes, + CacheType::TextureCache); auto map_id = slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, @@ -2063,6 +2065,7 @@ void TextureCache

::RegisterImage(ImageId image_id) { boost::container::small_vector sparse_maps; ForEachSparseSegment( image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, DAddr cpu_addr, size_t size) { + device_memory.RegisterFlushCacheRegion(cpu_addr, size, CacheType::TextureCache); auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); ForEachCPUPage(cpu_addr, size, [this, map_id](u64 page) { page_table[page].push_back(map_id); }); diff --git a/src/yuzu_video_core/yuzu_video_core.vcxproj b/src/yuzu_video_core/yuzu_video_core.vcxproj index 51b49a6e..ba1e4265 100644 --- a/src/yuzu_video_core/yuzu_video_core.vcxproj +++ b/src/yuzu_video_core/yuzu_video_core.vcxproj @@ -39,6 +39,7 @@ + diff --git a/src/yuzu_video_core/yuzu_video_core.vcxproj.filters b/src/yuzu_video_core/yuzu_video_core.vcxproj.filters index b79acafb..67761f2f 100644 --- a/src/yuzu_video_core/yuzu_video_core.vcxproj.filters +++ b/src/yuzu_video_core/yuzu_video_core.vcxproj.filters @@ -267,6 +267,9 @@ Header Files + + Header Files + Header Files