From 7936dd7386acfae8e2d7ae4886a4bb6692fd716a Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 24 Nov 2024 12:55:30 +0300 Subject: [PATCH] track potentially dirty images and hash them --- src/video_core/texture_cache/image.h | 6 +++-- .../texture_cache/texture_cache.cpp | 25 ++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 8d84277d8..fdf536fd7 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -22,9 +22,10 @@ VK_DEFINE_HANDLE(VmaAllocator) namespace VideoCore { enum ImageFlagBits : u32 { - CpuDirty = 1 << 1, ///< Contents have been modified from the CPU + MaybeCpuDirty = 1 << 0, ///< The page this image is in was touched before the image address + CpuDirty = 1 << 1, ///< Contents have been modified from the CPU GpuDirty = 1 << 2, ///< Contents have been modified from the GPU (valid data in buffer cache) - Dirty = CpuDirty | GpuDirty, + Dirty = CpuDirty | GpuDirty | MaybeCpuDirty, GpuModified = 1 << 3, ///< Contents have been modified from the GPU Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU Registered = 1 << 6, ///< True when the image is registered @@ -130,6 +131,7 @@ struct Image { std::vector subresource_states{}; boost::container::small_vector mip_hashes{}; u64 tick_accessed_last{0}; + u64 hash{0}; struct { union { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 81d1d6b7d..129c1af8c 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -47,7 +47,17 @@ void TextureCache::InvalidateMemory(VAddr addr, VAddr addr_aligned, size_t size) std::scoped_lock lock{mutex}; ForEachImageInRegion(addr_aligned, size, [&](ImageId image_id, Image& image) { const auto image_end = image.info.guest_address + image.info.guest_size_bytes; - if (addr < image_end) { + const auto page_end = addr_aligned + size; + if (addr < image.info.guest_address) { + // This page access may or may not modify the image. + // We should not mark it as dirty now, if it really was modified, + // it will receive more invalidations on subsequent pages. + if (image_end < page_end) { + // Image ends on this page so it can not receive any more invalidations. + // We will check it's hash later to see if it really was modified. + image.flags |= ImageFlagBits::MaybeCpuDirty; + } + } else if (addr < image_end) { // Ensure image is reuploaded when accessed again. image.flags |= ImageFlagBits::CpuDirty; } @@ -418,6 +428,19 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + if (True(image.flags & ImageFlagBits::MaybeCpuDirty) && + False(image.flags & ImageFlagBits::CpuDirty)) { + // The image size should be less than page size to be considered MaybeCpuDirty + // So this calculation should be very uncommon and reasonably fast + ASSERT(image.info.guest_size_bytes <= 4_KB); + const u8* addr = std::bit_cast(image.info.guest_address); + const u64 hash = XXH3_64bits(addr, image.info.guest_size_bytes); + if (image.hash == hash) { + return; + } + image.hash = hash; + } + const auto& num_layers = image.info.resources.layers; const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size());