From 8758932031ff4836652e7577ec566fd733f46e0b Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Mon, 22 May 2023 01:13:47 -0400
Subject: [PATCH] renderer_vulkan: barrier attachment feedback loops

---
 .../renderer_opengl/gl_texture_cache.h        |  4 +++
 .../renderer_vulkan/vk_graphics_pipeline.cpp  |  3 +-
 .../renderer_vulkan/vk_texture_cache.cpp      |  4 +++
 .../renderer_vulkan/vk_texture_cache.h        |  2 ++
 src/video_core/texture_cache/texture_cache.h  | 36 +++++++++++++++++++
 .../texture_cache/texture_cache_base.h        |  3 ++
 6 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 1190999a8d..3e9b3302b6 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -144,6 +144,10 @@ public:
         return state_tracker;
     }
 
+    void BarrierFeedbackLoop() const noexcept {
+        // OpenGL does not require a barrier for attachment feedback loops.
+    }
+
 private:
     struct StagingBuffers {
         explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index f1bcd5cd67..506b78f08e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -481,12 +481,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     if constexpr (Spec::enabled_stages[4]) {
         prepare_stage(4);
     }
+    texture_cache.UpdateRenderTargets(false);
+    texture_cache.CheckFeedbackLoop(views);
     ConfigureDraw(rescaling, render_area);
 }
 
 void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
                                      const RenderAreaPushConstant& render_area) {
-    texture_cache.UpdateRenderTargets(false);
     scheduler.RequestRenderpass(texture_cache.GetFramebuffer());
 
     if (!is_built.load(std::memory_order::relaxed)) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 4d0481f2a6..da5af25eb4 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -861,6 +861,10 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
     return *buffers[level];
 }
 
+void TextureCacheRuntime::BarrierFeedbackLoop() {
+    scheduler.RequestOutsideRenderPassOperationContext();
+}
+
 void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
                                            std::span<const VideoCommon::ImageCopy> copies) {
     std::vector<VkBufferImageCopy> vk_in_copies(copies.size());
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 4166b3d201..0f7a5ffd45 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -103,6 +103,8 @@ public:
 
     [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size);
 
+    void BarrierFeedbackLoop();
+
     const Device& device;
     Scheduler& scheduler;
     MemoryAllocator& memory_allocator;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index b24086fce5..8e62a5f78c 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -183,6 +183,42 @@ void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
                          views);
 }
 
+template <class P>
+void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
+    const bool requires_barrier = [&] {
+        for (const auto& view : views) {
+            if (!view.id) {
+                continue;
+            }
+            auto& image_view = slot_image_views[view.id];
+
+            // Check color targets
+            for (const auto& ct_view_id : render_targets.color_buffer_ids) {
+                if (ct_view_id) {
+                    auto& ct_view = slot_image_views[ct_view_id];
+                    if (image_view.image_id == ct_view.image_id) {
+                        return true;
+                    }
+                }
+            }
+
+            // Check zeta target
+            if (render_targets.depth_buffer_id) {
+                auto& zt_view = slot_image_views[render_targets.depth_buffer_id];
+                if (image_view.image_id == zt_view.image_id) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }();
+
+    if (requires_barrier) {
+        runtime.BarrierFeedbackLoop();
+    }
+}
+
 template <class P>
 typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
     if (index > channel_state->graphics_sampler_table.Limit()) {
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 0720494e55..1a3308e2db 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -148,6 +148,9 @@ public:
     /// Fill image_view_ids with the compute images in indices
     void FillComputeImageViews(std::span<ImageViewInOut> views);
 
+    /// Handle feedback loops during draws.
+    void CheckFeedbackLoop(std::span<const ImageViewInOut> views);
+
     /// Get the sampler from the graphics descriptor table in the specified index
     Sampler* GetGraphicsSampler(u32 index);