Browse Source

vulkan: properly handle teardown/deletion of GSG and prepared resources

rdb 7 years ago
parent
commit
ef6c6d3f71

+ 80 - 4
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -305,6 +305,37 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
  */
 VulkanGraphicsStateGuardian::
 ~VulkanGraphicsStateGuardian() {
+  // Remove the things we created in the constructor, in reverse order.
+  vkDestroyBuffer(_device, _color_vertex_buffer, nullptr);
+  vkDestroyDescriptorPool(_device, _descriptor_pool, nullptr);
+  vkDestroyPipelineLayout(_device, _pipeline_layout, nullptr);
+  vkDestroyDescriptorSetLayout(_device, _descriptor_set_layout, nullptr);
+  vkDestroyPipelineCache(_device, _pipeline_cache, nullptr);
+  vkDestroyCommandPool(_device, _cmd_pool, nullptr);
+  vkDestroyFence(_device, _fence, nullptr);
+  vkDestroyDevice(_device, nullptr);
+}
+
+/**
+ * This is called by the associated GraphicsWindow when close_window() is
+ * called.  It should null out the _win pointer and possibly free any open
+ * resources associated with the GSG.
+ */
+void VulkanGraphicsStateGuardian::
+close_gsg() {
+  // Make sure it's no longer doing anything before we destroy it.
+  vkDeviceWaitIdle(_device);
+
+  // We need to release all prepared resources, since the upcall to close_gsg
+  // will cause the PreparedGraphicsObjects to be cleared out.
+  {
+    PT(PreparedGraphicsObjects) pgo = std::move(_prepared_objects);
+    if (pgo != nullptr) {
+      pgo->release_all_now(this);
+    }
+  }
+
+  GraphicsStateGuardian::close_gsg();
 }
 
 /**
@@ -357,8 +388,6 @@ get_driver_version() {
  */
 TextureContext *VulkanGraphicsStateGuardian::
 prepare_texture(Texture *texture, int view) {
-  nassertr(_transfer_cmd != VK_NULL_HANDLE, nullptr);
-
   PStatTimer timer(_prepare_texture_pcollector);
 
   VulkanGraphicsPipe *vkpipe;
@@ -954,7 +983,31 @@ update_texture(TextureContext *tc, bool force) {
  * including deleting the TextureContext itself, if it is non-NULL.
  */
 void VulkanGraphicsStateGuardian::
-release_texture(TextureContext *) {
+release_texture(TextureContext *tc) {
+  // This is called during begin_frame, after the fence has been waited upon,
+  // so we know that any command buffers using this have finished executing.
+  VulkanTextureContext *vtc;
+  DCAST_INTO_V(vtc, tc);
+
+  if (vulkandisplay_cat.is_debug()) {
+    vulkandisplay_cat.debug()
+      << "Deleting image " << vtc->_image << " and view " << vtc->_image_view << "\n";
+  }
+
+  if (vtc->_image_view != VK_NULL_HANDLE) {
+    vkDestroyImageView(_device, vtc->_image_view, nullptr);
+    vtc->_image_view = VK_NULL_HANDLE;
+  }
+  if (vtc->_image != VK_NULL_HANDLE) {
+    vkDestroyImage(_device, vtc->_image, nullptr);
+    vtc->_image = VK_NULL_HANDLE;
+  }
+  if (vtc->_memory != VK_NULL_HANDLE) {
+    vkFreeMemory(_device, vtc->_memory, nullptr);
+    vtc->_memory = VK_NULL_HANDLE;
+  }
+
+  delete vtc;
 }
 
 /**
@@ -1050,7 +1103,18 @@ prepare_sampler(const SamplerState &sampler) {
  * including deleting the SamplerContext itself, if it is non-NULL.
  */
 void VulkanGraphicsStateGuardian::
-release_sampler(SamplerContext *) {
+release_sampler(SamplerContext *sc) {
+  // This is called during begin_frame, after the fence has been waited upon,
+  // so we know that any command buffers using this have finished executing.
+  VulkanSamplerContext *vsc;
+  DCAST_INTO_V(vsc, sc);
+
+  if (vsc->_sampler != VK_NULL_HANDLE) {
+    vkDestroySampler(_device, vsc->_sampler, nullptr);
+    vsc->_sampler = VK_NULL_HANDLE;
+  }
+
+  delete vsc;
 }
 
 /**
@@ -1121,6 +1185,8 @@ release_shader(ShaderContext *context) {
   VulkanShaderContext *sc;
   DCAST_INTO_V(sc, context);
 
+  // According to the Vulkan spec, it is safe to delete a shader module even
+  // if pipelines using it are still in use, so let's do it now.
   if (sc->_modules[0] != VK_NULL_HANDLE) {
     vkDestroyShaderModule(_device, sc->_modules[0], nullptr);
     sc->_modules[0] = VK_NULL_HANDLE;
@@ -1211,6 +1277,8 @@ update_vertex_buffer(VulkanVertexBufferContext *vbc,
  */
 void VulkanGraphicsStateGuardian::
 release_vertex_buffer(VertexBufferContext *context) {
+  // This is called during begin_frame, after the fence has been waited upon,
+  // so we know that any command buffers using this have finished executing.
   VulkanVertexBufferContext *vbc;
   DCAST_INTO_V(vbc, context);
 
@@ -1346,6 +1414,8 @@ update_index_buffer(VulkanIndexBufferContext *ibc,
  */
 void VulkanGraphicsStateGuardian::
 release_index_buffer(IndexBufferContext *context) {
+  // This is called during begin_frame, after the fence has been waited upon,
+  // so we know that any command buffers using this have finished executing.
   VulkanIndexBufferContext *ibc;
   DCAST_INTO_V(ibc, context);
 
@@ -1556,6 +1626,8 @@ prepare_lens() {
  */
 bool VulkanGraphicsStateGuardian::
 begin_frame(Thread *current_thread) {
+  nassertr_always(!_closing_gsg, false);
+
   VkResult err;
 
   {
@@ -1564,6 +1636,8 @@ begin_frame(Thread *current_thread) {
     // Make sure that the previous command buffer is done executing, so that
     // we don't update or delete resources while they're still being used.
     // We should probably come up with a better mechanism for this.
+    //NB: if we remove this, we also need to change the code in release_xxx in
+    // order not to delete resources that are still being used.
     err = vkWaitForFences(_device, 1, &_fence, VK_TRUE, 1000000000ULL);
     if (err == VK_TIMEOUT) {
       vulkandisplay_cat.error()
@@ -1942,6 +2016,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
 
   // You're not allowed to call this while a render pass is active.
   nassertr(_render_pass == VK_NULL_HANDLE, false);
+  nassertr(!_closing_gsg, false);
 
   nassertr(_fb_color_tc != nullptr, false);
   VulkanTextureContext *fbtc = _fb_color_tc;
@@ -2018,6 +2093,7 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
 
   // You're not allowed to call this while a render pass is active.
   nassertr(_render_pass == VK_NULL_HANDLE, false);
+  nassertr(!_closing_gsg, false);
 
   nassertr(_fb_color_tc != nullptr, false);
   VulkanTextureContext *fbtc = _fb_color_tc;

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

@@ -32,6 +32,8 @@ public:
                               uint32_t queue_family_index);
   virtual ~VulkanGraphicsStateGuardian();
 
+  virtual void close_gsg();
+
   virtual std::string get_driver_vendor();
   virtual std::string get_driver_renderer();
   virtual std::string get_driver_version();