From 7a9d9e575b4d85ca30fc119dfb73c1b22a6dbe63 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Thu, 24 Mar 2022 21:30:54 +0100
Subject: [PATCH 1/2] Texture Cache: Add Cached CPU system.

---
 .../renderer_opengl/gl_rasterizer.cpp         |  6 ++-
 .../renderer_vulkan/vk_rasterizer.cpp         |  6 ++-
 src/video_core/texture_cache/image_base.h     |  3 ++
 src/video_core/texture_cache/texture_cache.h  | 41 ++++++++++++++++++-
 .../texture_cache/texture_cache_base.h        | 11 +++++
 5 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4d632d2113..7e06d00696 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -352,7 +352,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
     shader_cache.OnCPUWrite(addr, size);
     {
         std::scoped_lock lock{texture_cache.mutex};
-        texture_cache.WriteMemory(addr, size);
+        texture_cache.CachedWriteMemory(addr, size);
     }
     {
         std::scoped_lock lock{buffer_cache.mutex};
@@ -363,6 +363,10 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
 void RasterizerOpenGL::SyncGuestHost() {
     MICROPROFILE_SCOPE(OpenGL_CacheManagement);
     shader_cache.SyncGuestHost();
+    {
+        std::scoped_lock lock{texture_cache.mutex};
+        texture_cache.FlushCachedWrites();
+    }
     {
         std::scoped_lock lock{buffer_cache.mutex};
         buffer_cache.FlushCachedWrites();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index fa87d37f85..dd6e0027e1 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -408,7 +408,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
     pipeline_cache.OnCPUWrite(addr, size);
     {
         std::scoped_lock lock{texture_cache.mutex};
-        texture_cache.WriteMemory(addr, size);
+        texture_cache.CachedWriteMemory(addr, size);
     }
     {
         std::scoped_lock lock{buffer_cache.mutex};
@@ -418,6 +418,10 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
 
 void RasterizerVulkan::SyncGuestHost() {
     pipeline_cache.SyncGuestHost();
+    {
+        std::scoped_lock lock{texture_cache.mutex};
+        texture_cache.FlushCachedWrites();
+    }
     {
         std::scoped_lock lock{buffer_cache.mutex};
         buffer_cache.FlushCachedWrites();
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index dd01064324..cc79990279 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -39,6 +39,9 @@ enum class ImageFlagBits : u32 {
     Rescaled = 1 << 13,
     CheckingRescalable = 1 << 14,
     IsRescalable = 1 << 15,
+
+    // Cached CPU
+    CachedCpuModified = 1 << 16, ///< Contents have been modified from the CPU
 };
 DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
 
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index efc1c45257..099b2ae1ba 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -437,6 +437,23 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
     });
 }
 
+template <class P>
+void TextureCache<P>::CachedWriteMemory(VAddr cpu_addr, size_t size) {
+    const VAddr new_cpu_addr = Common::AlignDown(cpu_addr, CPU_PAGE_SIZE);
+    const size_t new_size = Common::AlignUp(size + cpu_addr - new_cpu_addr, CPU_PAGE_SIZE);
+    ForEachImageInRegion(new_cpu_addr, new_size, [this](ImageId image_id, Image& image) {
+        if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
+            return;
+        }
+        image.flags |= ImageFlagBits::CachedCpuModified;
+        cached_cpu_invalidate.insert(image_id);
+
+        if (True(image.flags & ImageFlagBits::Tracked)) {
+            UntrackImage(image, image_id);
+        }
+    });
+}
+
 template <class P>
 void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
     std::vector<ImageId> images;
@@ -494,6 +511,18 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
     }
 }
 
+template <class P>
+void TextureCache<P>::FlushCachedWrites() {
+    for (ImageId image_id : cached_cpu_invalidate) {
+        Image& image = slot_images[image_id];
+        if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
+            image.flags &= ~ImageFlagBits::CachedCpuModified;
+            image.flags |= ImageFlagBits::CpuModified;
+        }
+    }
+    cached_cpu_invalidate.clear();
+}
+
 template <class P>
 void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
                                 const Tegra::Engines::Fermi2D::Surface& src,
@@ -1560,6 +1589,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
 template <class P>
 void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
     ASSERT(False(image.flags & ImageFlagBits::Tracked));
+    if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
+        return;
+    }
     image.flags |= ImageFlagBits::Tracked;
     if (False(image.flags & ImageFlagBits::Sparse)) {
         rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
@@ -1616,6 +1648,9 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
         tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
     }
     total_used_memory -= Common::AlignUp(tentative_size, 1024);
+    if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
+        cached_cpu_invalidate.erase(image_id);
+    }
     const GPUVAddr gpu_addr = image.gpu_addr;
     const auto alloc_it = image_allocs_table.find(gpu_addr);
     if (alloc_it == image_allocs_table.end()) {
@@ -1782,7 +1817,11 @@ template <class P>
 void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) {
     Image& image = slot_images[image_id];
     if (invalidate) {
-        image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
+        if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
+            cached_cpu_invalidate.erase(image_id);
+        }
+        image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified |
+                         ImageFlagBits::CachedCpuModified);
         if (False(image.flags & ImageFlagBits::Tracked)) {
             TrackImage(image, image_id);
         }
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index b1324edf31..ad5978a333 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -8,6 +8,7 @@
 #include <span>
 #include <type_traits>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 #include <queue>
 
@@ -50,6 +51,9 @@ class TextureCache {
     /// Address shift for caching images into a hash table
     static constexpr u64 PAGE_BITS = 20;
 
+    static constexpr u64 CPU_PAGE_BITS = 12;
+    static constexpr u64 CPU_PAGE_SIZE = 1ULL << CPU_PAGE_BITS;
+
     /// Enables debugging features to the texture cache
     static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
     /// Implement blits as copies between framebuffers
@@ -136,6 +140,9 @@ public:
     /// Mark images in a range as modified from the CPU
     void WriteMemory(VAddr cpu_addr, size_t size);
 
+    /// Mark images in a range as modified from the CPU
+    void CachedWriteMemory(VAddr cpu_addr, size_t size);
+
     /// Download contents of host images to guest memory in a region
     void DownloadMemory(VAddr cpu_addr, size_t size);
 
@@ -145,6 +152,8 @@ public:
     /// Remove images in a region
     void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
 
+    void FlushCachedWrites();
+
     /// Blit an image with the given parameters
     void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
                    const Tegra::Engines::Fermi2D::Surface& src,
@@ -366,6 +375,8 @@ private:
 
     std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
 
+    std::unordered_set<ImageId> cached_cpu_invalidate;
+
     VAddr virtual_invalid_space{};
 
     bool has_deleted_images = false;

From a2d7b2f905eef379f6412ef043100a98845aefb1 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Thu, 24 Mar 2022 21:54:48 +0100
Subject: [PATCH 2/2] Memory: Don't protect reads on Normal accuracy.

---
 src/core/memory.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 28d30eee29..3fed514008 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -322,7 +322,7 @@ struct Memory::Impl {
         }
 
         if (Settings::IsFastmemEnabled()) {
-            const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
+            const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
             system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
         }