Browse Source

vulkan: Descriptor set management, texture binding

rdb 5 years ago
parent
commit
5fc211cc71

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

@@ -10,23 +10,3 @@
  * @author rdb
  * @author rdb
  * @date 2016-02-16
  * @date 2016-02-16
  */
  */
-
-/**
- * Returns true if these two DescriptorSetKey objects are identical.
- */
-INLINE bool VulkanGraphicsStateGuardian::DescriptorSetKey::
-operator ==(const DescriptorSetKey &other) const {
-  return _tex_attrib == other._tex_attrib &&
-         _shader_attrib == other._shader_attrib;
-}
-
-/**
- * Defines relative sorting order of DescriptorSetKey objects.
- */
-INLINE bool VulkanGraphicsStateGuardian::DescriptorSetKey::
-operator < (const DescriptorSetKey &other) const {
-  if (_tex_attrib != other._tex_attrib) {
-    return _tex_attrib < other._tex_attrib;
-  }
-  return _shader_attrib < other._shader_attrib;
-}

+ 73 - 121
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -176,35 +176,16 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     vulkan_error(err, "Failed to create pipeline cache");
     vulkan_error(err, "Failed to create pipeline cache");
   }
   }
 
 
-  // Create a descriptor set layout.
-  VkDescriptorSetLayoutBinding layout_binding;
-  layout_binding.binding = 0;
-  layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-  layout_binding.descriptorCount = 1;
-  layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-  layout_binding.pImmutableSamplers = nullptr;
-
-  VkDescriptorSetLayoutCreateInfo set_info;
-  set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-  set_info.pNext = nullptr;
-  set_info.flags = 0;
-  set_info.bindingCount = 1;
-  set_info.pBindings = &layout_binding;
-
-  err = vkCreateDescriptorSetLayout(_device, &set_info, nullptr, &_descriptor_set_layout);
-  if (err) {
-    vulkan_error(err, "Failed to create descriptor set layout");
-  }
-
+  //TODO: dynamic allocation, create more pools if we run out
   VkDescriptorPoolSize pool_size;
   VkDescriptorPoolSize pool_size;
   pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
   pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-  pool_size.descriptorCount = 64;
+  pool_size.descriptorCount = 256;
 
 
   VkDescriptorPoolCreateInfo pool_info;
   VkDescriptorPoolCreateInfo pool_info;
   pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
   pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
   pool_info.pNext = nullptr;
   pool_info.pNext = nullptr;
-  pool_info.flags = 0;
-  pool_info.maxSets = 64;
+  pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+  pool_info.maxSets = 256;
   pool_info.poolSizeCount = 1;
   pool_info.poolSizeCount = 1;
   pool_info.pPoolSizes = &pool_size;
   pool_info.pPoolSizes = &pool_size;
 
 
@@ -398,7 +379,6 @@ VulkanGraphicsStateGuardian::
   vkDestroyBuffer(_device, _uniform_buffer, nullptr);
   vkDestroyBuffer(_device, _uniform_buffer, nullptr);
   vkDestroyBuffer(_device, _color_vertex_buffer, nullptr);
   vkDestroyBuffer(_device, _color_vertex_buffer, nullptr);
   vkDestroyDescriptorPool(_device, _descriptor_pool, nullptr);
   vkDestroyDescriptorPool(_device, _descriptor_pool, nullptr);
-  vkDestroyDescriptorSetLayout(_device, _descriptor_set_layout, nullptr);
   vkDestroyPipelineCache(_device, _pipeline_cache, nullptr);
   vkDestroyPipelineCache(_device, _pipeline_cache, nullptr);
   vkDestroyCommandPool(_device, _cmd_pool, nullptr);
   vkDestroyCommandPool(_device, _cmd_pool, nullptr);
   vkDestroyFence(_device, _fence, nullptr);
   vkDestroyFence(_device, _fence, nullptr);
@@ -1203,6 +1183,8 @@ update_texture(TextureContext *tc, bool force) {
     if (!upload_texture(vtc)) {
     if (!upload_texture(vtc)) {
       return false;
       return false;
     }
     }
+
+    vtc->mark_loaded();
   }
   }
 
 
   vtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
   vtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
@@ -1393,7 +1375,7 @@ prepare_shader(Shader *shader) {
   alloc_info.pSetLayouts = &sc->_descriptor_set_layouts[1];
   alloc_info.pSetLayouts = &sc->_descriptor_set_layouts[1];
   err = vkAllocateDescriptorSets(_device, &alloc_info, &sc->_uniform_descriptor_set);
   err = vkAllocateDescriptorSets(_device, &alloc_info, &sc->_uniform_descriptor_set);
   if (err) {
   if (err) {
-    vulkan_error(err, "Failed to allocate descriptor set");
+    vulkan_error(err, "Failed to allocate descriptor set for dynamic uniforms");
     delete sc;
     delete sc;
     return nullptr;
     return nullptr;
   }
   }
@@ -1756,35 +1738,26 @@ set_state_and_transform(const RenderState *state,
     vkCmdPushConstants(_cmd, sc->_pipeline_layout, sc->_push_constant_stage_mask, 64, 16, color.get_data());
     vkCmdPushConstants(_cmd, sc->_pipeline_layout, sc->_push_constant_stage_mask, 64, 16, color.get_data());
   }
   }
 
 
-  //TODO: properly compute altered field.
-  sc->update_uniform_buffers(this, _cmd, ~0);
+  // Update and bind descriptor sets.
+  VkDescriptorSet descriptor_sets[DS_SET_COUNT] = {};
 
 
-  const TextureAttrib *tex_attrib;
-  state->get_attrib_def(tex_attrib);
-
-  Texture *texture;
-  if (tex_attrib->has_on_stage(TextureStage::get_default())) {
-    texture = tex_attrib->get_on_texture(TextureStage::get_default());
-  } else {
-    texture = _white_texture;
+  if (get_attrib_descriptor_set(
+        descriptor_sets[DS_texture_attrib],
+        sc->_descriptor_set_layouts[DS_texture_attrib],
+        state->get_attrib_def(TextureAttrib::get_class_slot()))) {
+    // 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.
+    sc->update_descriptor_set(this, descriptor_sets[DS_texture_attrib]);
   }
   }
 
 
-  VulkanTextureContext *tc;
-  DCAST_INTO_V(tc, texture->prepare_now(0, get_prepared_objects(), this));
-  tc->set_active(true);
-  update_texture(tc, true);
+  descriptor_sets[DS_dynamic_uniforms] = sc->_uniform_descriptor_set;
 
 
-  // Transition the texture so that it can be read by the shader.  This has
-  // to happen on the transfer command buffer, since it can't happen during
-  // an active render pass.
-  tc->transition(_transfer_cmd, _graphics_queue_family_index,
-                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
-                 VK_ACCESS_SHADER_READ_BIT);
+  //TODO: properly compute altered field.
+  sc->update_uniform_buffers(this, ~0);
 
 
-  VkDescriptorSet ds = get_descriptor_set(state);
   vkCmdBindDescriptorSets(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
   vkCmdBindDescriptorSets(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
-                          sc->_pipeline_layout, 0, 1, &ds, 0, nullptr);
+                          sc->_pipeline_layout, 0, DS_SET_COUNT, descriptor_sets,
+                          sc->_num_uniform_offsets, sc->_uniform_offsets);
 }
 }
 
 
 /**
 /**
@@ -1962,6 +1935,10 @@ begin_frame(Thread *current_thread) {
     }
     }
   }
   }
 
 
+  // Increase the frame counter, which we use to determine whether we've
+  // updated any resources in this frame.
+  ++_frame_counter;
+
   // Now we can return any memory that was pending deletion.
   // Now we can return any memory that was pending deletion.
   for (VkBuffer buffer : _pending_delete_buffers) {
   for (VkBuffer buffer : _pending_delete_buffers) {
     vkDestroyBuffer(_device, buffer, nullptr);
     vkDestroyBuffer(_device, buffer, nullptr);
@@ -1969,6 +1946,15 @@ begin_frame(Thread *current_thread) {
   _pending_delete_buffers.clear();
   _pending_delete_buffers.clear();
   _pending_free.clear();
   _pending_free.clear();
 
 
+  if (!_pending_delete_descriptor_sets.empty()) {
+    if (!vkFreeDescriptorSets(_device, _descriptor_pool,
+                              _pending_delete_descriptor_sets.size(),
+                              &_pending_delete_descriptor_sets[0])) {
+      vulkan_error(err, "Failed to free descriptor sets");
+    }
+    _pending_delete_descriptor_sets.clear();
+  }
+
   // Recycle the global uniform buffer.  It's probably not worth it to expect
   // Recycle the global uniform buffer.  It's probably not worth it to expect
   // values to stick around between frames, because the vast majority of global
   // values to stick around between frames, because the vast majority of global
   // uniforms will change more frequently than that anyway.
   // uniforms will change more frequently than that anyway.
@@ -3177,94 +3163,60 @@ make_compute_pipeline(VulkanShaderContext *sc) {
 
 
 /**
 /**
  * Returns a VkDescriptorSet for the resources of the given render state.
  * Returns a VkDescriptorSet for the resources of the given render state.
+ * Returns true if this was the first use of this set in this frame, false
+ * otherwise.
  */
  */
-VkDescriptorSet VulkanGraphicsStateGuardian::
-get_descriptor_set(const RenderState *state) {
-  DescriptorSetKey key;
-  key._tex_attrib =
-    (const TextureAttrib *)state->get_attrib_def(TextureAttrib::get_class_slot());
-  key._shader_attrib =
-    (const ShaderAttrib *)state->get_attrib_def(ShaderAttrib::get_class_slot());
-
-  DescriptorSetMap::const_iterator it;
-  it = _descriptor_set_map.find(key);
-  if (it == _descriptor_set_map.end()) {
-    VkDescriptorSet ds = make_descriptor_set(state);
-    _descriptor_set_map[std::move(key)] = ds;
-    return ds;
-  } else {
-    return it->second;
-  }
-}
+bool VulkanGraphicsStateGuardian::
+get_attrib_descriptor_set(VkDescriptorSet &out, VkDescriptorSetLayout layout, const RenderAttrib *attrib) {
+  // Look it up in the attribute map.
+  auto it = _attrib_descriptor_set_map.find(attrib);
+  if (it != _attrib_descriptor_set_map.end()) {
+    // Found something.  Check that it's not just a different state that has
+    // been allocated in the memory of a previous state.
+    DescriptorSet &set = it->second;
+    if (!set._weak_ref->was_deleted()) {
+      // Nope, it's not deleted, which must mean it's the same one, which must
+      // mean we have a live pointer to it (so no need to lock anything).
+      out = set._handle;
+
+      bool is_current = _frame_counter == set._last_update_frame;
+      set._last_update_frame = _frame_counter;
+      return !is_current;
+    }
 
 
-/**
- * Creates a VkDescriptorSet for the resources of the given render state.
- */
-VkDescriptorSet VulkanGraphicsStateGuardian::
-make_descriptor_set(const RenderState *state) {
-  if (vulkandisplay_cat.is_debug()) {
-    vulkandisplay_cat.debug()
-      << "Making descriptor set for state " << *state << "\n";
+
+    // It's been deleted, which means it's for a very different state.  We can
+    // let go of this one and create a new one instead.
+    if (!set._weak_ref->unref()) {
+      delete set._weak_ref;
+    }
+
+    _pending_delete_descriptor_sets.push_back(set._handle);
+    set._handle = VK_NULL_HANDLE;
   }
   }
 
 
-  VkResult err;
-  VkDescriptorSet ds;
+  DescriptorSet set;
 
 
-  //TODO: layout creation.
   VkDescriptorSetAllocateInfo alloc_info;
   VkDescriptorSetAllocateInfo alloc_info;
   alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
   alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
   alloc_info.pNext = nullptr;
   alloc_info.pNext = nullptr;
   alloc_info.descriptorPool = _descriptor_pool;
   alloc_info.descriptorPool = _descriptor_pool;
   alloc_info.descriptorSetCount = 1;
   alloc_info.descriptorSetCount = 1;
-  alloc_info.pSetLayouts = &_descriptor_set_layout;
-  err = vkAllocateDescriptorSets(_device, &alloc_info, &ds);
+  alloc_info.pSetLayouts = &layout;
+  VkResult err = vkAllocateDescriptorSets(_device, &alloc_info, &set._handle);
   if (err) {
   if (err) {
-    vulkan_error(err, "Failed to allocate descriptor set");
-    return VK_NULL_HANDLE;
+    vulkan_error(err, "Failed to allocate descriptor set for attribute");
+    return false;
   }
   }
 
 
-  // Now fill in the descriptor set with bindings.
-  const TextureAttrib *tex_attrib;
-  state->get_attrib_def(tex_attrib);
-
-  Texture *texture;
-  SamplerState sampler;
+  // Keep a weak reference, so we'll know if it's been deleted.
+  set._weak_ref = ((RenderAttrib *)attrib)->weak_ref();
 
 
-  if (tex_attrib->has_on_stage(TextureStage::get_default())) {
-    texture = tex_attrib->get_on_texture(TextureStage::get_default());
-    sampler = tex_attrib->get_on_sampler(TextureStage::get_default());
-    nassertr(texture != nullptr, VK_NULL_HANDLE);
-  } else {
-    // Just use a white texture.
-    texture = _white_texture;
-  }
+  set._last_update_frame = _frame_counter;
 
 
-  VulkanTextureContext *tc;
-  DCAST_INTO_R(tc, texture->prepare_now(0, get_prepared_objects(), this), VK_NULL_HANDLE);
-
-  VulkanSamplerContext *sc;
-  DCAST_INTO_R(sc, sampler.prepare_now(get_prepared_objects(), this), VK_NULL_HANDLE);
-
-  VkDescriptorImageInfo image_info;
-  image_info.sampler = sc->_sampler;
-  image_info.imageView = tc->_image_view;
-  image_info.imageLayout = tc->_layout;
-
-  VkWriteDescriptorSet write;
-  write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-  write.pNext = nullptr;
-  write.dstSet = ds;
-  write.dstBinding = 0;
-  write.dstArrayElement = 0;
-  write.descriptorCount = 1;
-  write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-  write.pImageInfo = &image_info;
-  write.pBufferInfo = nullptr;
-  write.pTexelBufferView = nullptr;
-
-  vkUpdateDescriptorSets(_device, 1, &write, 0, nullptr);
-
-  return ds;
+  _attrib_descriptor_set_map[attrib] = std::move(set);
+  out = set._handle;
+  return true;
 }
 }
 
 
 /**
 /**

+ 28 - 20
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.h

@@ -145,8 +145,18 @@ public:
                            VkPrimitiveTopology topology);
                            VkPrimitiveTopology topology);
   VkPipeline make_compute_pipeline(VulkanShaderContext *sc);
   VkPipeline make_compute_pipeline(VulkanShaderContext *sc);
 
 
-  VkDescriptorSet get_descriptor_set(const RenderState *state);
-  VkDescriptorSet make_descriptor_set(const RenderState *state);
+  // Built-in descriptor set indices, ordered by frequency.  Static descriptor
+  // sets come first.  We separate descriptor sets by the RenderAttrib that
+  // contains those inputs, so we can swap out only the set that has changed.
+  enum DescriptorSetIndex {
+    DS_texture_attrib = 0,
+    DS_ATTRIB_COUNT = 1,
+    DS_dynamic_uniforms = 1,
+    DS_SET_COUNT = 2,
+  };
+
+  bool get_attrib_descriptor_set(VkDescriptorSet &out, VkDescriptorSetLayout layout,
+                                 const RenderAttrib *attrib);
 
 
   VkDeviceSize update_dynamic_uniform_buffer(void *data, VkDeviceSize size);
   VkDeviceSize update_dynamic_uniform_buffer(void *data, VkDeviceSize size);
   uint32_t get_color_palette_offset(const LColor &color);
   uint32_t get_color_palette_offset(const LColor &color);
@@ -157,25 +167,25 @@ public:
 
 
 public:
 public:
   VkDevice _device;
   VkDevice _device;
+  VkCommandBuffer _cmd;
+  VkCommandBuffer _transfer_cmd;
+  uint32_t _graphics_queue_family_index;
+  PT(Texture) _white_texture;
 
 
 private:
 private:
+  uint64_t _frame_counter = 0;
   VkQueue _queue;
   VkQueue _queue;
   VkQueue _dma_queue;
   VkQueue _dma_queue;
-  uint32_t _graphics_queue_family_index;
   VkSampleCountFlagBits _multisample_count;
   VkSampleCountFlagBits _multisample_count;
   VkFence _fence;
   VkFence _fence;
   VkCommandPool _cmd_pool;
   VkCommandPool _cmd_pool;
-  VkCommandBuffer _cmd;
-  VkCommandBuffer _transfer_cmd;
   pvector<VkRect2D> _viewports;
   pvector<VkRect2D> _viewports;
   VkPipelineCache _pipeline_cache;
   VkPipelineCache _pipeline_cache;
-  VkDescriptorSetLayout _descriptor_set_layout;
   VkDescriptorPool _descriptor_pool;
   VkDescriptorPool _descriptor_pool;
   VulkanShaderContext *_default_sc;
   VulkanShaderContext *_default_sc;
   VulkanShaderContext *_current_shader;
   VulkanShaderContext *_current_shader;
   const ShaderType::Struct *_push_constant_block_type = nullptr;
   const ShaderType::Struct *_push_constant_block_type = nullptr;
   CPT(GeomVertexFormat) _format;
   CPT(GeomVertexFormat) _format;
-  PT(Texture) _white_texture;
 
 
   // Single large uniform buffer used for everything in a frame.
   // Single large uniform buffer used for everything in a frame.
   VkBuffer _uniform_buffer;
   VkBuffer _uniform_buffer;
@@ -202,21 +212,18 @@ private:
   typedef pmap<LColorf, uint32_t> ColorPaletteIndices;
   typedef pmap<LColorf, uint32_t> ColorPaletteIndices;
   ColorPaletteIndices _color_palette;
   ColorPaletteIndices _color_palette;
 
 
-  /**
-   * Stores whatever is used to key a cached descriptor set into the
-   * descriptor set map.
-   */
-  struct DescriptorSetKey {
-    INLINE bool operator ==(const DescriptorSetKey &other) const;
-    INLINE bool operator < (const DescriptorSetKey &other) const;
-
-    CPT(TextureAttrib) _tex_attrib;
-    CPT(ShaderAttrib) _shader_attrib;
+  // Keep track of a created descriptor set and the last frame in which it was
+  // bound (since we can only update it once per frame).
+  struct DescriptorSet {
+    VkDescriptorSet _handle = VK_NULL_HANDLE;
+    uint64_t _last_update_frame = 0;
+    WeakReferenceList *_weak_ref = nullptr;
   };
   };
 
 
-  //TODO: we need a garbage collection mechanism for this.
-  typedef pmap<DescriptorSetKey, VkDescriptorSet> DescriptorSetMap;
-  DescriptorSetMap _descriptor_set_map;
+  // We need only one map rather than one per descriptor set since each
+  // different type of RenderAttrib corresponds to a different descriptor set.
+  typedef pmap<const RenderAttrib *, DescriptorSet> AttribDescriptorSetMap;
+  AttribDescriptorSetMap _attrib_descriptor_set_map;
 
 
   // Keep track of all the individual allocations.
   // Keep track of all the individual allocations.
   Mutex _allocator_lock;
   Mutex _allocator_lock;
@@ -226,6 +233,7 @@ private:
   // Keep track of blocks that should be deleted at the next fence.
   // Keep track of blocks that should be deleted at the next fence.
   pvector<VulkanMemoryBlock> _pending_free;
   pvector<VulkanMemoryBlock> _pending_free;
   pvector<VkBuffer> _pending_delete_buffers;
   pvector<VkBuffer> _pending_delete_buffers;
+  pvector<VkDescriptorSet> _pending_delete_descriptor_sets;
 
 
   // Queued buffer-to-RAM transfer.
   // Queued buffer-to-RAM transfer.
   struct QueuedDownload {
   struct QueuedDownload {

+ 110 - 6
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -66,6 +66,7 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     if (struct_type.get_num_members() > 0) {
     if (struct_type.get_num_members() > 0) {
       _mat_block_type = ShaderType::register_type(std::move(struct_type));
       _mat_block_type = ShaderType::register_type(std::move(struct_type));
       _mat_block_size = _mat_block_type->get_size_bytes();
       _mat_block_size = _mat_block_type->get_size_bytes();
+      ++_num_uniform_offsets;
     }
     }
   }
   }
 
 
@@ -85,6 +86,7 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     if (struct_type.get_num_members() > 0) {
     if (struct_type.get_num_members() > 0) {
       _ptr_block_type = ShaderType::register_type(std::move(struct_type));
       _ptr_block_type = ShaderType::register_type(std::move(struct_type));
       _ptr_block_size = _ptr_block_type->get_size_bytes();
       _ptr_block_size = _ptr_block_type->get_size_bytes();
+      ++_num_uniform_offsets;
     }
     }
   }
   }
 
 
@@ -182,7 +184,8 @@ make_pipeline_layout(VkDevice device) {
     set_info.pBindings = ds_bindings[0];
     set_info.pBindings = ds_bindings[0];
 
 
     VkResult
     VkResult
-    err = vkCreateDescriptorSetLayout(device, &set_info, nullptr, &_descriptor_set_layouts[0]);
+    err = vkCreateDescriptorSetLayout(device, &set_info, nullptr,
+      &_descriptor_set_layouts[VulkanGraphicsStateGuardian::DS_texture_attrib]);
     if (err) {
     if (err) {
       vulkan_error(err, "Failed to create descriptor set layout");
       vulkan_error(err, "Failed to create descriptor set layout");
       return false;
       return false;
@@ -217,7 +220,8 @@ make_pipeline_layout(VkDevice device) {
     set_info.pBindings = ds_bindings[1];
     set_info.pBindings = ds_bindings[1];
 
 
     VkResult
     VkResult
-    err = vkCreateDescriptorSetLayout(device, &set_info, nullptr, &_descriptor_set_layouts[1]);
+    err = vkCreateDescriptorSetLayout(device, &set_info, nullptr,
+      &_descriptor_set_layouts[VulkanGraphicsStateGuardian::DS_dynamic_uniforms]);
     if (err) {
     if (err) {
       vulkan_error(err, "Failed to create descriptor set layout");
       vulkan_error(err, "Failed to create descriptor set layout");
       return false;
       return false;
@@ -237,7 +241,7 @@ make_pipeline_layout(VkDevice device) {
   layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
   layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
   layout_info.pNext = nullptr;
   layout_info.pNext = nullptr;
   layout_info.flags = 0;
   layout_info.flags = 0;
-  layout_info.setLayoutCount = 2;
+  layout_info.setLayoutCount = VulkanGraphicsStateGuardian::DS_SET_COUNT;
   layout_info.pSetLayouts = _descriptor_set_layouts;
   layout_info.pSetLayouts = _descriptor_set_layouts;
   layout_info.pushConstantRangeCount = _push_constant_stage_mask ? 1 : 0;
   layout_info.pushConstantRangeCount = _push_constant_stage_mask ? 1 : 0;
   layout_info.pPushConstantRanges = &range;
   layout_info.pPushConstantRanges = &range;
@@ -256,7 +260,7 @@ make_pipeline_layout(VkDevice device) {
  * Updates the ShaderMatSpec uniforms.
  * Updates the ShaderMatSpec uniforms.
  */
  */
 void VulkanShaderContext::
 void VulkanShaderContext::
-update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, VkCommandBuffer cmd, int altered) {
+update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, int altered) {
   if (_mat_block_size == 0 && _ptr_block_size == 0) {
   if (_mat_block_size == 0 && _ptr_block_size == 0) {
     return;
     return;
   }
   }
@@ -333,9 +337,109 @@ update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, VkCommandBuffer cmd, in
 
 
   //TODO: ptr inputs
   //TODO: ptr inputs
   _uniform_offsets[count] = 0;
   _uniform_offsets[count] = 0;
+}
+
+/**
+ * Updates a VkDescriptorSet with the resources of the current render state.
+ */
+bool VulkanShaderContext::
+update_descriptor_set(VulkanGraphicsStateGuardian *gsg, VkDescriptorSet ds) {
+  PreparedGraphicsObjects *pgo = gsg->get_prepared_objects();
+
+  size_t num_textures = _shader->_tex_spec.size();
+  VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)alloca(num_textures * sizeof(VkWriteDescriptorSet));
+  VkDescriptorImageInfo *image_infos = (VkDescriptorImageInfo *)alloca(num_textures * sizeof(VkDescriptorImageInfo));
+
+  for (size_t i = 0; i < num_textures; ++i) {
+    Shader::ShaderTexSpec &spec = _shader->_tex_spec[i];
+
+    SamplerState sampler;
+    int view = gsg->get_current_tex_view_offset();
+
+    PT(Texture) tex = gsg->fetch_specified_texture(spec, sampler, view);
+    if (tex.is_null()) {
+      tex = gsg->_white_texture;
+    }
+
+    if (tex->get_texture_type() != spec._desired_type) {
+      switch (spec._part) {
+      case Shader::STO_named_input:
+        vulkandisplay_cat.error()
+          << "Sampler type of shader input '" << *spec._name << "' does not "
+             "match type of texture " << *tex << ".\n";
+        break;
+
+      case Shader::STO_stage_i:
+        vulkandisplay_cat.error()
+          << "Sampler type of shader input p3d_Texture" << spec._stage
+          << " does not match type of texture " << *tex << ".\n";
+        break;
+
+      case Shader::STO_light_i_shadow_map:
+        vulkandisplay_cat.error()
+          << "Sampler type of shader input p3d_LightSource[" << spec._stage
+          << "].shadowMap does not match type of texture " << *tex << ".\n";
+        break;
+      }
+      // TODO: also check whether shadow sampler textures have shadow filter
+      // enabled.
+    }
+
+    VulkanTextureContext *tc;
+    DCAST_INTO_R(tc, tex->prepare_now(view, pgo, gsg), false);
 
 
-  vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline_layout,
-                          1, 1, &_uniform_descriptor_set, count, _uniform_offsets);
+    VulkanSamplerContext *sampc;
+    DCAST_INTO_R(sampc, sampler.prepare_now(pgo, gsg), false);
+
+    tc->set_active(true);
+    gsg->update_texture(tc, true);
+
+    // Transition the texture so that it can be read by the shader.  This has
+    // to happen on the transfer command buffer, since it can't happen during
+    // an active render pass.
+    VkPipelineStageFlags stage_flags = 0;
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::vertex)) {
+      stage_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+    }
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::tess_control)) {
+      stage_flags |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
+    }
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::tess_evaluation)) {
+      stage_flags |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
+    }
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::fragment)) {
+      stage_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+    }
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::geometry)) {
+      stage_flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
+    }
+    if (spec._id._stage_mask & (1 << (int)Shader::Stage::compute)) {
+      stage_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+    }
+    tc->transition(gsg->_transfer_cmd, gsg->_graphics_queue_family_index,
+                   VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+                   stage_flags, VK_ACCESS_SHADER_READ_BIT);
+
+    VkDescriptorImageInfo &image_info = image_infos[i];
+    image_info.sampler = sampc->_sampler;
+    image_info.imageView = tc->_image_view;
+    image_info.imageLayout = tc->_layout;
+
+    VkWriteDescriptorSet &write = writes[i];
+    write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    write.pNext = nullptr;
+    write.dstSet = ds;
+    write.dstBinding = 0;
+    write.dstArrayElement = 0;
+    write.descriptorCount = 1;
+    write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    write.pImageInfo = &image_info;
+    write.pBufferInfo = nullptr;
+    write.pTexelBufferView = nullptr;
+  }
+
+  vkUpdateDescriptorSets(gsg->_device, num_textures, writes, 0, nullptr);
+  return true;
 }
 }
 
 
 /**
 /**

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

@@ -30,7 +30,8 @@ public:
   bool create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_type);
   bool create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_type);
   bool make_pipeline_layout(VkDevice device);
   bool make_pipeline_layout(VkDevice device);
 
 
-  void update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, VkCommandBuffer cmd, int altered);
+  void update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, int altered);
+  bool update_descriptor_set(VulkanGraphicsStateGuardian *gsg, VkDescriptorSet ds);
 
 
   VkPipeline get_pipeline(VulkanGraphicsStateGuardian *gsg,
   VkPipeline get_pipeline(VulkanGraphicsStateGuardian *gsg,
                           const RenderState *state,
                           const RenderState *state,
@@ -40,7 +41,7 @@ public:
 
 
 private:
 private:
   VkShaderModule _modules[(size_t)Shader::Stage::compute + 1];
   VkShaderModule _modules[(size_t)Shader::Stage::compute + 1];
-  VkDescriptorSetLayout _descriptor_set_layouts[2];
+  VkDescriptorSetLayout _descriptor_set_layouts[VulkanGraphicsStateGuardian::DS_SET_COUNT];
   VkPipelineLayout _pipeline_layout;
   VkPipelineLayout _pipeline_layout;
 
 
   // Describe the two UBOs and push constant range we create.
   // Describe the two UBOs and push constant range we create.
@@ -56,6 +57,7 @@ private:
 
 
   VkDescriptorSet _uniform_descriptor_set;
   VkDescriptorSet _uniform_descriptor_set;
   uint32_t _uniform_offsets[2];
   uint32_t _uniform_offsets[2];
+  uint32_t _num_uniform_offsets = 0;
 
 
   // These are for the push constants; maybe in the future we'll replace this
   // These are for the push constants; maybe in the future we'll replace this
   // with a more generic and flexible system.
   // with a more generic and flexible system.