Browse Source

vulkan: Experiment with switching over to dynamic rendering

It's a lot less of a headache than dealing with the implicit layout transitions, and we can more easily stop and start rendering if we need to eg. switch cube map index

There's one point of annoyance, which is that we can't do pipeline barriers mid-render anymore... that's annoying, and it could be an issue if we're using a texture as both a storage image and a sampled image in the same pass, but maybe this is a good restriction to enforce on the Panda end

If we want to support pre-1.3 Vulkan, we could emulate dynamic rendering by creating one render pass per fb config that doesn't do any clears or discards or transitions
rdb 1 year ago
parent
commit
a3f759b5f4

+ 5 - 0
panda/src/vulkandisplay/vulkanFrameData.cxx

@@ -142,6 +142,11 @@ end_transfer_cmd() {
         barrier.offset = 0;
         barrier.size = VK_WHOLE_SIZE;
       }
+
+      tc->_initial_src_access_mask = 0;
+      tc->_initial_dst_access_mask = 0;
+      tc->_initial_src_layout = VK_IMAGE_LAYOUT_UNDEFINED;
+      tc->_initial_dst_layout = VK_IMAGE_LAYOUT_UNDEFINED;
     }
     vkCmdPipelineBarrier(_transfer_cmd, _initial_barrier_src_stage_mask,
                          _initial_barrier_dst_stage_mask, 0,

+ 91 - 9
panda/src/vulkandisplay/vulkanGraphicsBuffer.cxx

@@ -144,7 +144,84 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   VulkanFrameData &frame_data = vkgsg->get_frame_data();
   VkCommandBuffer cmd = frame_data._cmd;
 
-  VkClearValue *clears = (VkClearValue *)
+  VkRenderingAttachmentInfo *color_attachments = (VkRenderingAttachmentInfo *)
+    alloca(_attachments.size() * sizeof(VkRenderingAttachmentInfo));
+  VkRenderingAttachmentInfo depth_attachment;
+  //VkRenderingAttachmentInfo stencil_attachment;
+
+  VkRenderingInfo render_info = {VK_STRUCTURE_TYPE_RENDERING_INFO};
+  render_info.layerCount = 1;
+  render_info.renderArea.extent.width = _framebuffer_size[0];
+  render_info.renderArea.extent.height = _framebuffer_size[1];
+  render_info.colorAttachmentCount = 0;
+  render_info.pColorAttachments = color_attachments;
+
+  for (size_t i = 0; i < _attachments.size(); ++i) {
+    Attachment &attach = _attachments[i];
+    nassertr(!attach._tc->is_used_this_frame(frame_data), false);
+    attach._tc->mark_used_this_frame(frame_data);
+
+    VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+    VkAccessFlags write_access_mask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+    VkAccessFlags read_access_mask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
+    VkPipelineStageFlags stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+    if (attach._plane == RTP_stencil || attach._plane == RTP_depth ||
+        attach._plane == RTP_depth_stencil) {
+      depth_attachment = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
+      render_info.pDepthAttachment = &depth_attachment;
+      vkgsg->_fb_depth_tc = attach._tc;
+
+      layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+      stage_mask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+                 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+      write_access_mask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+      read_access_mask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
+
+      if (get_clear_depth_active()) {
+        depth_attachment.clearValue.depthStencil.depth = get_clear_depth();
+        depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+      } else {
+        depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+      }
+      depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+      depth_attachment.imageView = attach._tc->get_image_view(0);
+      depth_attachment.imageLayout = layout;
+    }
+    else if (attach._plane == RTP_color) {
+      VkRenderingAttachmentInfo &color_attachment =
+        color_attachments[render_info.colorAttachmentCount++];
+      color_attachment = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
+      vkgsg->_fb_color_tc = attach._tc;
+
+      if (get_clear_active(attach._plane)) {
+        LColor clear_color = get_clear_value(attach._plane);
+        color_attachment.clearValue.color.float32[0] = clear_color[0];
+        color_attachment.clearValue.color.float32[1] = clear_color[1];
+        color_attachment.clearValue.color.float32[2] = clear_color[2];
+        color_attachment.clearValue.color.float32[3] = clear_color[3];
+        color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+      } else {
+        color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+      }
+      color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+      color_attachment.imageView = attach._tc->get_image_view(0);
+      color_attachment.imageLayout = layout;
+    }
+
+    if (attach._tc->_layout != layout ||
+        (attach._tc->_write_stage_mask & ~stage_mask) != 0 ||
+        (attach._tc->_read_stage_mask & ~stage_mask) != 0) {
+      frame_data.add_initial_barrier(attach._tc,
+        layout, stage_mask, read_access_mask | write_access_mask);
+    }
+  }
+
+  vkgsg->_vkCmdBeginRendering(cmd, &render_info);
+  vkgsg->_fb_config = _fb_config_id;
+  return true;
+
+  /*VkClearValue *clears = (VkClearValue *)
     alloca(sizeof(VkClearValue) * _attachments.size());
 
   VkRenderPassBeginInfo begin_info;
@@ -205,7 +282,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
   vkCmdBeginRenderPass(cmd, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
   vkgsg->_render_pass = _render_pass;
-  vkgsg->_fb_config = _fb_config_id;
+  vkgsg->_fb_config = _fb_config_id;*/
 
   return true;
 }
@@ -225,7 +302,8 @@ end_frame(FrameMode mode, Thread *current_thread) {
     VkCommandBuffer cmd = vkgsg->_frame_data->_cmd;
     nassertv(cmd != VK_NULL_HANDLE);
 
-    vkCmdEndRenderPass(cmd);
+    vkgsg->_vkCmdEndRendering(cmd);
+    /*vkCmdEndRenderPass(cmd);
     vkgsg->_render_pass = VK_NULL_HANDLE;
 
     // The driver implicitly transitioned this to the final layout.
@@ -234,7 +312,7 @@ end_frame(FrameMode mode, Thread *current_thread) {
 
       // This seems to squelch a validation warning, not sure about this yet
       attach._tc->_write_stage_mask |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-    }
+    }*/
 
     // Now we can do copy-to-texture, now that the render pass has ended.
     copy_to_textures();
@@ -332,6 +410,8 @@ open_buffer() {
  */
 bool VulkanGraphicsBuffer::
 setup_render_pass() {
+  return true;
+
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_R(vkgsg, _gsg, false);
 
@@ -531,14 +611,14 @@ destroy_framebuffer() {
   }
   _attachments.clear();
 
-  if (_framebuffer != VK_NULL_HANDLE) {
+  /*if (_framebuffer != VK_NULL_HANDLE) {
     if (vkgsg->_last_frame_data != nullptr) {
       vkgsg->_last_frame_data->_pending_destroy_framebuffers.push_back(_framebuffer);
     } else {
       vkDestroyFramebuffer(device, _framebuffer, nullptr);
     }
     _framebuffer = VK_NULL_HANDLE;
-  }
+  }*/
 
   _is_valid = false;
 }
@@ -552,8 +632,6 @@ create_framebuffer(CDReader &cdata) {
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_R(vkpipe, _pipe, false);
   DCAST_INTO_R(vkgsg, _gsg, false);
-  VkDevice device = vkgsg->_device;
-  VkResult err;
 
   PT(Texture) color_texture;
   PT(Texture) depth_texture;
@@ -579,6 +657,10 @@ create_framebuffer(CDReader &cdata) {
     }
   }
 
+  /*
+  VkDevice device = vkgsg->_device;
+  VkResult err;
+
   uint32_t num_attachments = _attachments.size();
   VkImageView *attach_views = (VkImageView *)alloca(sizeof(VkImageView) * num_attachments);
 
@@ -601,7 +683,7 @@ create_framebuffer(CDReader &cdata) {
   if (err) {
     vulkan_error(err, "Failed to create framebuffer");
     return false;
-  }
+  }*/
 
   _framebuffer_size = _size;
   _is_valid = true;

+ 13 - 2
panda/src/vulkandisplay/vulkanGraphicsPipe.cxx

@@ -147,8 +147,8 @@ VulkanGraphicsPipe() : _max_allocation_size(0) {
   app_info.engineVersion = PANDA_NUMERIC_VERSION;
   app_info.apiVersion = VK_API_VERSION_1_0;
 
-  if (inst_version >= VK_MAKE_VERSION(1, 1, 0)) {
-    app_info.apiVersion = VK_MAKE_VERSION(1, 1, 0);
+  if (inst_version >= VK_MAKE_VERSION(1, 3, 0)) {
+    app_info.apiVersion = VK_MAKE_VERSION(1, 3, 0);
   }
 
   VkInstanceCreateInfo inst_info;
@@ -310,6 +310,15 @@ VulkanGraphicsPipe() : _max_allocation_size(0) {
   if (pVkGetPhysicalDeviceFeatures2 != nullptr) {
     VkPhysicalDeviceFeatures2 features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
 
+    VkPhysicalDeviceDynamicRenderingFeatures dr_features = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
+      features2.pNext,
+    };
+    if (_gpu_properties.apiVersion >= VK_MAKE_VERSION(1, 3, 0) ||
+        has_device_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
+      features2.pNext = &dr_features;
+    }
+
     VkPhysicalDeviceCustomBorderColorFeaturesEXT cbc_features = {
       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
       features2.pNext,
@@ -353,6 +362,7 @@ VulkanGraphicsPipe() : _max_allocation_size(0) {
 
     pVkGetPhysicalDeviceFeatures2(_gpu, &features2);
     _gpu_features = features2.features;
+    _gpu_supports_dynamic_rendering = dr_features.dynamicRendering;
     _gpu_supports_custom_border_colors = cbc_features.customBorderColors
                                       && cbc_features.customBorderColorWithoutFormat;
     _gpu_supports_null_descriptor = ro2_features.nullDescriptor;
@@ -364,6 +374,7 @@ VulkanGraphicsPipe() : _max_allocation_size(0) {
   }
   else {
     vkGetPhysicalDeviceFeatures(_gpu, &_gpu_features);
+    _gpu_supports_dynamic_rendering = false;
     _gpu_supports_custom_border_colors = false;
     _gpu_supports_null_descriptor = false;
     _gpu_supports_vertex_attrib_divisor = false;

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

@@ -74,6 +74,7 @@ public:
   VkPhysicalDevice _gpu;
   VkPhysicalDeviceFeatures _gpu_features;
   VkPhysicalDeviceProperties _gpu_properties;
+  bool _gpu_supports_dynamic_rendering = false;
   bool _gpu_supports_custom_border_colors = false;
   bool _gpu_supports_null_descriptor = false;
   bool _gpu_supports_vertex_attrib_divisor = false;

+ 66 - 7
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -136,6 +136,22 @@ reset() {
   enabled_features.features.textureCompressionBC = features.textureCompressionBC;
   enabled_features.features.shaderFloat64 = features.shaderFloat64;
 
+  VkPhysicalDeviceDynamicRenderingFeatures dr_features = {
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
+    enabled_features.pNext,
+  };
+  if (pipe->_gpu_supports_dynamic_rendering) {
+    dr_features.dynamicRendering = VK_TRUE;
+    enabled_features.pNext = &dr_features;
+
+    if (pipe->_gpu_properties.apiVersion < VK_MAKE_VERSION(1, 3, 0)) {
+      extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
+    }
+    _supports_dynamic_rendering = true;
+  } else {
+    _supports_dynamic_rendering = false;
+  }
+
   VkPhysicalDeviceCustomBorderColorFeaturesEXT cbc_features = {
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
     enabled_features.pNext,
@@ -294,6 +310,11 @@ reset() {
   _vkCmdPushConstants = (PFN_vkCmdPushConstants)vkGetDeviceProcAddr(_device, "vkCmdPushConstants");
   _vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets)vkGetDeviceProcAddr(_device, "vkUpdateDescriptorSets");
 
+  if (_supports_dynamic_rendering) {
+    _vkCmdBeginRendering = (PFN_vkCmdBeginRendering)vkGetDeviceProcAddr(_device, "vkCmdBeginRendering");
+    _vkCmdEndRendering = (PFN_vkCmdEndRendering)vkGetDeviceProcAddr(_device, "vkCmdEndRendering");
+  }
+
   if (_supports_extended_dynamic_state2) {
     _vkCmdSetPrimitiveTopologyEXT = (PFN_vkCmdSetPrimitiveTopologyEXT)vkGetDeviceProcAddr(_device, "vkCmdSetPrimitiveTopologyEXT");
     _vkCmdSetPrimitiveRestartEnableEXT = (PFN_vkCmdSetPrimitiveRestartEnableEXT)vkGetDeviceProcAddr(_device, "vkCmdSetPrimitiveRestartEnableEXT");
@@ -467,6 +488,21 @@ reset() {
     }
   }
 
+  // Make a descriptor set that we apply when there are no lights.
+  {
+    VkDescriptorSetAllocateInfo alloc_info;
+    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    alloc_info.pNext = nullptr;
+    alloc_info.descriptorPool = _descriptor_pool;
+    alloc_info.descriptorSetCount = 1;
+    alloc_info.pSetLayouts = &_lattr_descriptor_set_layout;
+    VkResult err = vkAllocateDescriptorSets(_device, &alloc_info, &_empty_lattr_descriptor_set);
+    if (err) {
+      vulkan_error(err, "Failed to allocate descriptor set for empty light attribute");
+      return;
+    }
+  }
+
   // Create a uniform buffer that we'll use for everything.
   // Some cards set aside 256 MiB of device-local host-visible memory for data
   // like this, so we use that.
@@ -937,6 +973,12 @@ use_texture(Texture *texture, VkImageLayout layout,
   VulkanTextureContext *tc;
   DCAST_INTO_R(tc, texture->prepare_now(_prepared_objects, this), nullptr);
 
+  if (_fb_color_tc == tc || _fb_depth_tc == tc) {
+    vulkandisplay_cat.warning()
+      << "Attempt to use framebuffer texture " << *texture << " during render!\n";
+    return nullptr;
+  }
+
   // We only update the texture the first time it is used in a frame.
   // Otherwise, we would have to invalidate the descriptor sets.
   if (!tc->is_used_this_frame(frame_data)) {
@@ -2640,12 +2682,17 @@ set_state_and_transform(const RenderState *state,
 
   uint32_t first_set = 0;
   const LightAttrib *target_light;
-  state->get_attrib_def(target_light);
+  state->get_attrib(target_light);
   if (shader_changed ||
-      target_light != _state_rs->get_attrib_def(LightAttrib::get_class_slot())) {
-    if (get_attrib_descriptor_set(descriptor_sets[DS_light_attrib],
-                                  _lattr_descriptor_set_layout,
-                                  target_light)) {
+      target_light != _state_rs->get_attrib(LightAttrib::get_class_slot())) {
+    if (!sc->_uses_lattr_descriptors ||
+        target_light == nullptr ||
+        target_light->is_identity()) {
+      descriptor_sets[DS_light_attrib] = _empty_lattr_descriptor_set;
+    }
+    else if (get_attrib_descriptor_set(descriptor_sets[DS_light_attrib],
+                                      _lattr_descriptor_set_layout,
+                                      target_light)) {
       // The first time this set is bound in this frame, we update it.  Once we
       // use it in a command buffer, we can't update it again anyway.
       update_lattr_descriptor_set(descriptor_sets[DS_light_attrib], target_light);
@@ -4579,6 +4626,18 @@ make_pipeline(VulkanShaderContext *sc,
   pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
   pipeline_info.basePipelineIndex = 0;
 
+  VkPipelineRenderingCreateInfo render_info;
+  if (_render_pass == VK_NULL_HANDLE) {
+    pipeline_info.pNext = &render_info;
+    render_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
+    render_info.pNext = nullptr;
+    render_info.viewMask = 0;
+    render_info.colorAttachmentCount = fb_config._color_formats.size();
+    render_info.pColorAttachmentFormats = fb_config._color_formats.data();
+    render_info.depthAttachmentFormat = fb_config._depth_format;
+    render_info.stencilAttachmentFormat = fb_config._stencil_format;
+  }
+
   VkResult err;
   VkPipeline pipeline;
   err = vkCreateGraphicsPipelines(_device, _pipeline_cache, 1, &pipeline_info, nullptr, &pipeline);
@@ -4748,8 +4807,8 @@ update_lattr_descriptor_set(VkDescriptorSet ds, const LightAttrib *attr) {
 
     VkDescriptorImageInfo &image_info = image_infos[i];
     image_info.sampler = VK_NULL_HANDLE;
-    image_info.imageView = tc->get_image_view(0);
-    image_info.imageLayout = tc->_layout;
+    image_info.imageView = tc ? tc->get_image_view(0) : VK_NULL_HANDLE;
+    image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 
     VkWriteDescriptorSet &write = writes[i];
     write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;

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

@@ -281,6 +281,7 @@ private:
   // The others are shader-dependent and stored in VulkanShaderContext.
   VkSampler _shadow_sampler;
   VkDescriptorSetLayout _lattr_descriptor_set_layout;
+  VkDescriptorSet _empty_lattr_descriptor_set;
 
   // Keep track of all the individual allocations.
   Mutex _allocator_lock;
@@ -298,6 +299,7 @@ private:
   uint64_t _last_finished_frame = 0;
 
   // Feature checks.
+  bool _supports_dynamic_rendering = false;
   bool _supports_custom_border_colors = false;
   bool _supports_vertex_attrib_divisor = false;
   bool _supports_vertex_attrib_zero_divisor = false;
@@ -305,11 +307,13 @@ private:
   bool _supports_extended_dynamic_state2_patch_control_points = false;
 
   // Function pointers.
+  PFN_vkCmdBeginRendering _vkCmdBeginRendering;
   PFN_vkCmdBindIndexBuffer _vkCmdBindIndexBuffer;
   PFN_vkCmdBindPipeline _vkCmdBindPipeline;
   PFN_vkCmdBindVertexBuffers _vkCmdBindVertexBuffers;
   PFN_vkCmdDraw _vkCmdDraw;
   PFN_vkCmdDrawIndexed _vkCmdDrawIndexed;
+  PFN_vkCmdEndRendering _vkCmdEndRendering;
   PFN_vkCmdPushConstants _vkCmdPushConstants;
   PFN_vkCmdSetPatchControlPointsEXT _vkCmdSetPatchControlPointsEXT;
   PFN_vkCmdSetPrimitiveRestartEnableEXT _vkCmdSetPrimitiveRestartEnableEXT;

+ 105 - 20
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -89,7 +89,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
       << "Drawing " << this << ": exposed.\n";
   }
 
-  /*if (mode != FM_render) {
+  /*if (mode == FM_refresh) {
     return true;
   }*/
 
@@ -103,10 +103,10 @@ begin_frame(FrameMode mode, Thread *current_thread) {
       _image_available = VK_NULL_HANDLE;
     }
     destroy_swapchain();
-    if (_render_pass != VK_NULL_HANDLE) {
+    /*if (_render_pass != VK_NULL_HANDLE) {
       vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
       _render_pass = VK_NULL_HANDLE;
-    }
+    }*/
     vkgsg->reset_if_new();
   }
 
@@ -114,14 +114,14 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     return false;
   }
 
-  if (_current_clear_mask != _clear_mask || _render_pass == VK_NULL_HANDLE) {
+  /*if (_current_clear_mask != _clear_mask || _render_pass == VK_NULL_HANDLE) {
     // 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.
     if (!setup_render_pass()) {
       return false;
     }
-  }
+  }*/
 
   if (_swapchain_size != _size) {
     // Uh-oh, the window must have resized.  Recreate the swapchain.
@@ -143,7 +143,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
   copy_async_screenshot();
 
-  if (mode != FM_render) {
+  if (mode == FM_refresh) {
     return true;
   }
 
@@ -174,7 +174,89 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   // transition the swapchain images into the valid state for rendering into.
   VkCommandBuffer cmd = frame_data._cmd;
 
-  VkClearValue clears[2];
+  VkRenderingInfo render_info = {VK_STRUCTURE_TYPE_RENDERING_INFO};
+  render_info.layerCount = 1;
+  render_info.renderArea.extent.width = _swapchain_size[0];
+  render_info.renderArea.extent.height = _swapchain_size[1];
+  render_info.colorAttachmentCount = 1;
+
+  VkRenderingAttachmentInfo color_attachment = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
+  color_attachment.imageView = color_tc->get_image_view(0);
+  color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+  render_info.pColorAttachments = &color_attachment;
+
+  if (get_clear_color_active()) {
+    LColor clear_color = get_clear_color();
+    color_attachment.clearValue.color.float32[0] = clear_color[0];
+    color_attachment.clearValue.color.float32[1] = clear_color[1];
+    color_attachment.clearValue.color.float32[2] = clear_color[2];
+    color_attachment.clearValue.color.float32[3] = clear_color[3];
+    color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+  } else {
+    color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+  }
+  color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+
+  if (color_tc->_layout != color_attachment.imageLayout ||
+      (color_tc->_write_stage_mask & ~VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) != 0 ||
+      (color_tc->_read_stage_mask & ~VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) != 0) {
+    frame_data.add_initial_barrier(color_tc,
+      color_attachment.imageLayout,
+      VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+      VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
+  }
+
+  VkRenderingAttachmentInfo depth_attachment = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
+  VkRenderingAttachmentInfo stencil_attachment = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO};
+  if (_depth_stencil_tc != nullptr) {
+    nassertr(!_depth_stencil_tc->is_used_this_frame(frame_data), false);
+    _depth_stencil_tc->mark_used_this_frame(frame_data);
+
+    if (_depth_stencil_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
+      render_info.pDepthAttachment = &depth_attachment;
+
+      if (get_clear_depth_active()) {
+        depth_attachment.clearValue.depthStencil.depth = get_clear_depth();
+        depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+      } else {
+        depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+      }
+      depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+      depth_attachment.imageView = _depth_stencil_tc->get_image_view(0);
+      depth_attachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+    }
+
+    if (_depth_stencil_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
+      render_info.pStencilAttachment = &stencil_attachment;
+
+      if (get_clear_stencil_active()) {
+        stencil_attachment.clearValue.depthStencil.stencil = get_clear_stencil();
+        stencil_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+      } else {
+        stencil_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+      }
+      stencil_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+      stencil_attachment.imageView = _depth_stencil_tc->get_image_view(0);
+      stencil_attachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+    }
+
+    if (_depth_stencil_tc->_layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ||
+        (_depth_stencil_tc->_write_stage_mask & ~VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT) != 0 ||
+        (_depth_stencil_tc->_read_stage_mask & ~VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT) != 0) {
+      frame_data.add_initial_barrier(_depth_stencil_tc,
+        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+        VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
+        VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
+    }
+  }
+
+  vkgsg->_vkCmdBeginRendering(cmd, &render_info);
+  vkgsg->_fb_color_tc = color_tc;
+  vkgsg->_fb_depth_tc = _depth_stencil_tc;
+  vkgsg->_fb_config = _fb_config_id;
+  return true;
+
+  /*VkClearValue clears[2];
 
   VkRenderPassBeginInfo begin_info;
   begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
@@ -261,7 +343,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   vkgsg->_fb_color_tc = color_tc;
   vkgsg->_fb_depth_tc = _depth_stencil_tc;
   vkgsg->_fb_config = _fb_config_id;
-  return true;
+  return true;*/
 }
 
 /**
@@ -281,17 +363,18 @@ end_frame(FrameMode mode, Thread *current_thread) {
   SwapBuffer &buffer = _swap_buffers[_image_index];
 
   VkSemaphore signal_done = VK_NULL_HANDLE;
-  if (mode == FM_render) {
-    vkCmdEndRenderPass(cmd);
+  if (mode != FM_refresh) {
+    vkgsg->_vkCmdEndRendering(cmd);
+/*    vkCmdEndRenderPass(cmd);
     vkgsg->_render_pass = VK_NULL_HANDLE;
 
     // The driver implicitly transitioned this to the final layout.
-    buffer._tc->_layout = _final_layout;
+    buffer._tc->_layout = _final_layout;*/
     buffer._tc->mark_written(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                              VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
 
     if (_depth_stencil_tc != nullptr) {
-      _depth_stencil_tc->_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+      //_depth_stencil_tc->_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
       _depth_stencil_tc->mark_written(
         VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
@@ -465,10 +548,10 @@ close_window() {
     vkQueueWaitIdle(vkgsg->_queue);
     destroy_swapchain();
 
-    if (_render_pass != VK_NULL_HANDLE) {
+    /*if (_render_pass != VK_NULL_HANDLE) {
       vkDestroyRenderPass(vkgsg->_device, _render_pass, nullptr);
       _render_pass = VK_NULL_HANDLE;
-    }
+    }*/
 
     _gsg.clear();
   }
@@ -651,6 +734,8 @@ open_window() {
  */
 bool VulkanGraphicsWindow::
 setup_render_pass() {
+  return true;
+
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_R(vkgsg, _gsg, false);
 
@@ -835,7 +920,7 @@ destroy_swapchain() {
   // Destroy the resources held for each link in the swap chain.
   for (SwapBuffer &buffer : _swap_buffers) {
     // Destroy the framebuffers that use the swapchain images.
-    vkDestroyFramebuffer(device, buffer._framebuffer, nullptr);
+    //vkDestroyFramebuffer(device, buffer._framebuffer, nullptr);
     buffer._tc->_image = VK_NULL_HANDLE;
     buffer._tc->destroy_now(device);
     delete buffer._tc;
@@ -879,7 +964,7 @@ create_swapchain() {
 
   if (vulkandisplay_cat.is_debug()) {
     vulkandisplay_cat.debug()
-      << "Creating swap chain and framebuffers for VulkanGraphicsWindow " << this << "\n";
+      << "Creating swap chain for VulkanGraphicsWindow " << this << "\n";
   }
 
   // Get the surface capabilities to make sure we make a compatible swapchain.
@@ -1099,7 +1184,7 @@ create_swapchain() {
   }
 
   // Now finally create a framebuffer for each link in the swap chain.
-  VkImageView attach_views[3];
+  /*VkImageView attach_views[3];
   uint32_t num_views = 1;
 
   if (ms_color_view != VK_NULL_HANDLE) {
@@ -1120,11 +1205,11 @@ create_swapchain() {
   fb_info.pAttachments = attach_views;
   fb_info.width = swapchain_info.imageExtent.width;
   fb_info.height = swapchain_info.imageExtent.height;
-  fb_info.layers = 1;
+  fb_info.layers = 1;*/
 
   for (uint32_t i = 0; i < num_images; ++i) {
     SwapBuffer &buffer = _swap_buffers[i];
-    if (_ms_color_tc != nullptr) {
+    /*if (_ms_color_tc != nullptr) {
       attach_views[num_views - 1] = buffer._tc->get_image_view(0);
     } else {
       attach_views[0] = buffer._tc->get_image_view(0);
@@ -1133,7 +1218,7 @@ create_swapchain() {
     if (err) {
       vulkan_error(err, "Failed to create framebuffer");
       return false;
-    }
+    }*/
 
     // Don't start rendering until the image has been acquired.
     buffer._tc->mark_written(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0);

+ 52 - 9
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -68,6 +68,7 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
   // Abuse the var id field in the access chain to store the parameter index.
   // Later we replace it with the id, because the id is unique per-module.
   pvector<AccessChain> tattr_set_params;
+  pvector<AccessChain> lattr_set_params;
   pvector<AccessChain> sattr_set_params;
 
   ShaderType::Struct shader_input_block_struct;
@@ -86,13 +87,21 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     const ShaderType *remaining_type = r_extract_resources(param, chain, descriptors, param._type, num_resources);
 
     if (num_resources > 0) {
-      if (param._binding->get_state_dep() & Shader::D_texture) {
+      int state_dep = param._binding->get_state_dep();
+      if (state_dep & Shader::D_texture) {
         for (auto &item : descriptors) {
           tattr_set_params.push_back(item.first);
           _tattr_descriptors.push_back(std::move(item.second));
         }
         _num_tattr_descriptor_elements += num_resources;
-      } else {
+      }
+      else if (state_dep & Shader::D_light) {
+        for (auto &item : descriptors) {
+          lattr_set_params.push_back(item.first);
+        }
+        _uses_lattr_descriptors = true;
+      }
+      else {
         for (auto &item : descriptors) {
           sattr_set_params.push_back(item.first);
           _sattr_descriptors.push_back(std::move(item.second));
@@ -195,6 +204,20 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
         tattr_set_ids[i] = spv_module->get_parameter(index).id;
       }
     }
+    pvector<uint32_t> lattr_set_ids(lattr_set_params.size(), 0u);
+    for (size_t i = 0; i < lattr_set_params.size(); ++i) {
+      const AccessChain &chain = lattr_set_params[i];
+      int index = spv_module->find_parameter(_shader->_parameters[chain._var_id]._name);
+      if (index < 0) {
+        continue;
+      }
+      if (chain.size() > 0) {
+        // In a struct, need to hoist.
+        needs_hoist = true;
+      } else {
+        lattr_set_ids[i] = spv_module->get_parameter(index).id;
+      }
+    }
     pvector<uint32_t> sattr_set_ids(sattr_set_params.size(), 0u);
     for (size_t i = 0; i < sattr_set_params.size(); ++i) {
       const AccessChain &chain = sattr_set_params[i];
@@ -233,6 +256,22 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
           }
         }
       }
+      for (size_t i = 0; i < lattr_set_params.size(); ++i) {
+        AccessChain chain = lattr_set_params[i];
+        if (chain.size() > 0) {
+          int index = spv_module->find_parameter(_shader->_parameters[chain._var_id]._name);
+          if (index > 0) {
+            chain._var_id = spv_module->get_parameter(index).id;
+            auto it = hoist_pass._hoisted_vars.find(chain);
+            if (it != hoist_pass._hoisted_vars.end()) {
+              // Check if it hasn't been removed due to being unused.
+              if (transformer.get_db().has_definition(it->second)) {
+                lattr_set_ids[i] = it->second;
+              }
+            }
+          }
+        }
+      }
       for (size_t i = 0; i < sattr_set_params.size(); ++i) {
         AccessChain chain = sattr_set_params[i];
         if (chain.size() > 0) {
@@ -279,6 +318,9 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     if (!tattr_set_ids.empty()) {
       transformer.bind_descriptor_set(VulkanGraphicsStateGuardian::DS_texture_attrib, tattr_set_ids);
     }
+    if (!lattr_set_ids.empty()) {
+      transformer.bind_descriptor_set(VulkanGraphicsStateGuardian::DS_light_attrib, lattr_set_ids);
+    }
     if (!sattr_set_ids.empty()) {
       if (_shader_input_block._size > 0) {
         // Make room for the uniform buffer binding.
@@ -310,7 +352,8 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
       success = false;
     }
 
-    if (spv_module->get_stage() == Shader::Stage::FRAGMENT) {
+    if (spv_module->get_stage() == Shader::Stage::FRAGMENT &&
+        !_shader->_subsumes_alpha_test) {
       // Set us up to easily create versions of the shader for various alpha
       // testing modes.
       SpirVInjectAlphaTestPass pass(SpirVInjectAlphaTestPass::M_greater, 0, true);
@@ -662,8 +705,8 @@ fetch_descriptor(VulkanGraphicsStateGuardian *gsg, const Descriptor &desc,
 
       VkDescriptorImageInfo &image_info = *image_infos++;
       image_info.sampler = sc->_sampler;
-      image_info.imageView = tc->get_image_view(view);
-      image_info.imageLayout = tc->_layout;
+      image_info.imageView = tc ? tc->get_image_view(view) : VK_NULL_HANDLE;
+      image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
     }
     break;
 
@@ -682,7 +725,7 @@ fetch_descriptor(VulkanGraphicsStateGuardian *gsg, const Descriptor &desc,
                             VK_ACCESS_SHADER_READ_BIT);
 
       VkBufferView &texel_buffer_view = *texel_buffer_views++;
-      texel_buffer_view = tc->get_buffer_view(view);
+      texel_buffer_view = tc ? tc->get_buffer_view(view) : VK_NULL_HANDLE;
     }
     break;
 
@@ -716,12 +759,12 @@ fetch_descriptor(VulkanGraphicsStateGuardian *gsg, const Descriptor &desc,
       int view = gsg->get_current_tex_view_offset();
       if (desc._type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) {
         VkBufferView &texel_buffer_view = *texel_buffer_views++;
-        texel_buffer_view = tc->get_buffer_view(view);
+        texel_buffer_view = tc ? tc->get_buffer_view(view) : VK_NULL_HANDLE;
       } else {
         VkDescriptorImageInfo &image_info = *image_infos++;
         image_info.sampler = VK_NULL_HANDLE;
-        image_info.imageView = tc->get_image_view(view);
-        image_info.imageLayout = tc->_layout;
+        image_info.imageView = tc ? tc->get_image_view(view) : VK_NULL_HANDLE;
+        image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
       }
     }
     break;

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

@@ -157,6 +157,8 @@ private:
   pvector<Descriptor> _sattr_descriptors;
   size_t _num_sattr_descriptor_elements = 0;
 
+  bool _uses_lattr_descriptors = false;
+
   VkDescriptorSet _uniform_descriptor_set = VK_NULL_HANDLE;
   VkBuffer _uniform_buffer = VK_NULL_HANDLE;
   uint32_t _dynamic_uniform_offset = 0;