diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 641e38934b..7dbecb4fe1 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -791,6 +791,11 @@ bool BufferCache
::IsRegionGpuModified(DAddr addr, size_t size) {
return is_dirty;
}
+template
+bool BufferCache::IsRegionGpuModifiedCheap(DAddr addr, size_t size) {
+ return memory_tracker.IsRegionGpuModifiedCheap(addr, size);
+}
+
template
bool BufferCache::IsRegionRegistered(DAddr addr, size_t size) {
const DAddr end_addr = addr + size;
@@ -1025,7 +1030,8 @@ void BufferCache
::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
}();
const bool use_fast_buffer = needs_alignment_stream
|| (has_host_buffer && size <= channel_state->uniform_buffer_skip_cache_size
- && !memory_tracker.IsRegionGpuModified(device_addr, size));
+ && !(memory_tracker.IsRegionGpuModifiedCheap(device_addr, size)
+ && memory_tracker.IsRegionGpuModified(device_addr, size)));
if (use_fast_buffer) {
if constexpr (IS_OPENGL) {
if (runtime.HasFastBufferSubData()) {
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 3b900b95f9..622f222f2b 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -300,6 +300,8 @@ public:
/// Return true when a CPU region is modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(DAddr addr, size_t size);
+ [[nodiscard]] bool IsRegionGpuModifiedCheap(DAddr addr, size_t size);
+
/// Return true when a region is registered on the cache
[[nodiscard]] bool IsRegionRegistered(DAddr addr, size_t size);
diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h
index 4c9e978484..329c7e890d 100644
--- a/src/video_core/buffer_cache/memory_tracker_base.h
+++ b/src/video_core/buffer_cache/memory_tracker_base.h
@@ -69,6 +69,24 @@ public:
return false;
}
+ [[nodiscard]] bool IsRegionGpuModifiedCheap(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 = top_tier[page_index].load(std::memory_order_acquire);
+ if (manager != nullptr && manager->GpuModifiedPageCount() != 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) {
diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h
index 2e8717f195..0779adbd35 100644
--- a/src/video_core/buffer_cache/word_manager.h
+++ b/src/video_core/buffer_cache/word_manager.h
@@ -127,6 +127,7 @@ struct WordManager {
[[maybe_unused]] std::span cached_words = Span(Type::CachedCPU);
std::vector> ranges;
s64 cpu_delta = 0;
+ s64 gpu_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);
@@ -134,6 +135,9 @@ struct WordManager {
if (type == Type::CPU) {
const u64 old = state_words[index];
cpu_delta += enable ? std::popcount(~old & mask) : -std::popcount(old & mask);
+ } else if (type == Type::GPU) {
+ const u64 old = state_words[index];
+ gpu_delta += enable ? std::popcount(~old & mask) : -std::popcount(old & mask);
}
if (enable) {
state_words[index] |= mask;
@@ -152,6 +156,9 @@ struct WordManager {
if (cpu_delta != 0) {
cpu_modified_pages.fetch_add(static_cast(cpu_delta), std::memory_order_release);
}
+ if (gpu_delta != 0) {
+ gpu_modified_pages.fetch_add(static_cast(gpu_delta), std::memory_order_release);
+ }
if (!ranges.empty()) {
ApplyCollectedRanges(ranges, (!enable) ? 1 : -1);
}
@@ -180,6 +187,7 @@ struct WordManager {
};
std::vector> ranges;
s64 cpu_delta = 0;
+ s64 gpu_delta = 0;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if (type == Type::GPU)
mask &= ~untracked_words[index];
@@ -190,6 +198,8 @@ struct WordManager {
}
if (type == Type::CPU)
cpu_delta -= std::popcount(word);
+ else if (type == Type::GPU)
+ gpu_delta -= std::popcount(word);
state_words[index] &= ~mask;
if (type == Type::CPU || type == Type::CachedCPU)
untracked_words[index] &= ~mask;
@@ -214,6 +224,9 @@ struct WordManager {
if (cpu_delta != 0) {
cpu_modified_pages.fetch_add(static_cast(cpu_delta), std::memory_order_release);
}
+ if (gpu_delta != 0) {
+ gpu_modified_pages.fetch_add(static_cast(gpu_delta), std::memory_order_release);
+ }
if (pending) {
release();
}
@@ -348,10 +361,15 @@ struct WordManager {
return cpu_modified_pages.load(std::memory_order_acquire);
}
+ [[nodiscard]] u32 GpuModifiedPageCount() const noexcept {
+ return gpu_modified_pages.load(std::memory_order_acquire);
+ }
+
std::array heap = {};
DeviceTracker* tracker = nullptr;
VAddr cpu_addr = 0;
std::atomic cpu_modified_pages{0};
+ std::atomic gpu_modified_pages{0};
};
} // namespace VideoCommon
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index e7120fe8bb..b6664f93a4 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -684,9 +684,11 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
bool RasterizerVulkan::MustFlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which) {
if ((True(which & VideoCommon::CacheType::BufferCache))) {
- std::scoped_lock lock{buffer_cache.mutex};
- if (buffer_cache.IsRegionGpuModified(addr, size)) {
- return true;
+ if (buffer_cache.IsRegionGpuModifiedCheap(addr, size)) {
+ std::scoped_lock lock{buffer_cache.mutex};
+ if (buffer_cache.IsRegionGpuModified(addr, size)) {
+ return true;
+ }
}
}
if (!Settings::IsGPULevelHigh()) {