Browse Source

vulkan: Better handling of buffer resizes, without stalling

rdb 3 years ago
parent
commit
ba76b96bd2

+ 30 - 12
panda/src/vulkandisplay/vulkanGraphicsBuffer.cxx

@@ -92,7 +92,6 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     // The clear flags have changed.  Recreate the render pass.  Note that the
     // The clear flags have changed.  Recreate the render pass.  Note that the
     // clear flags don't factor into render pass compatibility, so we don't
     // clear flags don't factor into render pass compatibility, so we don't
     // need to recreate the framebuffer.
     // need to recreate the framebuffer.
-    vkQueueWaitIdle(vkgsg->_queue);
     setup_render_pass();
     setup_render_pass();
   }
   }
 
 
@@ -106,7 +105,6 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     // Uh-oh, the window must have resized.  Recreate the framebuffer.
     // Uh-oh, the window must have resized.  Recreate the framebuffer.
     // Before destroying the old, make sure the queue is no longer rendering
     // Before destroying the old, make sure the queue is no longer rendering
     // anything to it.
     // anything to it.
-    vkQueueWaitIdle(vkgsg->_queue);
     destroy_framebuffer();
     destroy_framebuffer();
     if (!create_framebuffer()) {
     if (!create_framebuffer()) {
       return false;
       return false;
@@ -233,15 +231,16 @@ close_buffer() {
     VulkanGraphicsStateGuardian *vkgsg;
     VulkanGraphicsStateGuardian *vkgsg;
     DCAST_INTO_V(vkgsg, _gsg);
     DCAST_INTO_V(vkgsg, _gsg);
 
 
-    // Wait until the queue is done with any commands that might use the swap
-    // chain, then destroy it.
-    vkQueueWaitIdle(vkgsg->_queue);
     destroy_framebuffer();
     destroy_framebuffer();
 
 
     if (_render_pass != VK_NULL_HANDLE) {
     if (_render_pass != VK_NULL_HANDLE) {
-      vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
-      _render_pass = VK_NULL_HANDLE;
+      if (vkgsg->_last_frame_data != nullptr) {
+        vkgsg->_last_frame_data->_pending_destroy_render_passes.push_back(_render_pass);
+      } else {
+        vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
+      }
     }
     }
+    _render_pass = VK_NULL_HANDLE;
 
 
     _gsg.clear();
     _gsg.clear();
   }
   }
@@ -397,6 +396,8 @@ setup_render_pass() {
       << "Creating render pass for VulkanGraphicsBuffer " << this << "\n";
       << "Creating render pass for VulkanGraphicsBuffer " << this << "\n";
   }
   }
 
 
+  nassertr(vkgsg->_frame_data == nullptr, false);
+
   // Check if we are planning on doing anything with the depth/color output.
   // Check if we are planning on doing anything with the depth/color output.
   BitMask32 transfer_planes = 0;
   BitMask32 transfer_planes = 0;
   {
   {
@@ -513,8 +514,11 @@ setup_render_pass() {
 
 
   // Destroy the previous render pass object.
   // Destroy the previous render pass object.
   if (_render_pass != VK_NULL_HANDLE) {
   if (_render_pass != VK_NULL_HANDLE) {
-    vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
-    _render_pass = VK_NULL_HANDLE;
+    if (vkgsg->_last_frame_data != nullptr) {
+      vkgsg->_last_frame_data->_pending_destroy_render_passes.push_back(_render_pass);
+    } else {
+      vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
+    }
   }
   }
 
 
   _render_pass = pass;
   _render_pass = pass;
@@ -532,6 +536,8 @@ destroy_framebuffer() {
   DCAST_INTO_V(vkgsg, _gsg);
   DCAST_INTO_V(vkgsg, _gsg);
   VkDevice device = vkgsg->_device;
   VkDevice device = vkgsg->_device;
 
 
+  nassertv(vkgsg->_frame_data == nullptr);
+
   // Make sure that the GSG's command buffer releases its resources.
   // Make sure that the GSG's command buffer releases its resources.
   //if (vkgsg->_cmd != VK_NULL_HANDLE) {
   //if (vkgsg->_cmd != VK_NULL_HANDLE) {
   //  vkResetCommandBuffer(vkgsg->_cmd, 0);
   //  vkResetCommandBuffer(vkgsg->_cmd, 0);
@@ -545,12 +551,20 @@ destroy_framebuffer() {
   // Destroy the resources held for each attachment.
   // Destroy the resources held for each attachment.
   for (Attachment &attach : _attachments) {
   for (Attachment &attach : _attachments) {
     if (attach._tc->_image_view != VK_NULL_HANDLE) {
     if (attach._tc->_image_view != VK_NULL_HANDLE) {
-      vkDestroyImageView(device, attach._tc->_image_view, nullptr);
+      if (vkgsg->_last_frame_data != nullptr) {
+        vkgsg->_last_frame_data->_pending_destroy_image_views.push_back(attach._tc->_image_view);
+      } else {
+        vkDestroyImageView(vkgsg->_device, attach._tc->_image_view, nullptr);
+      }
       attach._tc->_image_view = VK_NULL_HANDLE;
       attach._tc->_image_view = VK_NULL_HANDLE;
     }
     }
 
 
     if (attach._tc->_image != VK_NULL_HANDLE) {
     if (attach._tc->_image != VK_NULL_HANDLE) {
-      vkDestroyImage(device, attach._tc->_image, nullptr);
+      if (vkgsg->_last_frame_data != nullptr) {
+        vkgsg->_last_frame_data->_pending_destroy_images.push_back(attach._tc->_image);
+      } else {
+        vkDestroyImage(vkgsg->_device, attach._tc->_image, nullptr);
+      }
       attach._tc->_image = VK_NULL_HANDLE;
       attach._tc->_image = VK_NULL_HANDLE;
     }
     }
 
 
@@ -560,7 +574,11 @@ destroy_framebuffer() {
   _attachments.clear();
   _attachments.clear();
 
 
   if (_framebuffer != VK_NULL_HANDLE) {
   if (_framebuffer != VK_NULL_HANDLE) {
-    vkDestroyFramebuffer(device, _framebuffer, nullptr);
+    if (vkgsg->_last_frame_data != nullptr) {
+      vkgsg->_last_frame_data->_pending_destroy_framebuffers.push_back(_framebuffer);
+    } else {
+      vkDestroyFramebuffer(vkgsg->_device, _framebuffer, nullptr);
+    }
     _framebuffer = VK_NULL_HANDLE;
     _framebuffer = VK_NULL_HANDLE;
   }
   }
 
 

+ 21 - 1
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -2543,6 +2543,7 @@ end_frame(Thread *current_thread) {
     nassertv(!err);
     nassertv(!err);
   }
   }
 
 
+  _last_frame_data = _frame_data;
   _frame_data = nullptr;
   _frame_data = nullptr;
 
 
   //TODO: delete command buffer, schedule for deletion, or recycle.
   //TODO: delete command buffer, schedule for deletion, or recycle.
@@ -2568,6 +2569,11 @@ finish_frame(FrameData &frame_data) {
   }
   }
   frame_data._pending_destroy_buffers.clear();
   frame_data._pending_destroy_buffers.clear();
 
 
+  for (VkFramebuffer framebuffer : frame_data._pending_destroy_framebuffers) {
+    vkDestroyFramebuffer(_device, framebuffer, nullptr);
+  }
+  frame_data._pending_destroy_framebuffers.clear();
+
   for (VkImageView image_view : frame_data._pending_destroy_image_views) {
   for (VkImageView image_view : frame_data._pending_destroy_image_views) {
     vkDestroyImageView(_device, image_view, nullptr);
     vkDestroyImageView(_device, image_view, nullptr);
   }
   }
@@ -2578,6 +2584,11 @@ finish_frame(FrameData &frame_data) {
   }
   }
   frame_data._pending_destroy_images.clear();
   frame_data._pending_destroy_images.clear();
 
 
+  for (VkRenderPass render_pass : frame_data._pending_destroy_render_passes) {
+    vkDestroyRenderPass(_device, render_pass, nullptr);
+  }
+  frame_data._pending_destroy_render_passes.clear();
+
   for (VkSampler sampler : frame_data._pending_destroy_samplers) {
   for (VkSampler sampler : frame_data._pending_destroy_samplers) {
     vkDestroySampler(_device, sampler, nullptr);
     vkDestroySampler(_device, sampler, nullptr);
   }
   }
@@ -2833,8 +2844,17 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
     }
     }
   }
   }
 
 
+  PreparedGraphicsObjects *pgo = get_prepared_objects();
+
   VulkanTextureContext *tc;
   VulkanTextureContext *tc;
-  DCAST_INTO_R(tc, tex->prepare_now(view, get_prepared_objects(), this), false);
+  DCAST_INTO_R(tc, tex->prepare_now(view, pgo, this), false);
+
+  // Temporary, prepare_now should really deal with the resizing
+  if (tc->_extent.width != tex->get_x_size() ||
+      tc->_extent.height != tex->get_y_size()) {
+    pgo->release_texture(tc);
+    DCAST_INTO_R(tc, tex->prepare_now(view, pgo, this), false);
+  }
 
 
   nassertr(fbtc->_extent.width <= tc->_extent.width &&
   nassertr(fbtc->_extent.width <= tc->_extent.width &&
            fbtc->_extent.height <= tc->_extent.height &&
            fbtc->_extent.height <= tc->_extent.height &&

+ 1 - 0
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.h

@@ -301,6 +301,7 @@ private:
   size_t _frame_data_head = _frame_data_capacity;
   size_t _frame_data_head = _frame_data_capacity;
   size_t _frame_data_tail = 0;
   size_t _frame_data_tail = 0;
   FrameData *_frame_data = nullptr;
   FrameData *_frame_data = nullptr;
+  FrameData *_last_frame_data = nullptr;
 
 
   uint64_t _frame_counter = 0;
   uint64_t _frame_counter = 0;
   uint64_t _last_finished_frame = 0;
   uint64_t _last_finished_frame = 0;

+ 7 - 3
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -105,7 +105,6 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     // The clear flags have changed.  Recreate the render pass.  Note that the
     // The clear flags have changed.  Recreate the render pass.  Note that the
     // clear flags don't factor into render pass compatibility, so we don't
     // clear flags don't factor into render pass compatibility, so we don't
     // need to recreate the framebuffer.
     // need to recreate the framebuffer.
-    vkQueueWaitIdle(vkgsg->_queue);
     setup_render_pass();
     setup_render_pass();
   }
   }
 
 
@@ -639,6 +638,8 @@ setup_render_pass() {
       << "Creating render pass for VulkanGraphicsWindow " << this << "\n";
       << "Creating render pass for VulkanGraphicsWindow " << this << "\n";
   }
   }
 
 
+  nassertr(vkgsg->_frame_data == nullptr, false);
+
   {
   {
     // Do we intend to copy the framebuffer to a texture?
     // Do we intend to copy the framebuffer to a texture?
     _final_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
     _final_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
@@ -768,8 +769,11 @@ setup_render_pass() {
 
 
   // Destroy the previous render pass object.
   // Destroy the previous render pass object.
   if (_render_pass != VK_NULL_HANDLE) {
   if (_render_pass != VK_NULL_HANDLE) {
-    vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
-    _render_pass = VK_NULL_HANDLE;
+    if (vkgsg->_last_frame_data != nullptr) {
+      vkgsg->_last_frame_data->_pending_destroy_render_passes.push_back(_render_pass);
+    } else {
+      vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
+    }
   }
   }
 
 
   _render_pass = pass;
   _render_pass = pass;