Explorar el Código

vulkan: various changes to presentation to enable get_screenshot()

Now it also uses proper semaphores to synchronize presentation and rendering, and no longer waits until the queue is idle afterwards.
rdb hace 7 años
padre
commit
a9dc1c7587

+ 20 - 0
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -46,6 +46,8 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
   _cmd(VK_NULL_HANDLE),
   _transfer_cmd(VK_NULL_HANDLE),
   _render_pass(VK_NULL_HANDLE),
+  _wait_semaphore(VK_NULL_HANDLE),
+  _signal_semaphore(VK_NULL_HANDLE),
   _pipeline_cache(VK_NULL_HANDLE),
   _pipeline_layout(VK_NULL_HANDLE),
   _default_sc(nullptr)
@@ -1809,6 +1811,20 @@ end_frame(Thread *current_thread) {
   submit_info.signalSemaphoreCount = 0;
   submit_info.pSignalSemaphores = nullptr;
 
+  if (_wait_semaphore != VK_NULL_HANDLE) {
+    // We may need to wait until the attachments are available for writing.
+    static const VkPipelineStageFlags flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    submit_info.waitSemaphoreCount = 1;
+    submit_info.pWaitSemaphores = &_wait_semaphore;
+    submit_info.pWaitDstStageMask = &flags;
+  }
+
+  if (_signal_semaphore != VK_NULL_HANDLE) {
+    // And we were asked to signal a semaphore when we are done rendering.
+    submit_info.signalSemaphoreCount = 1;
+    submit_info.pSignalSemaphores = &_signal_semaphore;
+  }
+
   VkResult err;
   err = vkQueueSubmit(_queue, 1, &submit_info, _fence);
   if (err) {
@@ -1816,6 +1832,10 @@ end_frame(Thread *current_thread) {
     return;
   }
 
+  // We're done with these for now.
+  _wait_semaphore = VK_NULL_HANDLE;
+  _signal_semaphore = VK_NULL_HANDLE;
+
   // If we queued up texture downloads, wait for the queue to finish (slow!)
   // and then copy the data from Vulkan host memory to Panda memory.
   if (!_download_queue.empty()) {

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

@@ -200,6 +200,8 @@ private:
   VkRenderPass _render_pass;
   VulkanTextureContext *_fb_color_tc;
   VulkanTextureContext *_fb_depth_tc;
+  VkSemaphore _wait_semaphore;
+  VkSemaphore _signal_semaphore;
 
   // Palette for flat colors.
   VkBuffer _color_vertex_buffer;

+ 85 - 67
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -31,7 +31,8 @@ VulkanGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _surface(VK_NULL_HANDLE),
   _swapchain(VK_NULL_HANDLE),
   _render_pass(VK_NULL_HANDLE),
-  _present_complete(VK_NULL_HANDLE),
+  _image_available(VK_NULL_HANDLE),
+  _render_complete(VK_NULL_HANDLE),
   _current_clear_mask(-1),
   _depth_stencil_tc(nullptr),
   _image_index(0)
@@ -129,22 +130,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     return true;
   }
 
-  VkSemaphoreCreateInfo semaphore_info;
-  semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-  semaphore_info.pNext = nullptr;
-  semaphore_info.flags = 0;
-
-  VkResult err;
-  err = vkCreateSemaphore(vkgsg->_device, &semaphore_info,
-                          nullptr, &_present_complete);
-  nassertr(err == 0, false);
-
-  if (mode == FM_render) {
-    err = vkAcquireNextImageKHR(vkgsg->_device, _swapchain, UINT64_MAX,
-                                _present_complete, (VkFence)0, &_image_index);
-
-    nassertr(_image_index < _swap_buffers.size(), false);
-  }
+  nassertr(_image_index < _swap_buffers.size(), false);
   SwapBuffer &buffer = _swap_buffers[_image_index];
 
   /*if (mode == FM_render) {
@@ -227,6 +213,8 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   vkgsg->_render_pass = _render_pass;
   vkgsg->_fb_color_tc = buffer._tc;
   vkgsg->_fb_depth_tc = _depth_stencil_tc;
+  vkgsg->_wait_semaphore = _image_available;
+  vkgsg->_signal_semaphore = _render_complete;
 
   return true;
 }
@@ -243,34 +231,34 @@ end_frame(FrameMode mode, Thread *current_thread) {
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_V(vkgsg, _gsg);
 
-  if (mode == FM_render) {
-    VkCommandBuffer cmd = vkgsg->_cmd;
-    nassertv(cmd != VK_NULL_HANDLE);
+  VkCommandBuffer cmd = vkgsg->_cmd;
+  nassertv(cmd != VK_NULL_HANDLE);
+  SwapBuffer &buffer = _swap_buffers[_image_index];
 
+  if (mode == FM_render) {
     vkCmdEndRenderPass(cmd);
     vkgsg->_render_pass = VK_NULL_HANDLE;
 
     // The driver implicitly transitioned this to the final layout.
-    SwapBuffer &buffer = _swap_buffers[_image_index];
     buffer._tc->_layout = _final_layout;
     buffer._tc->_access_mask = VK_ACCESS_MEMORY_READ_BIT;
 
     // Now we can do copy-to-texture, now that the render pass has ended.
     copy_to_textures();
-
-    // If we copied the textures, transition it back to the present state.
-    buffer._tc->transition(cmd, vkgsg->_graphics_queue_family_index,
-                           VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-                           VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
-                           VK_ACCESS_MEMORY_READ_BIT);
   }
 
-  // Note: this will close the command buffer.
+  // If we copied the textures, transition it back to the present state.
+  buffer._tc->transition(cmd, vkgsg->_graphics_queue_family_index,
+                         VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+                         VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                         VK_ACCESS_MEMORY_READ_BIT);
+
+  // Note: this will close the command buffer, and unsignal the previous
+  // frame's semaphore.
   vkgsg->end_frame(current_thread);
 
   if (mode == FM_render) {
-    nassertv(_present_complete != VK_NULL_HANDLE);
-    trigger_flip();
+    _flip_ready = true;
     clear_cube_map_selection();
   }
 }
@@ -288,6 +276,36 @@ end_frame(FrameMode mode, Thread *current_thread) {
  */
 void VulkanGraphicsWindow::
 begin_flip() {
+  VulkanGraphicsStateGuardian *vkgsg;
+  DCAST_INTO_V(vkgsg, _gsg);
+  VkDevice device = vkgsg->_device;
+  VkQueue queue = vkgsg->_queue;
+  VkResult err;
+
+  SwapBuffer &buffer = _swap_buffers[_image_index];
+
+  VkResult results[1];
+  VkPresentInfoKHR present;
+  present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+  present.pNext = nullptr;
+  present.waitSemaphoreCount = 1;
+  present.pWaitSemaphores = &_render_complete;
+  present.swapchainCount = 1;
+  present.pSwapchains = &_swapchain;
+  present.pImageIndices = &_image_index;
+  present.pResults = results;
+
+  err = vkQueuePresentKHR(queue, &present);
+  if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+    cerr << "out of date.\n";
+
+  } else if (err == VK_SUBOPTIMAL_KHR) {
+    cerr << "suboptimal.\n";
+
+  } else if (err != VK_SUCCESS) {
+    vulkan_error(err, "Error presenting queue");
+    return;
+  }
 }
 
 /**
@@ -316,47 +334,21 @@ void VulkanGraphicsWindow::
 end_flip() {
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_V(vkgsg, _gsg);
-  VkDevice device = vkgsg->_device;
-  VkQueue queue = vkgsg->_queue;
-  VkResult err;
-
-  SwapBuffer &buffer = _swap_buffers[_image_index];
-
-  // Make sure it is in the state we expect it to be.
-  nassertv(buffer._tc->_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
-
-  VkResult results[1];
-  VkPresentInfoKHR present;
-  present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
-  present.pNext = nullptr;
-  present.waitSemaphoreCount = 0;
-  present.pWaitSemaphores = nullptr;
-  //present.waitSemaphoreCount = 1;
-  //present.pWaitSemaphores = &_present_complete;
-  present.swapchainCount = 1;
-  present.pSwapchains = &_swapchain;
-  present.pImageIndices = &_image_index;
-  present.pResults = results;
-
-  err = vkQueuePresentKHR(queue, &present);
-  if (err == VK_ERROR_OUT_OF_DATE_KHR) {
-    cerr << "out of date.\n";
 
-  } else if (err == VK_SUBOPTIMAL_KHR) {
-    cerr << "suboptimal.\n";
+  // Get a new image for rendering into.  This may wait until a new image is
+  // available.
+  VkResult
+  err = vkAcquireNextImageKHR(vkgsg->_device, _swapchain, UINT64_MAX,
+                              _image_available, VK_NULL_HANDLE, &_image_index);
+  nassertv(err == VK_SUCCESS);
 
-  } else if (err != VK_SUCCESS) {
-    vulkan_error(err, "Error presenting queue");
-    return;
+  if (vulkandisplay_cat.is_spam()) {
+    vulkandisplay_cat.spam()
+      << "Acquired image " << _image_index << " from swapchain\n";
   }
 
-  // Should we really wait for the present to be done?  Seems like a waste of
-  // precious frame time.
-  err = vkQueueWaitIdle(queue);
-  assert(err == VK_SUCCESS);
-
-  vkDestroySemaphore(vkgsg->_device, _present_complete, nullptr);
-  _present_complete = VK_NULL_HANDLE;
+  // Don't flip again until we've rendered another frame.
+  _flip_ready = false;
 }
 
 /**
@@ -1000,5 +992,31 @@ create_swapchain() {
     }
   }
 
+  // Create a semaphore for signalling the availability of an image.
+  // It will be signalled in end_flip() and waited upon before submitting the
+  // command buffers that use that image for rendering to.
+  VkSemaphoreCreateInfo semaphore_info = {};
+  semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+  err = vkCreateSemaphore(vkgsg->_device, &semaphore_info,
+                          nullptr, &_image_available);
+  nassertr(err == VK_SUCCESS, false);
+
+  // Now create another one that is signalled when we are finished rendering,
+  // to indicate that it is safe to present the image.
+  err = vkCreateSemaphore(vkgsg->_device, &semaphore_info,
+                          nullptr, &_render_complete);
+  nassertr(err == VK_SUCCESS, false);
+
+  // We need to acquire an image before we continue rendering.
+  _image_index = 0;
+  _flip_ready = false;
+  err = vkAcquireNextImageKHR(vkgsg->_device, _swapchain, UINT64_MAX,
+                              _image_available, VK_NULL_HANDLE, &_image_index);
+  if (err) {
+    vulkan_error(err, "Failed to acquire swapchain image");
+    return false;
+  }
+
   return true;
 }

+ 4 - 1
panda/src/vulkandisplay/vulkanGraphicsWindow.h

@@ -64,9 +64,12 @@ private:
   VkSurfaceKHR _surface;
   VkSwapchainKHR _swapchain;
   VkRenderPass _render_pass;
-  VkSemaphore _present_complete;
   int _current_clear_mask;
 
+  // We'll need these to synchronize the rendering with the presentation.
+  VkSemaphore _render_complete;
+  VkSemaphore _image_available;
+
   LVecBase2i _swapchain_size;
   VkSurfaceFormatKHR _surface_format;