Переглянути джерело

vulkan: make pipeline layout and pipeline creation shader-specific

rdb 7 роки тому
батько
коміт
e7c71b8e90

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

@@ -11,71 +11,6 @@
  * @date 2016-02-16
  */
 
-/**
- * Copy constructor for PipelineKey.
- */
-INLINE VulkanGraphicsStateGuardian::PipelineKey::
-PipelineKey(const PipelineKey &copy) :
-  _state(copy._state),
-  _format(copy._format),
-  _topology(copy._topology) {
-}
-
-/**
- * Move constructor for PipelineKey, defined for efficiency (so that we don't
- * have to increase and decrease the reference count when moving this object).
- */
-INLINE VulkanGraphicsStateGuardian::PipelineKey::
-PipelineKey(PipelineKey &&from) noexcept :
-  _state(std::move(from._state)),
-  _format(std::move(from._format)),
-  _topology(from._topology) {
-}
-
-/**
- * Copy assignment operator for PipelineKey.
- */
-INLINE void VulkanGraphicsStateGuardian::PipelineKey::
-operator = (const PipelineKey &copy) {
-  _state = copy._state;
-  _format = copy._format;
-  _topology = copy._topology;
-}
-
-/**
- * Move assignment operator for PipelineKey.
- */
-INLINE void VulkanGraphicsStateGuardian::PipelineKey::
-operator = (PipelineKey &&from) noexcept {
-  _state = std::move(from._state);
-  _format = std::move(from._format);
-  _topology = from._topology;
-}
-
-/**
- * Returns true if these two PipelineKey objects are identical.
- */
-INLINE bool VulkanGraphicsStateGuardian::PipelineKey::
-operator ==(const PipelineKey &other) const {
-  return _state == other._state
-      && _format == other._format
-      && _topology == other._topology;
-}
-
-/**
- * Defines relative sorting order of PipelineKey objects.
- */
-INLINE bool VulkanGraphicsStateGuardian::PipelineKey::
-operator < (const PipelineKey &other) const {
-  if (_state != other._state) {
-    return _state < other._state;
-  }
-  if (_format != other._format) {
-    return _format < other._format;
-  }
-  return _topology < other._topology;
-}
-
 /**
  * Copy constructor for DescriptorSetKey.
  */

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

@@ -49,7 +49,6 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
   _wait_semaphore(VK_NULL_HANDLE),
   _signal_semaphore(VK_NULL_HANDLE),
   _pipeline_cache(VK_NULL_HANDLE),
-  _pipeline_layout(VK_NULL_HANDLE),
   _default_sc(nullptr)
 {
   const char *const layers[] = {
@@ -162,30 +161,6 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     vulkan_error(err, "Failed to create descriptor set layout");
   }
 
-  // Create a pipeline layout.  We'll do that here for now.
-  VkPushConstantRange ranges[2];
-  ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
-  ranges[0].offset = 0;
-  ranges[0].size = 64;
-  ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-  ranges[1].offset = 64;
-  ranges[1].size = 16;
-
-  VkPipelineLayoutCreateInfo layout_info;
-  layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
-  layout_info.pNext = nullptr;
-  layout_info.flags = 0;
-  layout_info.setLayoutCount = 1;
-  layout_info.pSetLayouts = &_descriptor_set_layout;
-  layout_info.pushConstantRangeCount = 2;
-  layout_info.pPushConstantRanges = ranges;
-
-  err = vkCreatePipelineLayout(_device, &layout_info, nullptr, &_pipeline_layout);
-  if (err) {
-    vulkan_error(err, "Failed to create pipeline layout");
-    return;
-  }
-
   VkDescriptorPoolSize pool_size;
   pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
   pool_size.descriptorCount = 64;
@@ -223,6 +198,17 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
   _color_palette[LColorf(1, 1, 1, 1)] = 1;
   _next_palette_index = 2;
 
+  // Load the default shader.  Temporary hack.
+  static PT(Shader) default_shader;
+  if (default_shader.is_null()) {
+    default_shader = Shader::load(Shader::SL_SPIR_V, "vert.spv", "frag.spv");
+    nassertv(default_shader);
+
+    ShaderContext *sc = default_shader->prepare_now(get_prepared_objects(), this);
+    nassertv(sc);
+    _default_sc = DCAST(VulkanShaderContext, sc);
+  }
+
   // Fill in the features supported by this physical device.
   const VkPhysicalDeviceLimits &limits = pipe->_gpu_properties.limits;
   const VkPhysicalDeviceFeatures &features = pipe->_gpu_features;
@@ -316,11 +302,6 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
  */
 VulkanGraphicsStateGuardian::
 ~VulkanGraphicsStateGuardian() {
-  // Remove all the pipeline states.
-  for (const auto &item : _pipeline_map) {
-    vkDestroyPipeline(_device, item.second, nullptr);
-  }
-
   // And all the semaphores that were generated on this device.
   for (VkSemaphore semaphore : _semaphores) {
     vkDestroySemaphore(_device, semaphore, nullptr);
@@ -329,7 +310,6 @@ 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);
@@ -1196,6 +1176,11 @@ prepare_shader(Shader *shader) {
     }
   }
 
+  if (!sc->make_pipeline_layout(_device)) {
+    delete sc;
+    return nullptr;
+  }
+
   return sc;
 }
 
@@ -1217,6 +1202,17 @@ release_shader(ShaderContext *context) {
     vkDestroyShaderModule(_device, sc->_modules[1], nullptr);
     sc->_modules[1] = VK_NULL_HANDLE;
   }
+
+  // Destroy the pipeline states that use these modules.
+  //TODO: is this safe?
+  for (const auto &item : sc->_pipeline_map) {
+    vkDestroyPipeline(_device, item.second, nullptr);
+  }
+  sc->_pipeline_map.clear();
+
+  vkDestroyPipelineLayout(_device, sc->_pipeline_layout, nullptr);
+  vkDestroyDescriptorSetLayout(_device, sc->_descriptor_set_layout, nullptr);
+
   delete sc;
 }
 
@@ -1494,15 +1490,18 @@ set_state_and_transform(const RenderState *state,
   // the vertex format.
   _state_rs = state;
 
+  VulkanShaderContext *sc = _default_sc;
+  _current_shader = sc;
+
   // Put the modelview projection matrix in the push constants.
   CPT(TransformState) combined = _projection_mat->compose(trans);
   LMatrix4f matrix = LCAST(float, combined->get_mat());
-  vkCmdPushConstants(_cmd, _pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 64, matrix.get_data());
+  vkCmdPushConstants(_cmd, sc->_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 64, matrix.get_data());
 
   const ColorScaleAttrib *color_scale_attrib;
   state->get_attrib_def(color_scale_attrib);
   LColorf color = LCAST(float, color_scale_attrib->get_scale());
-  vkCmdPushConstants(_cmd, _pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 64, 16, color.get_data());
+  vkCmdPushConstants(_cmd, sc->_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 64, 16, color.get_data());
 
   const TextureAttrib *tex_attrib;
   state->get_attrib_def(tex_attrib);
@@ -1525,7 +1524,7 @@ set_state_and_transform(const RenderState *state,
 
   VkDescriptorSet ds = get_descriptor_set(state);
   vkCmdBindDescriptorSets(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
-                          _pipeline_layout, 0, 1, &ds, 0, nullptr);
+                          sc->_pipeline_layout, 0, 1, &ds, 0, nullptr);
 }
 
 /**
@@ -2205,7 +2204,7 @@ bool VulkanGraphicsStateGuardian::
 do_draw_primitive(const GeomPrimitivePipelineReader *reader, bool force,
                   VkPrimitiveTopology topology) {
 
-  VkPipeline pipeline = get_pipeline(_state_rs, _format, topology);
+  VkPipeline pipeline = _current_shader->get_pipeline(this, _state_rs, _format, topology);
   nassertr(pipeline != VK_NULL_HANDLE, false);
   vkCmdBindPipeline(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
 
@@ -2306,56 +2305,23 @@ create_semaphore() {
   return semaphore;
 }
 
-/**
- * Returns a VkPipeline for the given RenderState+GeomVertexFormat combination.
- */
-VkPipeline VulkanGraphicsStateGuardian::
-get_pipeline(const RenderState *state, const GeomVertexFormat *format,
-             VkPrimitiveTopology topology) {
-  PipelineKey key;
-  key._state = state;
-  key._format = format;
-  key._topology = topology;
-
-  PipelineMap::const_iterator it;
-  it = _pipeline_map.find(key);
-  if (it == _pipeline_map.end()) {
-    VkPipeline pipeline = make_pipeline(state, format, topology);
-    _pipeline_map[std::move(key)] = pipeline;
-    return pipeline;
-  } else {
-    return it->second;
-  }
-}
-
 /**
  * Creates a VkPipeline for the given RenderState+GeomVertexFormat combination.
  */
 VkPipeline VulkanGraphicsStateGuardian::
-make_pipeline(const RenderState *state, const GeomVertexFormat *format,
-              VkPrimitiveTopology topology) {
+make_pipeline(VulkanShaderContext *sc, const RenderState *state,
+              const GeomVertexFormat *format, VkPrimitiveTopology topology) {
   if (vulkandisplay_cat.is_debug()) {
     vulkandisplay_cat.debug()
       << "Making pipeline for state " << *state << " and format " << *format << "\n";
   }
 
-  // Load the default shader.  Temporary hack.
-  static PT(Shader) default_shader;
-  if (default_shader.is_null()) {
-    default_shader = Shader::load(Shader::SL_SPIR_V, "vert.spv", "frag.spv");
-    nassertr(default_shader, VK_NULL_HANDLE);
-
-    ShaderContext *sc = default_shader->prepare_now(get_prepared_objects(), this);
-    nassertr(sc, VK_NULL_HANDLE);
-    _default_sc = DCAST(VulkanShaderContext, sc);
-  }
-
   VkPipelineShaderStageCreateInfo stages[2];
   stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
   stages[0].pNext = nullptr;
   stages[0].flags = 0;
   stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
-  stages[0].module = _default_sc->_modules[0];
+  stages[0].module = sc->_modules[0];
   stages[0].pName = "main";
   stages[0].pSpecializationInfo = nullptr;
 
@@ -2363,7 +2329,7 @@ make_pipeline(const RenderState *state, const GeomVertexFormat *format,
   stages[1].pNext = nullptr;
   stages[1].flags = 0;
   stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
-  stages[1].module = _default_sc->_modules[1];
+  stages[1].module = sc->_modules[1];
   stages[1].pName = "main";
   stages[1].pSpecializationInfo = nullptr;
 
@@ -2749,7 +2715,7 @@ make_pipeline(const RenderState *state, const GeomVertexFormat *format,
   pipeline_info.pDepthStencilState = &ds_info;
   pipeline_info.pColorBlendState = &blend_info;
   pipeline_info.pDynamicState = &dynamic_info;
-  pipeline_info.layout = _pipeline_layout;
+  pipeline_info.layout = sc->_pipeline_layout;
   pipeline_info.renderPass = _render_pass;
   pipeline_info.subpass = 0;
   pipeline_info.basePipelineHandle = VK_NULL_HANDLE;

+ 6 - 29
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.h

@@ -124,35 +124,13 @@ private:
   bool do_draw_primitive(const GeomPrimitivePipelineReader *reader, bool force,
                          VkPrimitiveTopology topology);
 
+public:
   bool create_buffer(VkDeviceSize size, VkBuffer &buffer, VkDeviceMemory &memory,
                      int usage_flags, VkMemoryPropertyFlagBits flags);
 
   VkSemaphore create_semaphore();
 
-  /**
-   * Stores whatever is used to key a cached pipeline into the pipeline map.
-   * This allows us to map Panda states to Vulkan pipelines effectively.
-   */
-  struct PipelineKey {
-    INLINE PipelineKey() = default;
-    INLINE PipelineKey(const PipelineKey &copy);
-    INLINE PipelineKey(PipelineKey &&from) noexcept;
-
-    INLINE void operator = (const PipelineKey &copy);
-    INLINE void operator = (PipelineKey &&from) noexcept;
-
-    INLINE bool operator ==(const PipelineKey &other) const;
-    INLINE bool operator < (const PipelineKey &other) const;
-
-    CPT(RenderState) _state;
-    CPT(GeomVertexFormat) _format;
-    VkPrimitiveTopology _topology;
-  };
-
-  VkPipeline get_pipeline(const RenderState *state,
-                          const GeomVertexFormat *format,
-                          VkPrimitiveTopology topology);
-  VkPipeline make_pipeline(const RenderState *state,
+  VkPipeline make_pipeline(VulkanShaderContext *sc, const RenderState *state,
                            const GeomVertexFormat *format,
                            VkPrimitiveTopology topology);
 
@@ -180,8 +158,10 @@ private:
 
   VkFormat get_image_format(const Texture *texture) const;
 
-private:
+public:
   VkDevice _device;
+
+private:
   VkQueue _queue;
   VkQueue _dma_queue;
   uint32_t _graphics_queue_family_index;
@@ -191,10 +171,10 @@ private:
   VkCommandBuffer _transfer_cmd;
   pvector<VkRect2D> _viewports;
   VkPipelineCache _pipeline_cache;
-  VkPipelineLayout _pipeline_layout;
   VkDescriptorSetLayout _descriptor_set_layout;
   VkDescriptorPool _descriptor_pool;
   VulkanShaderContext *_default_sc;
+  VulkanShaderContext *_current_shader;
   CPT(GeomVertexFormat) _format;
   PT(Texture) _white_texture;
 
@@ -214,9 +194,6 @@ private:
   typedef pmap<LColorf, uint32_t> ColorPaletteIndices;
   ColorPaletteIndices _color_palette;
 
-  typedef pmap<PipelineKey, VkPipeline> PipelineMap;
-  PipelineMap _pipeline_map;
-
   typedef pmap<DescriptorSetKey, VkDescriptorSet> DescriptorSetMap;
   DescriptorSetMap _descriptor_set_map;
 

+ 24 - 0
panda/src/vulkandisplay/vulkanShaderContext.I

@@ -21,3 +21,27 @@ VulkanShaderContext(Shader *shader) : ShaderContext(shader) {
   _modules[0] = VK_NULL_HANDLE;
   _modules[1] = VK_NULL_HANDLE;
 }
+
+/**
+ * Returns true if these two PipelineKey objects are identical.
+ */
+INLINE bool VulkanShaderContext::PipelineKey::
+operator ==(const PipelineKey &other) const {
+  return _state == other._state
+      && _format == other._format
+      && _topology == other._topology;
+}
+
+/**
+ * Defines relative sorting order of PipelineKey objects.
+ */
+INLINE bool VulkanShaderContext::PipelineKey::
+operator < (const PipelineKey &other) const {
+  if (_state != other._state) {
+    return _state < other._state;
+  }
+  if (_format != other._format) {
+    return _format < other._format;
+  }
+  return _topology < other._topology;
+}

+ 83 - 0
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -14,3 +14,86 @@
 #include "vulkanShaderContext.h"
 
 TypeHandle VulkanShaderContext::_type_handle;
+
+/**
+ *
+ */
+bool VulkanShaderContext::
+make_pipeline_layout(VkDevice device) {
+  // Create a descriptor set layout.
+  uint32_t binding_count = (uint32_t)_shader->_tex_spec.size();
+  VkDescriptorSetLayoutBinding *ds_bindings = (VkDescriptorSetLayoutBinding *)
+    alloca(sizeof(VkDescriptorSetLayoutBinding) * binding_count);
+
+  uint32_t index = 0;
+  for (Shader::ShaderTexSpec &spec : _shader->_tex_spec) {
+    VkDescriptorSetLayoutBinding &binding = ds_bindings[index];
+    binding.binding = spec._id._seqno;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+    binding.pImmutableSamplers = nullptr;
+    ++index;
+  }
+
+  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 = binding_count;
+  set_info.pBindings = ds_bindings;
+
+  VkResult
+  err = vkCreateDescriptorSetLayout(device, &set_info, nullptr, &_descriptor_set_layout);
+  if (err) {
+    vulkan_error(err, "Failed to create descriptor set layout");
+    return false;
+  }
+
+  VkPushConstantRange ranges[2];
+  ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+  ranges[0].offset = 0;
+  ranges[0].size = 64;
+  ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+  ranges[1].offset = 64;
+  ranges[1].size = 16;
+
+  VkPipelineLayoutCreateInfo layout_info;
+  layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+  layout_info.pNext = nullptr;
+  layout_info.flags = 0;
+  layout_info.setLayoutCount = 1;
+  layout_info.pSetLayouts = &_descriptor_set_layout;
+  layout_info.pushConstantRangeCount = 2;
+  layout_info.pPushConstantRanges = ranges;
+
+  err = vkCreatePipelineLayout(device, &layout_info, nullptr, &_pipeline_layout);
+  if (err) {
+    vulkan_error(err, "Failed to create pipeline layout");
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * Returns a VkPipeline for the given RenderState+GeomVertexFormat combination.
+ */
+VkPipeline VulkanShaderContext::
+get_pipeline(VulkanGraphicsStateGuardian *gsg, const RenderState *state,
+             const GeomVertexFormat *format, VkPrimitiveTopology topology) {
+  PipelineKey key;
+  key._state = state;
+  key._format = format;
+  key._topology = topology;
+
+  PipelineMap::const_iterator it;
+  it = _pipeline_map.find(key);
+  if (it == _pipeline_map.end()) {
+    VkPipeline pipeline = gsg->make_pipeline(this, state, format, topology);
+    _pipeline_map[std::move(key)] = pipeline;
+    return pipeline;
+  } else {
+    return it->second;
+  }
+}

+ 30 - 1
panda/src/vulkandisplay/vulkanShaderContext.h

@@ -26,8 +26,37 @@ public:
 
   ALLOC_DELETED_CHAIN(VulkanShaderContext);
 
-public:
+  bool make_pipeline_layout(VkDevice device);
+  VkPipeline get_pipeline(VulkanGraphicsStateGuardian *gsg,
+                          const RenderState *state,
+                          const GeomVertexFormat *format,
+                          VkPrimitiveTopology topology);
+
+private:
   VkShaderModule _modules[2];
+  VkDescriptorSetLayout _descriptor_set_layout;
+  VkPipelineLayout _pipeline_layout;
+
+  /**
+   * Stores whatever is used to key a cached pipeline into the pipeline map.
+   * This allows us to map Panda states to Vulkan pipelines effectively.
+   */
+  struct PipelineKey {
+    INLINE bool operator ==(const PipelineKey &other) const;
+    INLINE bool operator < (const PipelineKey &other) const;
+
+    CPT(RenderState) _state;
+    CPT(GeomVertexFormat) _format;
+    VkPrimitiveTopology _topology;
+  };
+
+  // A map of all pipelines that use this shader.  This is in ShaderContext
+  // because when a shader is released we have no more use of the pipelines
+  // which use that shader.
+  typedef pmap<PipelineKey, VkPipeline> PipelineMap;
+  PipelineMap _pipeline_map;
+
+  friend class VulkanGraphicsStateGuardian;
 
 public:
   static TypeHandle get_class_type() {