From b10db7e4a5f43414679b7969ea309b1829937a37 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 18 Feb 2020 17:20:39 -0400
Subject: [PATCH] FenceManager: Implement async buffer cache flushes on High
 settings

---
 src/video_core/buffer_cache/buffer_cache.h    | 50 +++++++++++++++++++
 src/video_core/fence_manager.h                | 15 ++++--
 src/video_core/gpu_thread.cpp                 |  2 +-
 .../renderer_opengl/gl_fence_manager.cpp      |  4 +-
 .../renderer_opengl/gl_fence_manager.h        |  5 +-
 .../renderer_opengl/gl_rasterizer.cpp         |  3 +-
 6 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index df4c0211e3..d72df90eff 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -21,6 +21,7 @@
 #include "common/common_types.h"
 #include "core/core.h"
 #include "core/memory.h"
+#include "core/settings.h"
 #include "video_core/buffer_cache/buffer_block.h"
 #include "video_core/buffer_cache/map_interval.h"
 #include "video_core/memory_manager.h"
@@ -80,6 +81,9 @@ public:
         auto map = MapAddress(block, gpu_addr, cpu_addr, size);
         if (is_written) {
             map->MarkAsModified(true, GetModifiedTicks());
+            if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+                AsyncFlushMap(map);
+            }
             if (!map->IsWritten()) {
                 map->MarkAsWritten(true);
                 MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1);
@@ -193,6 +197,39 @@ public:
         marked_for_unregister.clear();
     }
 
+    void CommitAsyncFlushes() {
+        commited_flushes.push_back(uncommited_flushes);
+        uncommited_flushes.reset();
+    }
+
+    bool ShouldWaitAsyncFlushes() {
+        if (commited_flushes.empty()) {
+            return false;
+        }
+        auto& flush_list = commited_flushes.front();
+        if (!flush_list) {
+            return false;
+        }
+        return true;
+    }
+
+    void PopAsyncFlushes() {
+        if (commited_flushes.empty()) {
+            return;
+        }
+        auto& flush_list = commited_flushes.front();
+        if (!flush_list) {
+            commited_flushes.pop_front();
+            return;
+        }
+        for (MapInterval& map : *flush_list) {
+            if (map->IsRegistered()) {
+                FlushMap(map);
+            }
+        }
+        commited_flushes.pop_front();
+    }
+
     virtual BufferType GetEmptyBuffer(std::size_t size) = 0;
 
 protected:
@@ -316,6 +353,9 @@ private:
         MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr);
         if (modified_inheritance) {
             new_map->MarkAsModified(true, GetModifiedTicks());
+            if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+                AsyncFlushMap(new_map);
+            }
         }
         Register(new_map, write_inheritance);
         return new_map;
@@ -502,6 +542,13 @@ private:
         return false;
     }
 
+    void AsyncFlushMap(MapInterval& map) {
+        if (!uncommited_flushes) {
+            uncommited_flushes = std::make_shared<std::list<MapInterval>>();
+        }
+        uncommited_flushes->push_back(map);
+    }
+
     VideoCore::RasterizerInterface& rasterizer;
     Core::System& system;
 
@@ -533,6 +580,9 @@ private:
     std::vector<u8> staging_buffer;
     std::list<MapInterval> marked_for_unregister;
 
+    std::shared_ptr<std::list<MapInterval>> uncommited_flushes{};
+    std::list<std::shared_ptr<std::list<MapInterval>>> commited_flushes;
+
     std::recursive_mutex mutex;
 };
 
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 036f3996c3..c4b190503f 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -6,8 +6,8 @@
 
 #include <algorithm>
 #include <array>
-#include <queue>
 #include <memory>
+#include <queue>
 
 #include "common/assert.h"
 #include "common/common_types.h"
@@ -37,7 +37,7 @@ private:
     u32 payload;
 };
 
-template <typename TFence, typename TTextureCache>
+template <typename TFence, typename TTextureCache, typename TTBufferCache>
 class FenceManager {
 public:
     void SignalFence(GPUVAddr addr, u32 value) {
@@ -46,6 +46,7 @@ public:
         QueueFence(new_fence);
         fences.push(new_fence);
         texture_cache.CommitAsyncFlushes();
+        buffer_cache.CommitAsyncFlushes();
         rasterizer.FlushCommands();
         rasterizer.SyncGuestHost();
     }
@@ -54,10 +55,12 @@ public:
         while (!fences.empty()) {
             TFence& current_fence = fences.front();
             bool should_wait = texture_cache.ShouldWaitAsyncFlushes();
+            should_wait |= buffer_cache.ShouldWaitAsyncFlushes();
             if (should_wait) {
                 WaitFence(current_fence);
             }
             texture_cache.PopAsyncFlushes();
+            buffer_cache.PopAsyncFlushes();
             auto& gpu{system.GPU()};
             auto& memory_manager{gpu.MemoryManager()};
             memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
@@ -67,8 +70,9 @@ public:
 
 protected:
     FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
-                 TTextureCache& texture_cache)
-        : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache} {}
+                 TTextureCache& texture_cache, TTBufferCache& buffer_cache)
+        : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache}, buffer_cache{
+                                                                                    buffer_cache} {}
 
     virtual TFence CreateFence(GPUVAddr addr, u32 value) = 0;
     virtual void QueueFence(TFence& fence) = 0;
@@ -78,16 +82,19 @@ protected:
     Core::System& system;
     VideoCore::RasterizerInterface& rasterizer;
     TTextureCache& texture_cache;
+    TTBufferCache& buffer_cache;
 
 private:
     void TryReleasePendingFences() {
         while (!fences.empty()) {
             TFence& current_fence = fences.front();
             bool should_wait = texture_cache.ShouldWaitAsyncFlushes();
+            should_wait |= buffer_cache.ShouldWaitAsyncFlushes();
             if (should_wait && !IsFenceSignaled(current_fence)) {
                 return;
             }
             texture_cache.PopAsyncFlushes();
+            buffer_cache.PopAsyncFlushes();
             auto& gpu{system.GPU()};
             auto& memory_manager{gpu.MemoryManager()};
             memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 1c3ab2145d..3e2be00e9d 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -81,7 +81,7 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
 }
 
 void ThreadManager::FlushRegion(VAddr addr, u64 size) {
-    if (!Settings::IsGPULevelExtreme()) {
+    if (!Settings::IsGPULevelHigh()) {
         return;
     }
     if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) {
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 4517ef1507..69dd3211b0 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -33,8 +33,8 @@ void GLInnerFence::Wait() {
 }
 
 FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
-                               TextureCacheOpenGL& texture_cache)
-    : GenericFenceManager(system, rasterizer, texture_cache) {}
+                               TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache)
+    : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache) {}
 
 Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value) {
     return std::make_shared<GLInnerFence>(addr, value);
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index 3cfa8b1d01..b48d5eaa0d 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -9,6 +9,7 @@
 
 #include "common/common_types.h"
 #include "video_core/fence_manager.h"
+#include "video_core/renderer_opengl/gl_buffer_cache.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 
@@ -30,12 +31,12 @@ private:
 };
 
 using Fence = std::shared_ptr<GLInnerFence>;
-using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCacheOpenGL>;
+using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCacheOpenGL, OGLBufferCache>;
 
 class FenceManagerOpenGL final : public GenericFenceManager {
 public:
     FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
-                   TextureCacheOpenGL& texture_cache);
+                       TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache);
 
 protected:
     Fence CreateFence(GPUVAddr addr, u32 value) override;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index db7eae065c..88914828ca 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -102,7 +102,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
       shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
       screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
       buffer_cache{*this, system, device, STREAM_BUFFER_SIZE}, fence_manager{system, *this,
-                                                                             texture_cache} {
+                                                                             texture_cache,
+                                                                             buffer_cache} {
     CheckExtensions();
 }