diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index ffeb1e0a7e..641e38934b 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1721,6 +1721,9 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) { + if (!memory_tracker.HasCpuModifiedCheap(device_addr, size)) { + return true; + } upload_copies.clear(); u64 total_size_bytes = 0; u64 largest_copy = 0; diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index 887b4ff149..5ffa3b1542 100644 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -50,6 +51,25 @@ public: }); } + [[nodiscard]] bool HasCpuModifiedCheap(VAddr query_cpu_addr, u64 query_size) noexcept { + std::size_t remaining_size{query_size}; + std::size_t page_index{query_cpu_addr >> HIGHER_PAGE_BITS}; + u64 page_offset{query_cpu_addr & HIGHER_PAGE_MASK}; + while (remaining_size > 0) { + const std::size_t copy_amount{ + std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; + const Manager* manager = + std::atomic_ref(top_tier[page_index]).load(std::memory_order_acquire); + if (manager == nullptr || manager->CpuModifiedPageCount() != 0) { + return true; + } + page_index++; + page_offset = 0; + remaining_size -= copy_amount; + } + return false; + } + /// Returns true if a region has been modified from the CPU [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { return IteratePages(query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { @@ -260,7 +280,8 @@ private: void CreateRegion(std::size_t page_index) { const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS; - top_tier[page_index] = GetNewManager(base_cpu_addr); + std::atomic_ref(top_tier[page_index]) + .store(GetNewManager(base_cpu_addr), std::memory_order_release); } Manager* GetNewManager(VAddr base_cpu_address) { diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h index 76d608bbe0..2e8717f195 100644 --- a/src/video_core/buffer_cache/word_manager.h +++ b/src/video_core/buffer_cache/word_manager.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -48,6 +49,11 @@ struct WordManager { u64 const last_word = (~u64{0} << shift) >> shift; heap[num_words * size_t(Type::CPU) + num_words - 1] = last_word; heap[num_words * size_t(Type::Untracked) + num_words - 1] = last_word; + u32 cpu_pages = 0; + for (size_t i = 0; i < num_words; ++i) { + cpu_pages += static_cast(std::popcount(heap[num_words * size_t(Type::CPU) + i])); + } + cpu_modified_pages.store(cpu_pages, std::memory_order_relaxed); } explicit WordManager() = default; @@ -120,10 +126,15 @@ struct WordManager { [[maybe_unused]] std::span untracked_words = Span(Type::Untracked); [[maybe_unused]] std::span cached_words = Span(Type::CachedCPU); std::vector> ranges; + s64 cpu_delta = 0; IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) { if (type == Type::CPU || type == Type::CachedCPU) { CollectChangedRanges(!enable, index, untracked_words[index], mask, ranges); } + if (type == Type::CPU) { + const u64 old = state_words[index]; + cpu_delta += enable ? std::popcount(~old & mask) : -std::popcount(old & mask); + } if (enable) { state_words[index] |= mask; if (type == Type::CPU || type == Type::CachedCPU) @@ -138,6 +149,9 @@ struct WordManager { untracked_words[index] &= ~mask; } }); + if (cpu_delta != 0) { + cpu_modified_pages.fetch_add(static_cast(cpu_delta), std::memory_order_release); + } if (!ranges.empty()) { ApplyCollectedRanges(ranges, (!enable) ? 1 : -1); } @@ -165,6 +179,7 @@ struct WordManager { (pending_pointer - pending_offset) * BYTES_PER_PAGE); }; std::vector> ranges; + s64 cpu_delta = 0; IterateWords(offset, size, [&](size_t index, u64 mask) { if (type == Type::GPU) mask &= ~untracked_words[index]; @@ -173,6 +188,8 @@ struct WordManager { if (type == Type::CPU || type == Type::CachedCPU) { CollectChangedRanges(true, index, untracked_words[index], mask, ranges); } + if (type == Type::CPU) + cpu_delta -= std::popcount(word); state_words[index] &= ~mask; if (type == Type::CPU || type == Type::CachedCPU) untracked_words[index] &= ~mask; @@ -194,6 +211,9 @@ struct WordManager { } }); }); + if (cpu_delta != 0) { + cpu_modified_pages.fetch_add(static_cast(cpu_delta), std::memory_order_release); + } if (pending) { release(); } @@ -248,13 +268,18 @@ struct WordManager { auto const untracked_words = Span(Type::Untracked); auto const cpu_words = Span(Type::CPU); std::vector> ranges; + s64 cpu_delta = 0; for (u64 word_index = 0; word_index < num_words; ++word_index) { const u64 cached_bits = cached_words[word_index]; CollectChangedRanges(false, word_index, untracked_words[word_index], cached_bits, ranges); + cpu_delta += std::popcount(~cpu_words[word_index] & cached_bits); untracked_words[word_index] |= cached_bits; cpu_words[word_index] |= cached_bits; cached_words[word_index] = 0; } + if (cpu_delta != 0) { + cpu_modified_pages.fetch_add(static_cast(cpu_delta), std::memory_order_release); + } if (!ranges.empty()) { ApplyCollectedRanges(ranges, -1); } @@ -319,9 +344,14 @@ struct WordManager { return std::span(heap.data() + num_words * size_t(type), num_words); } + [[nodiscard]] u32 CpuModifiedPageCount() const noexcept { + return cpu_modified_pages.load(std::memory_order_acquire); + } + std::array heap = {}; DeviceTracker* tracker = nullptr; VAddr cpu_addr = 0; + std::atomic cpu_modified_pages{0}; }; } // namespace VideoCommon