From ba698430cb76e4d0247fb073ffaccf7a46955815 Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Wed, 10 Feb 2021 01:59:48 +0100
Subject: [PATCH] [dxvk] Suspend render pass when updating framebuffer

Saves barriers in case some of the previously bound framebuffer
arre reused in the new one.
---
 src/dxvk/dxvk_context.cpp     | 81 ++++++++++++++++++++++++-----------
 src/dxvk/dxvk_context.h       | 12 +++++-
 src/dxvk/dxvk_context_state.h |  1 +
 3 files changed, 67 insertions(+), 27 deletions(-)

diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp
index 77cd3bc1..5ba2d96c 100644
--- a/src/dxvk/dxvk_context.cpp
+++ b/src/dxvk/dxvk_context.cpp
@@ -3388,6 +3388,7 @@ namespace dxvk {
       this->flushClears(true);
 
       m_flags.set(DxvkContextFlag::GpRenderPassBound);
+      m_flags.clr(DxvkContextFlag::GpRenderPassSuspended);
 
       m_execBarriers.recordCommands(m_cmd);
 
@@ -3413,7 +3414,7 @@ namespace dxvk {
   }
   
   
-  void DxvkContext::spillRenderPass(bool flushClears) {
+  void DxvkContext::spillRenderPass(bool suspend) {
     if (m_flags.test(DxvkContextFlag::GpRenderPassBound)) {
       m_flags.clr(DxvkContextFlag::GpRenderPassBound);
 
@@ -3423,14 +3424,25 @@ namespace dxvk {
       m_queryManager.endQueries(m_cmd, VK_QUERY_TYPE_PIPELINE_STATISTICS);
       
       this->renderPassUnbindFramebuffer();
-      this->transitionRenderTargetLayouts(m_gfxBarriers);
+
+      if (suspend)
+        m_flags.set(DxvkContextFlag::GpRenderPassSuspended);
+      else
+        this->transitionRenderTargetLayouts(m_gfxBarriers);
 
       m_gfxBarriers.recordCommands(m_cmd);
 
       this->unbindGraphicsPipeline();
 
       m_flags.clr(DxvkContextFlag::GpDirtyXfbCounters);
-    } else if (flushClears) {
+    } else if (!suspend) {
+      // We may end a previously suspended render pass
+      if (m_flags.test(DxvkContextFlag::GpRenderPassSuspended)) {
+        m_flags.clr(DxvkContextFlag::GpRenderPassSuspended);
+        this->transitionRenderTargetLayouts(m_gfxBarriers);
+        m_gfxBarriers.recordCommands(m_cmd);
+      }
+
       // Execute deferred clears if necessary
       this->flushClears(false);
     }
@@ -3958,7 +3970,7 @@ namespace dxvk {
     if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) {
       m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer);
 
-      this->spillRenderPass(false);
+      this->spillRenderPass(true);
 
       auto fb = m_device->createFramebuffer(m_state.om.renderTargets);
       this->updateRenderTargetLayouts(fb, m_state.om.framebuffer);
@@ -4009,16 +4021,7 @@ namespace dxvk {
       const DxvkAttachment& color = m_state.om.framebuffer->getColorTarget(i);
 
       if (color.view != nullptr) {
-        barriers.accessImage(
-          color.view->image(),
-          color.view->subresources(),
-          m_rtLayouts.color[i],
-          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
-          VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
-          color.view->imageInfo().layout,
-          color.view->imageInfo().stages,
-          color.view->imageInfo().access);
-
+        this->transitionColorAttachment(barriers, color, m_rtLayouts.color[i]);
         m_rtLayouts.color[i] = color.view->imageInfo().layout;
       }
     }
@@ -4026,23 +4029,44 @@ namespace dxvk {
     const DxvkAttachment& depth = m_state.om.framebuffer->getDepthTarget();
 
     if (depth.view != nullptr) {
-      barriers.accessImage(
-        depth.view->image(),
-        depth.view->subresources(),
-        m_rtLayouts.depth,
-        VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
-        VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
-        m_rtLayouts.depth != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
-          ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT : 0,
-        depth.view->imageInfo().layout,
-        depth.view->imageInfo().stages,
-        depth.view->imageInfo().access);
-
+      this->transitionDepthAttachment(barriers, depth, m_rtLayouts.depth);
       m_rtLayouts.depth = depth.view->imageInfo().layout;
     }
   }
 
 
+  void DxvkContext::transitionColorAttachment(
+          DxvkBarrierSet&         barriers,
+    const DxvkAttachment&         attachment,
+          VkImageLayout           oldLayout) {
+    barriers.accessImage(
+      attachment.view->image(),
+      attachment.view->subresources(), oldLayout,
+      VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+      VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+      attachment.view->imageInfo().layout,
+      attachment.view->imageInfo().stages,
+      attachment.view->imageInfo().access);
+  }
+
+
+  void DxvkContext::transitionDepthAttachment(
+          DxvkBarrierSet&         barriers,
+    const DxvkAttachment&         attachment,
+          VkImageLayout           oldLayout) {
+    barriers.accessImage(
+      attachment.view->image(),
+      attachment.view->subresources(), oldLayout,
+      VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
+      VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
+      oldLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
+        ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT : 0,
+      attachment.view->imageInfo().layout,
+      attachment.view->imageInfo().stages,
+      attachment.view->imageInfo().access);
+  }
+
+
   void DxvkContext::updateRenderTargetLayouts(
     const Rc<DxvkFramebuffer>&    newFb,
     const Rc<DxvkFramebuffer>&    oldFb) {
@@ -4076,6 +4100,9 @@ namespace dxvk {
             if (found)
               layouts.color[j] = m_rtLayouts.color[i];
           }
+
+          if (!found && m_flags.test(DxvkContextFlag::GpRenderPassSuspended))
+            this->transitionColorAttachment(m_execBarriers, oldAttachment, m_rtLayouts.color[i]);
         }
       }
 
@@ -4090,6 +4117,8 @@ namespace dxvk {
 
         if (found)
           layouts.depth = m_rtLayouts.depth;
+        else if (m_flags.test(DxvkContextFlag::GpRenderPassSuspended))
+          this->transitionDepthAttachment(m_execBarriers, oldAttachment, m_rtLayouts.depth);
       }
     }
 
diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h
index 7cf7d214..e5789c8e 100644
--- a/src/dxvk/dxvk_context.h
+++ b/src/dxvk/dxvk_context.h
@@ -1102,7 +1102,7 @@ namespace dxvk {
             bool                      useRenderPass);
 
     void startRenderPass();
-    void spillRenderPass(bool flushClears = true);
+    void spillRenderPass(bool suspend = false);
     
     void renderPassBindFramebuffer(
       const Rc<DxvkFramebuffer>&  framebuffer,
@@ -1148,6 +1148,16 @@ namespace dxvk {
     void transitionRenderTargetLayouts(
             DxvkBarrierSet&         barriers);
 
+    void transitionColorAttachment(
+            DxvkBarrierSet&         barriers,
+      const DxvkAttachment&         attachment,
+            VkImageLayout           oldLayout);
+
+    void transitionDepthAttachment(
+            DxvkBarrierSet&         barriers,
+      const DxvkAttachment&         attachment,
+            VkImageLayout           oldLayout);
+
     void updateRenderTargetLayouts(
       const Rc<DxvkFramebuffer>&    newFb,
       const Rc<DxvkFramebuffer>&    oldFb);
diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h
index 48ddf0d2..5ddaa780 100644
--- a/src/dxvk/dxvk_context_state.h
+++ b/src/dxvk/dxvk_context_state.h
@@ -22,6 +22,7 @@ namespace dxvk {
    */
   enum class DxvkContextFlag : uint32_t  {
     GpRenderPassBound,          ///< Render pass is currently bound
+    GpRenderPassSuspended,      ///< Render pass is currently suspended
     GpXfbActive,                ///< Transform feedback is enabled
     GpDirtyFramebuffer,         ///< Framebuffer binding is out of date
     GpDirtyPipeline,            ///< Graphics pipeline binding is out of date