Browse Source

vulkan: Use non-dynamic UBO for ShaderPtrSpec uniform handling

By putting them in the ShaderAttrib descriptor set, we don't need to use dynamic UBOs
rdb 5 years ago
parent
commit
c4dfe4036e

+ 41 - 12
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1490,14 +1490,8 @@ prepare_shader(Shader *shader) {
       buffer_info[count].range = sc->_mat_block_size;
       ++count;
     }
-    if (sc->_ptr_block_size > 0) {
-      buffer_info[count].buffer = _uniform_buffer;
-      buffer_info[count].offset = 0;
-      buffer_info[count].range = sc->_ptr_block_size;
-      ++count;
-    }
 
-    VkWriteDescriptorSet write[2];
+    VkWriteDescriptorSet write[1];
     for (size_t i = 0; i < count; ++i) {
       write[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
       write[i].pNext = nullptr;
@@ -1870,11 +1864,16 @@ set_state_and_transform(const RenderState *state,
   descriptor_sets[DS_dynamic_uniforms] = sc->_uniform_descriptor_set;
 
   //TODO: properly compute altered field.
-  sc->update_uniform_buffers(this, ~0);
+  uint32_t num_offsets = 0;
+  uint32_t offset = 0;
+  if (sc->_mat_block_size > 0) {
+    offset = sc->update_dynamic_uniforms(this, ~0);
+    num_offsets = 1;
+  }
 
   vkCmdBindDescriptorSets(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
                           sc->_pipeline_layout, 0, DS_SET_COUNT, descriptor_sets,
-                          sc->_num_uniform_offsets, sc->_uniform_offsets);
+                          num_offsets, &offset);
 }
 
 /**
@@ -3518,12 +3517,38 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
     return true;
   }
 
+  VulkanShaderContext *sc;
+  DCAST_INTO_R(sc, shader->prepare_now(get_prepared_objects(), this), false);
+
   // Allocate enough memory.
-  size_t max_num_textures = shader->_tex_spec.size();
-  VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)alloca(max_num_textures * sizeof(VkWriteDescriptorSet));
-  VkDescriptorImageInfo *image_infos = (VkDescriptorImageInfo *)alloca(max_num_textures * sizeof(VkDescriptorImageInfo));
+  size_t max_num_descriptors = 1 + shader->_tex_spec.size();
+  VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)alloca(max_num_descriptors * sizeof(VkWriteDescriptorSet));
+  VkDescriptorImageInfo *image_infos = (VkDescriptorImageInfo *)alloca(max_num_descriptors * sizeof(VkDescriptorImageInfo));
 
+  // First the UBO, then the shader input textures.
   size_t i = 0;
+
+  VkDescriptorBufferInfo buffer_info;
+  if (sc->_ptr_block_size > 0) {
+    buffer_info.buffer = _uniform_buffer;
+    buffer_info.offset = sc->update_sattr_uniforms(this);
+    buffer_info.range = sc->_ptr_block_size;
+
+    VkWriteDescriptorSet &write = writes[i];
+    write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    write.pNext = nullptr;
+    write.dstSet = ds;
+    write.dstBinding = i;
+    write.dstArrayElement = 0;
+    write.descriptorCount = 1;
+    write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    write.pImageInfo = nullptr;
+    write.pBufferInfo = &buffer_info;
+    write.pTexelBufferView = nullptr;
+
+    ++i;
+  }
+
   for (Shader::ShaderTexSpec &spec : shader->_tex_spec) {
     if (spec._part != Shader::STO_named_input || spec._id._location < 0) {
       continue;
@@ -3608,6 +3633,10 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
  */
 VkDeviceSize VulkanGraphicsStateGuardian::
 update_dynamic_uniform_buffer(void *data, VkDeviceSize size) {
+  if (size == 0) {
+    return 0;
+  }
+
   VkDeviceSize offset = _uniform_buffer_offset;
   VkDeviceSize align = _uniform_buffer_offset_alignment;
   offset = offset - 1 - (offset - 1) % align + align;

+ 151 - 140
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -62,7 +62,6 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     if (struct_type.get_num_members() > 0) {
       _mat_block_type = ShaderType::register_type(std::move(struct_type));
       _mat_block_size = _mat_block_type->get_size_bytes();
-      ++_num_uniform_offsets;
     }
   }
 
@@ -82,13 +81,15 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     if (struct_type.get_num_members() > 0) {
       _ptr_block_type = ShaderType::register_type(std::move(struct_type));
       _ptr_block_size = _ptr_block_type->get_size_bytes();
-      ++_num_uniform_offsets;
     }
   }
 
   // Compose descriptor sets for all the texture inputs.
   vector_int tex_stage_set_locations;
   vector_int tex_input_set_locations;
+  if (_ptr_block_size > 0) {
+    tex_input_set_locations.push_back(-1);
+  }
   for (const Shader::ShaderTexSpec &spec : _shader->_tex_spec) {
     if (spec._id._location >= 0) {
       if (spec._part == Shader::STO_stage_i) {
@@ -117,14 +118,13 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
     ShaderModuleSpirV::InstructionWriter writer(instructions);
 
     // Create UBOs and a push constant block for the uniforms.
-    size_t count = 0;
     if (_mat_block_size > 0) {
       writer.make_block(_mat_block_type, mat_struct_locations, spv::StorageClassUniform,
-                        count++, VulkanGraphicsStateGuardian::DS_dynamic_uniforms);
+                        0, VulkanGraphicsStateGuardian::DS_dynamic_uniforms);
     }
     if (_ptr_block_size > 0) {
       writer.make_block(_ptr_block_type, ptr_struct_locations, spv::StorageClassUniform,
-                        count++, VulkanGraphicsStateGuardian::DS_dynamic_uniforms);
+                        0, VulkanGraphicsStateGuardian::DS_shader_attrib);
     }
     if (push_constant_block_type != nullptr &&
         _push_constant_stage_mask & (1 << (int)module->get_stage())) {
@@ -187,9 +187,22 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
 VkDescriptorSetLayout VulkanShaderContext::
 make_shader_attrib_descriptor_set_layout(VkDevice device) {
   VkDescriptorSetLayoutBinding *bindings;
-  bindings = (VkDescriptorSetLayoutBinding *)alloca(sizeof(VkDescriptorSetLayoutBinding) * _shader->_tex_spec.size());
+  bindings = (VkDescriptorSetLayoutBinding *)alloca(sizeof(VkDescriptorSetLayoutBinding) * (1 + _shader->_tex_spec.size()));
 
   size_t i = 0;
+
+  // First binding is for the UBO.
+  if (_ptr_block_size > 0) {
+    VkDescriptorSetLayoutBinding &binding = bindings[i];
+    binding.binding = i;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = _ptr_block_stage_mask;
+    binding.pImmutableSamplers = nullptr;
+
+    ++i;
+  }
+
   for (const Shader::ShaderTexSpec &spec : _shader->_tex_spec) {
     if (spec._part != Shader::STO_named_input || spec._id._location < 0) {
       continue;
@@ -231,6 +244,8 @@ VkDescriptorSetLayout VulkanShaderContext::
 make_dynamic_uniform_descriptor_set_layout(VkDevice device) {
   VkDescriptorSetLayoutBinding bindings[2];
 
+  // This is a dynamic UBO, which means that we'll be specifying the offsets in
+  // the bind call, rather than when writing the descriptor set.
   size_t count = 0;
   if (_mat_block_size > 0) {
     VkDescriptorSetLayoutBinding &binding = bindings[count];
@@ -240,14 +255,6 @@ make_dynamic_uniform_descriptor_set_layout(VkDevice device) {
     binding.stageFlags = _mat_block_stage_mask;
     binding.pImmutableSamplers = nullptr;
   }
-  if (_ptr_block_size > 0) {
-    VkDescriptorSetLayoutBinding &binding = bindings[count];
-    binding.binding = count++;
-    binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
-    binding.descriptorCount = 1;
-    binding.stageFlags = _ptr_block_stage_mask;
-    binding.pImmutableSamplers = nullptr;
-  }
 
   VkDescriptorSetLayoutCreateInfo set_info;
   set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
@@ -268,15 +275,135 @@ make_dynamic_uniform_descriptor_set_layout(VkDevice device) {
 }
 
 /**
- * Updates the ShaderMatSpec uniforms.
+ * Updates the ShaderPtrSpec uniforms, which change with the ShaderAttrib.
  */
-void VulkanShaderContext::
-update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, int altered) {
-  if (_mat_block_size == 0 && _ptr_block_size == 0) {
-    return;
+uint32_t VulkanShaderContext::
+update_sattr_uniforms(VulkanGraphicsStateGuardian *gsg) {
+  if (_ptr_block_size == 0) {
+    return 0;
+  }
+
+  void *ptr = alloca(_ptr_block_size);
+
+  size_t i = 0;
+  for (Shader::ShaderPtrSpec &spec : _shader->_ptr_spec) {
+    Shader::ShaderPtrData ptr_data;
+    if (!gsg->fetch_ptr_parameter(spec, ptr_data)) {
+      continue;
+    }
+
+    nassertd(spec._dim[1] > 0) continue;
+
+    uint32_t dim = spec._dim[1] * spec._dim[2];
+
+    uint32_t offset = _ptr_block_type->get_member(i++).offset;
+    void *dest = (void *)((char *)ptr + offset);
+
+    int array_size = std::min(spec._dim[0], (uint32_t)(ptr_data._size / dim));
+    switch (spec._type) {
+    case ShaderType::ST_bool:
+    case ShaderType::ST_float:
+      {
+        float *data = (float *)dest;
+
+        switch (ptr_data._type) {
+        case ShaderType::ST_int:
+          // Convert int data to float data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (float)(((int*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        case ShaderType::ST_uint:
+          // Convert unsigned int data to float data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (float)(((unsigned int*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        case ShaderType::ST_double:
+          // Downgrade double data to float data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (float)(((double*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        case ShaderType::ST_float:
+          memcpy(data, ptr_data._ptr, array_size * dim * sizeof(float));
+          break;
+
+        default:
+          nassertd(false) continue;
+        }
+      }
+      break;
+
+    case ShaderType::ST_int:
+    case ShaderType::ST_uint:
+      if (ptr_data._type != ShaderType::ST_int &&
+          ptr_data._type != ShaderType::ST_uint) {
+        vulkandisplay_cat.error()
+          << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
+
+      } else {
+        memcpy(dest, ptr_data._ptr, array_size * dim * sizeof(int));
+        nassertd(false) continue;
+      }
+      break;
+
+    case ShaderType::ST_double:
+      {
+        double *data = (double *)dest;
+
+        switch (ptr_data._type) {
+        case ShaderType::ST_int:
+          // Convert int data to double data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (double)(((int*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        case ShaderType::ST_uint:
+          // Convert unsigned int data to double data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (double)(((unsigned int*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        case ShaderType::ST_double:
+          memcpy(data, ptr_data._ptr, array_size * dim * sizeof(double));
+          break;
+
+        case ShaderType::ST_float:
+          // Upgrade float data to double data.
+          for (int i = 0; i < (array_size * dim); ++i) {
+            data[i] = (double)(((double*)ptr_data._ptr)[i]);
+          }
+          break;
+
+        default:
+          nassertd(false) continue;
+        }
+      }
+      break;
+
+    default:
+      continue;
+    }
+  }
+
+  return gsg->update_dynamic_uniform_buffer(ptr, _ptr_block_size);
+}
+
+/**
+ * Updates the ShaderMatSpec uniforms, if they have changed.
+ */
+uint32_t VulkanShaderContext::
+update_dynamic_uniforms(VulkanGraphicsStateGuardian *gsg, int altered) {
+  if (_mat_block_size == 0) {
+    return 0;
   }
 
-  size_t count = 0;
   if (altered & _mat_deps) {
     gsg->update_shader_matrix_cache(_shader, _mat_part_cache, altered);
 
@@ -399,128 +526,12 @@ update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, int altered) {
       }
     }
 
-    _uniform_offsets[count] = gsg->update_dynamic_uniform_buffer(ptr, _mat_block_size);
-  }
-
-  if (_mat_block_size != 0) {
-    ++count;
+    uint32_t offset = gsg->update_dynamic_uniform_buffer(ptr, _mat_block_size);
+    _dynamic_uniform_offset = offset;
+    return offset;
   }
 
-  // We have no way to track modifications to PTAs, so we assume that they are
-  // modified every frame and when we switch ShaderAttribs.
-  if (_ptr_block_size > 0 &&
-      (altered & (Shader::SSD_shaderinputs | Shader::SSD_frame)) != 0) {
-    void *ptr = alloca(_ptr_block_size);
-
-    size_t i = 0;
-    for (Shader::ShaderPtrSpec &spec : _shader->_ptr_spec) {
-      Shader::ShaderPtrData ptr_data;
-      if (!gsg->fetch_ptr_parameter(spec, ptr_data)) {
-        continue;
-      }
-
-      nassertd(spec._dim[1] > 0) continue;
-
-      uint32_t dim = spec._dim[1] * spec._dim[2];
-
-      uint32_t offset = _ptr_block_type->get_member(i++).offset;
-      void *dest = (void *)((char *)ptr + offset);
-
-      int array_size = std::min(spec._dim[0], (uint32_t)(ptr_data._size / dim));
-      switch (spec._type) {
-      case ShaderType::ST_bool:
-      case ShaderType::ST_float:
-        {
-          float *data = (float *)dest;
-
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-            // Convert int data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_uint:
-            // Convert unsigned int data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((unsigned int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_double:
-            // Downgrade double data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((double*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_float:
-            memcpy(data, ptr_data._ptr, array_size * dim * sizeof(float));
-            break;
-
-          default:
-            nassertd(false) continue;
-          }
-        }
-        break;
-
-      case ShaderType::ST_int:
-      case ShaderType::ST_uint:
-        if (ptr_data._type != ShaderType::ST_int &&
-            ptr_data._type != ShaderType::ST_uint) {
-          vulkandisplay_cat.error()
-            << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-        } else {
-          memcpy(dest, ptr_data._ptr, array_size * dim * sizeof(int));
-          nassertd(false) continue;
-        }
-        break;
-
-      case ShaderType::ST_double:
-        {
-          double *data = (double *)dest;
-
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-            // Convert int data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_uint:
-            // Convert unsigned int data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((unsigned int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_double:
-            memcpy(data, ptr_data._ptr, array_size * dim * sizeof(double));
-            break;
-
-          case ShaderType::ST_float:
-            // Upgrade float data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((double*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          default:
-            nassertd(false) continue;
-          }
-        }
-        break;
-
-      default:
-        continue;
-      }
-    }
-
-    _uniform_offsets[count] = gsg->update_dynamic_uniform_buffer(ptr, _mat_block_size);
-  }
+  return _dynamic_uniform_offset;
 }
 
 /**

+ 3 - 3
panda/src/vulkandisplay/vulkanShaderContext.h

@@ -32,7 +32,8 @@ public:
   VkDescriptorSetLayout make_shader_attrib_descriptor_set_layout(VkDevice device);
   VkDescriptorSetLayout make_dynamic_uniform_descriptor_set_layout(VkDevice device);
 
-  void update_uniform_buffers(VulkanGraphicsStateGuardian *gsg, int altered);
+  uint32_t update_sattr_uniforms(VulkanGraphicsStateGuardian *gsg);
+  uint32_t update_dynamic_uniforms(VulkanGraphicsStateGuardian *gsg, int altered);
 
   VkPipeline get_pipeline(VulkanGraphicsStateGuardian *gsg,
                           const RenderState *state,
@@ -58,8 +59,7 @@ private:
   pvector<Shader::ShaderMatSpec> _mat_spec;
 
   VkDescriptorSet _uniform_descriptor_set;
-  uint32_t _uniform_offsets[2];
-  uint32_t _num_uniform_offsets = 0;
+  uint32_t _dynamic_uniform_offset = 0;
 
   // These are for the push constants; maybe in the future we'll replace this
   // with a more generic and flexible system.