فهرست منبع

Vulkan: improve window code, respect requests better, recreate VkRenderPass when clear settings change

rdb 9 سال پیش
والد
کامیت
786d23230b

+ 18 - 3
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1348,7 +1348,8 @@ clear(DrawableRegion *clearable) {
   VkClearAttachment attachments[2];
   int ai = 0;
 
-  if (clearable->get_clear_color_active()) {
+  if (clearable->get_clear_color_active() &&
+      _current_properties->get_color_bits() > 0) {
     LColor color = clearable->get_clear_color();
     VkClearAttachment &attachment = attachments[ai++];
     attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -1359,8 +1360,10 @@ clear(DrawableRegion *clearable) {
     attachment.clearValue.color.float32[3] = color[3];
   }
 
-  if (clearable->get_clear_depth_active() ||
-      clearable->get_clear_stencil_active()) {
+  if ((clearable->get_clear_depth_active() ||
+       clearable->get_clear_stencil_active()) &&
+      (_current_properties->get_depth_bits() > 0 ||
+       _current_properties->get_stencil_bits() > 0)) {
     VkClearAttachment &attachment = attachments[ai++];
     attachment.aspectMask = 0;
     if (clearable->get_clear_depth_active()) {
@@ -1707,6 +1710,18 @@ void VulkanGraphicsStateGuardian::
 reset() {
 }
 
+/**
+ * Copy the pixels within the indicated display region from the framebuffer
+ * into texture memory.
+ *
+ * If z > -1, it is the cube map index into which to copy.
+ */
+bool VulkanGraphicsStateGuardian::
+framebuffer_copy_to_texture(Texture *tex, int view, int z, const DisplayRegion *,
+                            const RenderBuffer &) {
+  return false;
+}
+
 /**
  * Invoked by all the draw_xyz methods.
  */

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

@@ -98,6 +98,10 @@ public:
 
   virtual void reset();
 
+  virtual bool framebuffer_copy_to_texture(Texture *tex, int view, int z,
+                                           const DisplayRegion *dr,
+                                           const RenderBuffer &rb);
+
 private:
   bool do_draw_primitive(const GeomPrimitivePipelineReader *reader, bool force,
                          VkPrimitiveTopology topology);

+ 295 - 144
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -32,10 +32,12 @@ VulkanGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _swapchain(VK_NULL_HANDLE),
   _render_pass(VK_NULL_HANDLE),
   _present_complete(VK_NULL_HANDLE),
+  _current_clear_mask(-1),
   _depth_stencil_image(VK_NULL_HANDLE),
   _depth_stencil_view(VK_NULL_HANDLE),
   _depth_stencil_memory(VK_NULL_HANDLE),
   _depth_stencil_layout_defined(false),
+  _depth_stencil_aspect_mask(0),
   _image_index(0)
 {
 }
@@ -47,6 +49,20 @@ VulkanGraphicsWindow::
 ~VulkanGraphicsWindow() {
 }
 
+/**
+ * Clears the entire framebuffer before rendering, according to the settings
+ * of get_color_clear_active() and get_depth_clear_active() (inherited from
+ * DrawableRegion).
+ *
+ * This function is called only within the draw thread.
+ */
+void VulkanGraphicsWindow::
+clear(Thread *current_thread) {
+  // We do the clear in begin_frame(), and the validation layers don't like it
+  // if an extra clear is being done at the beginning of a frame.  That's why
+  // this is empty for now.  Need a cleaner solution for this.
+}
+
 /**
  * This function will be called within the draw thread before beginning
  * rendering for a given frame.  It should do whatever setup is required, and
@@ -81,6 +97,14 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   DCAST_INTO_R(vkgsg, _gsg, false);
   //vkgsg->reset_if_new();
 
+  if (_current_clear_mask != _clear_mask) {
+    // 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
+    // need to recreate the framebuffer.
+    vkQueueWaitIdle(vkgsg->_queue);
+    setup_render_pass();
+  }
+
   if (_swapchain_size != _size) {
     // Uh-oh, the window must have resized.  Recreate the swapchain.
     // Before destroying the old, make sure the queue is no longer rendering
@@ -123,6 +147,25 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   // transition the swapchain images into the valid state for rendering into.
   VkCommandBuffer cmd = vkgsg->_cmd;
 
+  VkClearValue clears[2];
+  LColor clear_color = get_clear_color();
+  clears[0].color.float32[0] = clear_color[0];
+  clears[0].color.float32[1] = clear_color[1];
+  clears[0].color.float32[2] = clear_color[2];
+  clears[0].color.float32[3] = clear_color[3];
+
+  VkRenderPassBeginInfo begin_info;
+  begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+  begin_info.pNext = NULL;
+  begin_info.renderPass = _render_pass;
+  begin_info.framebuffer = buffer._framebuffer;
+  begin_info.renderArea.offset.x = 0;
+  begin_info.renderArea.offset.y = 0;
+  begin_info.renderArea.extent.width = _size[0];
+  begin_info.renderArea.extent.height = _size[1];
+  begin_info.clearValueCount = 1;
+  begin_info.pClearValues = clears;
+
   VkImageMemoryBarrier barriers[2];
   barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
   barriers[0].pNext = NULL;
@@ -139,22 +182,6 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   barriers[0].subresourceRange.baseArrayLayer = 0;
   barriers[0].subresourceRange.layerCount = 1;
 
-  barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-  barriers[1].pNext = NULL;
-  barriers[1].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
-  barriers[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
-  barriers[1].oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-  barriers[1].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
-  barriers[1].srcQueueFamilyIndex = vkgsg->_graphics_queue_family_index; //TODO support separate present queue.
-  barriers[1].dstQueueFamilyIndex = vkgsg->_graphics_queue_family_index;
-  barriers[1].image = _depth_stencil_image;
-  barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT |
-                                            VK_IMAGE_ASPECT_STENCIL_BIT;
-  barriers[1].subresourceRange.baseMipLevel = 0;
-  barriers[1].subresourceRange.levelCount = 1;
-  barriers[1].subresourceRange.baseArrayLayer = 0;
-  barriers[1].subresourceRange.layerCount = 1;
-
   if (!buffer._layout_defined) {
     // If this is the first time we are using these images, they are still in
     // the UNDEFINED layout.
@@ -163,36 +190,40 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     buffer._layout_defined = true;
   }
 
-  if (!_depth_stencil_layout_defined) {
-    barriers[1].srcAccessMask = 0;
-    barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-    _depth_stencil_layout_defined = true;
-  }
-
-  vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
-                       VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
-                       0, NULL, 0, NULL, 2, barriers);
+  if (_depth_stencil_image == VK_NULL_HANDLE) {
+    vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
+                         0, NULL, 0, NULL, 1, barriers);
+  } else {
+    begin_info.clearValueCount++;
+    clears[1].depthStencil.depth = get_clear_depth();
+    clears[1].depthStencil.stencil = get_clear_stencil();
 
-  VkClearValue clears[2];
-  LColor clear_color = get_clear_color();
-  clears[0].color.float32[0] = clear_color[0];
-  clears[0].color.float32[1] = clear_color[1];
-  clears[0].color.float32[2] = clear_color[2];
-  clears[0].color.float32[3] = clear_color[3];
-  clears[1].depthStencil.depth = get_clear_depth();
-  clears[1].depthStencil.stencil = get_clear_stencil();
+    barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    barriers[1].pNext = NULL;
+    barriers[1].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+    barriers[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+    barriers[1].oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+    barriers[1].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+    barriers[1].srcQueueFamilyIndex = vkgsg->_graphics_queue_family_index; //TODO support separate present queue.
+    barriers[1].dstQueueFamilyIndex = vkgsg->_graphics_queue_family_index;
+    barriers[1].image = _depth_stencil_image;
+    barriers[1].subresourceRange.aspectMask = _depth_stencil_aspect_mask;
+    barriers[1].subresourceRange.baseMipLevel = 0;
+    barriers[1].subresourceRange.levelCount = 1;
+    barriers[1].subresourceRange.baseArrayLayer = 0;
+    barriers[1].subresourceRange.layerCount = 1;
+
+    if (!_depth_stencil_layout_defined) {
+      barriers[1].srcAccessMask = 0;
+      barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+      _depth_stencil_layout_defined = true;
+    }
 
-  VkRenderPassBeginInfo begin_info;
-  begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
-  begin_info.pNext = NULL;
-  begin_info.renderPass = _render_pass;
-  begin_info.framebuffer = buffer._framebuffer;
-  begin_info.renderArea.offset.x = 0;
-  begin_info.renderArea.offset.y = 0;
-  begin_info.renderArea.extent.width = _size[0];
-  begin_info.renderArea.extent.height = _size[1];
-  begin_info.clearValueCount = 2;
-  begin_info.pClearValues = clears;
+    vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+                         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
+                         0, NULL, 0, NULL, 2, barriers);
+  }
 
   vkCmdBeginRenderPass(cmd, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
   vkgsg->_render_pass = _render_pass;
@@ -276,7 +307,6 @@ end_flip() {
   VkFence fence = vkgsg->_fence;
 
   nassertv(_present_complete != VK_NULL_HANDLE);
-  nassertv(_depth_stencil_layout_defined);
 
   SwapBuffer &buffer = _swap_buffers[_image_index];
   nassertv(buffer._layout_defined);
@@ -437,29 +467,104 @@ open_window() {
   // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the
   // surface has no preferred format.  Otherwise, at least one supported
   // format will be returned.
-  //TODO: add more logic for picking a suitabl format.
+  //TODO: add more logic for picking a suitable format.
   if (num_formats == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
-    _surface_format.format = VK_FORMAT_B8G8R8A8_UNORM;
+    if (_fb_properties.get_srgb_color()) {
+      _surface_format.format = VK_FORMAT_B8G8R8A8_SRGB;
+    } else {
+      _surface_format.format = VK_FORMAT_B8G8R8A8_UNORM;
+    }
     _surface_format.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
   } else {
+    // Find a format that meets the requirements.
     nassertr(num_formats > 1, false);
     _surface_format = formats[0];
+
+    if (_fb_properties.get_srgb_color()) {
+      for (uint32_t i = 0U; i < num_formats; ++i) {
+        if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB ||
+            formats[i].format == VK_FORMAT_R8G8B8A8_SRGB) {
+          _surface_format = formats[i];
+          _fb_properties.set_rgba_bits(8, 8, 8, 8);
+          break;
+        }
+      }
+    } else {
+      for (uint32_t i = 0U; i < num_formats; ++i) {
+        if (formats[i].format == VK_FORMAT_B8G8R8A8_UNORM ||
+            formats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
+          _surface_format = formats[i];
+          _fb_properties.set_rgba_bits(8, 8, 8, 8);
+          break;
+        }
+      }
+    }
   }
 
   // Choose a suitable depth/stencil format that satisfies the requirements.
-  if (_fb_properties.get_depth_bits() > 24 ||
-      _fb_properties.get_float_depth()) {
-    _fb_properties.set_depth_bits(32);
-    if (_fb_properties.get_stencil_bits() > 0) {
+  VkFormatProperties fmt_props;
+  bool request_depth32 = _fb_properties.get_depth_bits() > 24 ||
+                         _fb_properties.get_float_depth();
+
+  if (_fb_properties.get_depth_bits() > 0 || _fb_properties.get_stencil_bits() > 0) {
+    // Vulkan requires support for at least of one of these two formats.
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT_S8_UINT, &fmt_props);
+    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D24_UNORM_S8_UINT, &fmt_props);
+    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+
+    if ((supports_depth32 && request_depth32) || !supports_depth24) {
       _depth_stencil_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
-      _fb_properties.set_stencil_bits(8);
+      _fb_properties.set_depth_bits(32);
     } else {
+      _depth_stencil_format = VK_FORMAT_D24_UNORM_S8_UINT;
+      _fb_properties.set_depth_bits(24);
+    }
+    _fb_properties.set_stencil_bits(8);
+
+    _depth_stencil_aspect_mask |= VK_IMAGE_ASPECT_DEPTH_BIT |
+                                  VK_IMAGE_ASPECT_STENCIL_BIT;
+
+  } else if (_fb_properties.get_depth_bits() > 0) {
+    // Vulkan requires support for at least of one of these two formats.
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT, &fmt_props);
+    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_X8_D24_UNORM_PACK32, &fmt_props);
+    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+
+    if ((supports_depth32 && request_depth32) || !supports_depth24) {
       _depth_stencil_format = VK_FORMAT_D32_SFLOAT;
+      _fb_properties.set_depth_bits(32);
+    } else {
+      _depth_stencil_format = VK_FORMAT_X8_D24_UNORM_PACK32;
+      _fb_properties.set_depth_bits(24);
     }
+
+    _depth_stencil_aspect_mask |= VK_IMAGE_ASPECT_DEPTH_BIT;
+
   } else {
-    _depth_stencil_format = VK_FORMAT_D24_UNORM_S8_UINT;
-    _fb_properties.set_depth_bits(24);
-    _fb_properties.set_stencil_bits(8);
+    _depth_stencil_format = VK_FORMAT_UNDEFINED;
+    _depth_stencil_aspect_mask |= 0;
+  }
+
+  return setup_render_pass() && create_swapchain();
+}
+
+/**
+ * Creates a render pass object for this window.  Call this whenever the
+ * format or clear parameters change.  Note that all pipeline states become
+ * invalid if the render pass is no longer compatible; however, we currently
+ * call this only when the clear flags change, which does not affect pipeline
+ * compatibility.
+ */
+bool VulkanGraphicsWindow::
+setup_render_pass() {
+  VulkanGraphicsStateGuardian *vkgsg;
+  DCAST_INTO_R(vkgsg, _gsg, false);
+
+  if (vulkandisplay_cat.is_debug()) {
+    vulkandisplay_cat.debug()
+      << "Creating render pass for VulkanGraphicsWindow " << this << "\n";
   }
 
   // Now we want to create a render pass, and for that we need to describe the
@@ -468,7 +573,7 @@ open_window() {
   attachments[0].flags = 0;
   attachments[0].format = _surface_format.format;
   attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
-  attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
   attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@@ -478,13 +583,27 @@ open_window() {
   attachments[1].flags = 0;
   attachments[1].format = _depth_stencil_format;
   attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
-  attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
   attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
   attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
   attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
+  if (get_clear_color_active()) {
+    attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  } else {
+    attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+  }
+
+  if (get_clear_depth_active()) {
+    attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  }
+
+  if (get_clear_stencil_active()) {
+    attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  }
+
   VkAttachmentReference color_reference;
   color_reference.attachment = 0;
   color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@@ -501,7 +620,7 @@ open_window() {
   subpass.colorAttachmentCount = 1;
   subpass.pColorAttachments = &color_reference;
   subpass.pResolveAttachments = NULL;
-  subpass.pDepthStencilAttachment = &depth_reference;
+  subpass.pDepthStencilAttachment = _depth_stencil_format ? &depth_reference : NULL;
   subpass.preserveAttachmentCount = 0;
   subpass.pPreserveAttachments = NULL;
 
@@ -509,20 +628,35 @@ open_window() {
   pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
   pass_info.pNext = NULL;
   pass_info.flags = 0;
-  pass_info.attachmentCount = 2;
+  pass_info.attachmentCount = _depth_stencil_format ? 2 : 1;
   pass_info.pAttachments = attachments;
   pass_info.subpassCount = 1;
   pass_info.pSubpasses = &subpass;
   pass_info.dependencyCount = 0;
   pass_info.pDependencies = NULL;
 
-  err = vkCreateRenderPass(vkgsg->_device, &pass_info, NULL, &_render_pass);
+  VkRenderPass pass;
+  VkResult
+  err = vkCreateRenderPass(vkgsg->_device, &pass_info, NULL, &pass);
   if (err) {
     vulkan_error(err, "Failed to create render pass");
     return false;
   }
 
-  return create_swapchain();
+  // Destroy the previous render pass object.
+  if (_render_pass != VK_NULL_HANDLE) {
+    // Actually, we can't destroy it, since we may now have pipeline states
+    // that reference it.  Destroying it now would also require destroying the
+    // framebuffer and clearing all of the prepared states from the GSG.
+    // Maybe we need to start reference counting render passes?
+    vulkandisplay_cat.warning() << "Leaking VkRenderPass.\n";
+    //vkDestroyRenderPass(vkgsg->_device, _render_pass, NULL);
+    //_render_pass = VK_NULL_HANDLE;
+  }
+
+  _render_pass = pass;
+  _current_clear_mask = _clear_mask;
+  return true;
 }
 
 /**
@@ -593,6 +727,11 @@ create_swapchain() {
   VkDevice device = vkgsg->_device;
   VkResult err;
 
+  if (vulkandisplay_cat.is_debug()) {
+    vulkandisplay_cat.debug()
+      << "Creating swap chain and framebuffers for VulkanGraphicsWindow " << this << "\n";
+  }
+
   // Get the surface capabilities to make sure we make a compatible swapchain.
   VkSurfaceCapabilitiesKHR surf_caps;
   err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkpipe->_gpu, _surface, &surf_caps);
@@ -695,83 +834,89 @@ create_swapchain() {
   }
 
   // Now create a depth image.
-  VkImageCreateInfo depth_img_info;
-  depth_img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-  depth_img_info.pNext = NULL;
-  depth_img_info.flags = 0;
-  depth_img_info.imageType = VK_IMAGE_TYPE_2D;
-  depth_img_info.format = _depth_stencil_format;
-  depth_img_info.extent.width = _size[0];
-  depth_img_info.extent.height = _size[1];
-  depth_img_info.extent.depth = 1;
-  depth_img_info.mipLevels = 1;
-  depth_img_info.arrayLayers = 1;
-  depth_img_info.samples = VK_SAMPLE_COUNT_1_BIT;
-  depth_img_info.tiling = VK_IMAGE_TILING_OPTIMAL;
-  depth_img_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
-  depth_img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-  depth_img_info.queueFamilyIndexCount = 0;
-  depth_img_info.pQueueFamilyIndices = NULL;
-  depth_img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
-  err = vkCreateImage(device, &depth_img_info, NULL, &_depth_stencil_image);
-  if (err) {
-    vulkan_error(err, "Failed to create depth image");
-    return false;
-  }
+  _depth_stencil_image = VK_NULL_HANDLE;
+  _depth_stencil_view = VK_NULL_HANDLE;
+  bool have_ds = (_depth_stencil_format != VK_FORMAT_UNDEFINED);
+
+  if (have_ds) {
+    VkImageCreateInfo depth_img_info;
+    depth_img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    depth_img_info.pNext = NULL;
+    depth_img_info.flags = 0;
+    depth_img_info.imageType = VK_IMAGE_TYPE_2D;
+    depth_img_info.format = _depth_stencil_format;
+    depth_img_info.extent.width = _size[0];
+    depth_img_info.extent.height = _size[1];
+    depth_img_info.extent.depth = 1;
+    depth_img_info.mipLevels = 1;
+    depth_img_info.arrayLayers = 1;
+    depth_img_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    depth_img_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    depth_img_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+    depth_img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    depth_img_info.queueFamilyIndexCount = 0;
+    depth_img_info.pQueueFamilyIndices = NULL;
+    depth_img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    err = vkCreateImage(device, &depth_img_info, NULL, &_depth_stencil_image);
+    if (err) {
+      vulkan_error(err, "Failed to create depth image");
+      return false;
+    }
 
-  // Get the memory requirements, and find an appropriate heap to alloc in.
-  VkMemoryRequirements mem_reqs;
-  vkGetImageMemoryRequirements(device, _depth_stencil_image, &mem_reqs);
+    // Get the memory requirements, and find an appropriate heap to alloc in.
+    VkMemoryRequirements mem_reqs;
+    vkGetImageMemoryRequirements(device, _depth_stencil_image, &mem_reqs);
 
-  VkMemoryAllocateInfo alloc_info;
-  alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
-  alloc_info.pNext = NULL;
-  alloc_info.allocationSize = mem_reqs.size;
+    VkMemoryAllocateInfo alloc_info;
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    alloc_info.pNext = NULL;
+    alloc_info.allocationSize = mem_reqs.size;
 
-  if (!vkpipe->find_memory_type(alloc_info.memoryTypeIndex, mem_reqs.memoryTypeBits, 0)) {
-    vulkan_error(err, "Failed to find memory heap to allocate depth buffer");
-    return false;
-  }
+    if (!vkpipe->find_memory_type(alloc_info.memoryTypeIndex, mem_reqs.memoryTypeBits, 0)) {
+      vulkan_error(err, "Failed to find memory heap to allocate depth buffer");
+      return false;
+    }
 
-  err = vkAllocateMemory(device, &alloc_info, NULL, &_depth_stencil_memory);
-  if (err) {
-    vulkan_error(err, "Failed to allocate memory for depth image");
-    return false;
-  }
+    err = vkAllocateMemory(device, &alloc_info, NULL, &_depth_stencil_memory);
+    if (err) {
+      vulkan_error(err, "Failed to allocate memory for depth image");
+      return false;
+    }
 
-  // Bind memory to image.
-  err = vkBindImageMemory(device, _depth_stencil_image, _depth_stencil_memory, 0);
-  if (err) {
-    vulkan_error(err, "Failed to bind memory to depth image");
-    return false;
-  }
+    // Bind memory to image.
+    err = vkBindImageMemory(device, _depth_stencil_image, _depth_stencil_memory, 0);
+    if (err) {
+      vulkan_error(err, "Failed to bind memory to depth image");
+      return false;
+    }
 
-  VkImageViewCreateInfo view_info;
-  view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-  view_info.pNext = NULL;
-  view_info.flags = 0;
-  view_info.image = _depth_stencil_image;
-  view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-  view_info.format = depth_img_info.format;
-  view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
-  view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
-  view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
-  view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
-  view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
-  view_info.subresourceRange.baseMipLevel = 0;
-  view_info.subresourceRange.levelCount = 1;
-  view_info.subresourceRange.baseArrayLayer = 0;
-  view_info.subresourceRange.layerCount = 1;
-
-  if (_fb_properties.get_stencil_bits()) {
-    view_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
-  }
-
-  err = vkCreateImageView(device, &view_info, NULL, &_depth_stencil_view);
-  if (err) {
-    vulkan_error(err, "Failed to create image view for depth/stencil");
-    return false;
+    VkImageViewCreateInfo view_info;
+    view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    view_info.pNext = NULL;
+    view_info.flags = 0;
+    view_info.image = _depth_stencil_image;
+    view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    view_info.format = depth_img_info.format;
+    view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    view_info.subresourceRange.aspectMask = _depth_stencil_aspect_mask;
+    view_info.subresourceRange.baseMipLevel = 0;
+    view_info.subresourceRange.levelCount = 1;
+    view_info.subresourceRange.baseArrayLayer = 0;
+    view_info.subresourceRange.layerCount = 1;
+
+    if (_fb_properties.get_stencil_bits()) {
+      view_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
+    }
+
+    err = vkCreateImageView(device, &view_info, NULL, &_depth_stencil_view);
+    if (err) {
+      vulkan_error(err, "Failed to create image view for depth/stencil");
+      return false;
+    }
   }
 
   // Now finally create a framebuffer for each link in the swap chain.
@@ -783,7 +928,7 @@ create_swapchain() {
   fb_info.pNext = NULL;
   fb_info.flags = 0;
   fb_info.renderPass = _render_pass;
-  fb_info.attachmentCount = 2;
+  fb_info.attachmentCount = 1 + (int)have_ds;
   fb_info.pAttachments = attach_views;
   fb_info.width = _size[0];
   fb_info.height = _size[1];
@@ -847,20 +992,26 @@ create_swapchain() {
     barriers[0].subresourceRange.baseArrayLayer = 0;
     barriers[0].subresourceRange.layerCount = 1;
 
-    barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-    barriers[1].pNext = NULL;
-    barriers[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
-    barriers[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
-    barriers[1].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
-    barriers[1].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-    barriers[1].srcQueueFamilyIndex = vkgsg->_graphics_queue_family_index;
-    barriers[1].dstQueueFamilyIndex = vkgsg->_graphics_queue_family_index; //TODO support separate present queue.
-    barriers[1].image = _depth_stencil_image;
-    barriers[1].subresourceRange = view_info.subresourceRange;
+    if (have_ds) {
+      barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+      barriers[1].pNext = NULL;
+      barriers[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+      barriers[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+      barriers[1].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+      barriers[1].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+      barriers[1].srcQueueFamilyIndex = vkgsg->_graphics_queue_family_index;
+      barriers[1].dstQueueFamilyIndex = vkgsg->_graphics_queue_family_index; //TODO support separate present queue.
+      barriers[1].image = _depth_stencil_image;
+      barriers[1].subresourceRange.aspectMask = _depth_stencil_aspect_mask;
+      barriers[1].subresourceRange.baseMipLevel = 0;
+      barriers[1].subresourceRange.levelCount = 1;
+      barriers[1].subresourceRange.baseArrayLayer = 0;
+      barriers[1].subresourceRange.layerCount = 1;
+    }
 
     vkCmdPipelineBarrier(present_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                          VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
-                         0, NULL, 0, NULL, 2, barriers);
+                         0, NULL, 0, NULL, 1 + (int)have_ds, barriers);
 
     vkEndCommandBuffer(present_cmd);
   }

+ 5 - 0
panda/src/vulkandisplay/vulkanGraphicsWindow.h

@@ -43,6 +43,7 @@ public:
                        GraphicsOutput *host);
   virtual ~VulkanGraphicsWindow();
 
+  virtual void clear(Thread *current_thread);
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
 
@@ -54,6 +55,8 @@ protected:
   virtual void close_window();
   virtual bool open_window();
 
+  bool setup_render_pass();
+
   void destroy_swapchain();
   bool create_swapchain();
 
@@ -62,6 +65,7 @@ private:
   VkSwapchainKHR _swapchain;
   VkRenderPass _render_pass;
   VkSemaphore _present_complete;
+  int _current_clear_mask;
 
   LVecBase2i _swapchain_size;
   VkSurfaceFormatKHR _surface_format;
@@ -83,6 +87,7 @@ private:
   VkImage _depth_stencil_image;
   VkImageView _depth_stencil_view;
   VkDeviceMemory _depth_stencil_memory;
+  VkImageAspectFlags _depth_stencil_aspect_mask;
 
 public:
   static TypeHandle get_class_type() {