Browse Source

vulkan: Support vertex attrib divisors, prefer zero divisor over zero stride

Portability subset restricts zero stride for MoltenVK, but it does support VK_EXT_vertex_attribute_divisor with vertexAttributeInstanceRateZeroDivisor so we can just use that to get the effect of MTLStepFunctionConstant for constant vertex data
rdb 2 years ago
parent
commit
3b87ff6746

+ 14 - 1
panda/src/vulkandisplay/vulkanGraphicsPipe.cxx

@@ -326,15 +326,28 @@ VulkanGraphicsPipe() : _max_allocation_size(0) {
       features2.pNext = &ro2_features;
     }
 
+    VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT div_features = {
+      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT,
+      features2.pNext,
+    };
+    if (has_device_extension(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
+      features2.pNext = &div_features;
+    }
+
     pVkGetPhysicalDeviceFeatures2(_gpu, &features2);
     _gpu_features = features2.features;
     _gpu_supports_custom_border_colors = cbc_features.customBorderColors
                                       && cbc_features.customBorderColorWithoutFormat;
     _gpu_supports_null_descriptor = ro2_features.nullDescriptor;
-  } else {
+    _gpu_supports_vertex_attrib_divisor = div_features.vertexAttributeInstanceRateDivisor;
+    _gpu_supports_vertex_attrib_zero_divisor = div_features.vertexAttributeInstanceRateZeroDivisor;
+  }
+  else {
     vkGetPhysicalDeviceFeatures(_gpu, &_gpu_features);
     _gpu_supports_custom_border_colors = false;
     _gpu_supports_null_descriptor = false;
+    _gpu_supports_vertex_attrib_divisor = false;
+    _gpu_supports_vertex_attrib_zero_divisor = false;
   }
 
   // Default the maximum allocation size to the largest of the heaps.

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

@@ -76,6 +76,8 @@ public:
   VkPhysicalDeviceProperties _gpu_properties;
   bool _gpu_supports_custom_border_colors = false;
   bool _gpu_supports_null_descriptor = false;
+  bool _gpu_supports_vertex_attrib_divisor = false;
+  bool _gpu_supports_vertex_attrib_zero_divisor = false;
   VkPhysicalDeviceMemoryProperties _memory_properties;
   pvector<VkQueueFamilyProperties> _queue_families;
   VkDeviceSize _max_allocation_size;

+ 69 - 5
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -124,6 +124,22 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     supports_null_descriptor = true;
   }
 
+  // VK_EXT_vertex_attribute_divisor
+  VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT div_features = {
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT,
+    enabled_features.pNext,
+  };
+  if (pipe->_gpu_supports_vertex_attrib_divisor) {
+    div_features.vertexAttributeInstanceRateDivisor = VK_TRUE;
+    div_features.vertexAttributeInstanceRateZeroDivisor = pipe->_gpu_supports_vertex_attrib_zero_divisor;
+    enabled_features.pNext = &div_features;
+
+    extensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
+    _supports_vertex_attrib_divisor = true;
+    _supports_vertex_attrib_zero_divisor = pipe->_gpu_supports_vertex_attrib_zero_divisor;
+  }
+
+  // VK_KHR_portability_subset
   VkPhysicalDevicePortabilitySubsetFeaturesKHR portability_features = {
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
     enabled_features.pNext,
@@ -3394,11 +3410,28 @@ make_pipeline(VulkanShaderContext *sc,
   VkVertexInputBindingDescription *binding_desc = (VkVertexInputBindingDescription *)
     alloca(sizeof(VkVertexInputBindingDescription) * (num_bindings + 2));
 
+  VkVertexInputBindingDivisorDescriptionEXT *divisors;
+  int num_divisors = 0;
+  if (_supports_vertex_attrib_divisor) {
+    divisors = (VkVertexInputBindingDivisorDescriptionEXT *)
+      alloca(sizeof(VkVertexInputBindingDivisorDescriptionEXT) * (num_bindings + 2));
+  }
+
   int i = 0;
   for (i = 0; i < num_bindings; ++i) {
+    const GeomVertexArrayFormat *array = key._format->get_array(i);
     binding_desc[i].binding = i;
-    binding_desc[i].stride = key._format->get_array(i)->get_stride();
-    binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+    binding_desc[i].stride = array->get_stride();
+    if (array->get_divisor() == 0) {
+      binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+    } else {
+      binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
+      if (_supports_vertex_attrib_divisor) {
+        divisors[num_divisors].binding = i;
+        divisors[num_divisors].divisor = array->get_divisor();
+        ++num_divisors;
+      }
+    }
   }
 
   // Prepare "dummy" bindings, in case we need it, which are bound to missing
@@ -3407,8 +3440,19 @@ make_pipeline(VulkanShaderContext *sc,
   if (sc->_uses_vertex_color) {
     color_binding = i;
     binding_desc[i].binding = i;
-    binding_desc[i].stride = 0;
-    binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+    if (_supports_vertex_attrib_zero_divisor) {
+      // MoltenVK uses portability subset, which doesn't allow zero stride,
+      // but it does support zero divisor.
+      binding_desc[i].stride = 16;
+      binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
+      divisors[num_divisors].binding = i;
+      divisors[num_divisors].divisor = 0;
+      ++num_divisors;
+    } else {
+      binding_desc[i].stride = 0;
+      binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+    }
     ++i;
     ++num_bindings;
   }
@@ -3416,7 +3460,15 @@ make_pipeline(VulkanShaderContext *sc,
   int null_binding = -1;
   binding_desc[i].binding = i;
   binding_desc[i].stride = 0;
-  binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+  if (_supports_vertex_attrib_zero_divisor) {
+    binding_desc[i].stride = 4;
+    binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
+    divisors[num_divisors].binding = i;
+    divisors[num_divisors].divisor = 0;
+  } else {
+    binding_desc[i].stride = 0;
+    binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+  }
   ++i;
 
   // Now describe each vertex attribute (ie. GeomVertexColumn).
@@ -3451,6 +3503,9 @@ make_pipeline(VulkanShaderContext *sc,
       // containing a fixed value with a stride of 0.
       if (null_binding == -1) {
         null_binding = num_bindings++;
+        if (_supports_vertex_attrib_zero_divisor) {
+          ++num_divisors;
+        }
       }
 
       attrib_desc[i].binding = null_binding;
@@ -3547,6 +3602,15 @@ make_pipeline(VulkanShaderContext *sc,
   vertex_info.vertexAttributeDescriptionCount = i;
   vertex_info.pVertexAttributeDescriptions = attrib_desc;
 
+  VkPipelineVertexInputDivisorStateCreateInfoEXT divisor_info;
+  if (num_divisors > 0) {
+    divisor_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
+    divisor_info.pNext = vertex_info.pNext;
+    divisor_info.vertexBindingDivisorCount = num_divisors;
+    divisor_info.pVertexBindingDivisors = divisors;
+    vertex_info.pNext = &divisor_info;
+  }
+
   VkPipelineInputAssemblyStateCreateInfo assembly_info;
   assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
   assembly_info.pNext = nullptr;

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

@@ -275,6 +275,8 @@ private:
 
   // Feature checks.
   bool _supports_custom_border_colors = false;
+  bool _supports_vertex_attrib_divisor = false;
+  bool _supports_vertex_attrib_zero_divisor = false;
 
   // Function pointers.
   PFN_vkCmdBindIndexBuffer _vkCmdBindIndexBuffer;