Просмотр исходного кода

Vulkan: index buffer updates, support uint8 indices

rdb 9 лет назад
Родитель
Сommit
eaad3e2e57

+ 83 - 23
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1134,6 +1134,9 @@ prepare_vertex_buffer(GeomVertexArrayData *array_data) {
   vbc->_buffer = buffer;
   vbc->_memory = memory;
   vbc->update_data_size_bytes(data_size);
+
+  update_vertex_buffer(vbc, handle, false);
+
   return vbc;
 }
 
@@ -1211,6 +1214,18 @@ prepare_index_buffer(GeomPrimitive *primitive) {
   CPT(GeomVertexArrayDataHandle) handle = primitive->get_vertices()->get_handle();
   VkDeviceSize data_size = handle->get_data_size_bytes();
 
+  GeomEnums::NumericType index_type = primitive->get_index_type();
+  if (index_type == GeomEnums::NT_uint8) {
+    // We widen 8-bits indices to 16-bits.
+    data_size *= 2;
+
+  } else if (index_type != GeomEnums::NT_uint16 &&
+             index_type != GeomEnums::NT_uint32) {
+    vulkandisplay_cat.error()
+      << "Unsupported index type: " << index_type;
+    return NULL;
+  }
+
   //TODO: don't use host-visible memory, but copy from a staging buffer.
   VkBuffer buffer;
   VkDeviceMemory memory;
@@ -1227,23 +1242,78 @@ prepare_index_buffer(GeomPrimitive *primitive) {
   ibc->_memory = memory;
   ibc->update_data_size_bytes(data_size);
 
-  void *data;
-  VkResult
-  err = vkMapMemory(_device, memory, 0, data_size, 0, &data);
-  if (err || !data) {
-    vulkan_error(err, "Failed to map index buffer memory");
-    vkDestroyBuffer(_device, buffer, NULL);
-    vkFreeMemory(_device, memory, NULL);
-    return NULL;
+  if (index_type == GeomEnums::NT_uint32) {
+    ibc->_index_type = VK_INDEX_TYPE_UINT32;
+  } else {
+    // NT_uint8 is automatically promoted to uint16 below.
+    ibc->_index_type = VK_INDEX_TYPE_UINT16;
   }
 
-  const unsigned char *source_data = handle->get_read_pointer(true);
-  memcpy(data, source_data, data_size);
+  GeomPrimitivePipelineReader reader(primitive, Thread::get_current_thread());
+  update_index_buffer(ibc, &reader, false);
 
-  vkUnmapMemory(_device, memory);
   return ibc;
 }
 
+/**
+ * Makes sure that the data in the index buffer is up-to-date.
+ */
+bool VulkanGraphicsStateGuardian::
+update_index_buffer(VulkanIndexBufferContext *ibc,
+                    const GeomPrimitivePipelineReader *reader,
+                    bool force) {
+  ibc->set_active(true);
+
+  if (ibc->was_modified(reader)) {
+    VkDeviceSize num_bytes = reader->get_data_size_bytes();
+    if (num_bytes != 0) {
+      const unsigned char *client_pointer = reader->get_read_pointer(force);
+      if (client_pointer == NULL) {
+        return false;
+      }
+
+      GeomEnums::NumericType index_type = reader->get_index_type();
+      if (index_type == GeomEnums::NT_uint8) {
+        // We widen 8-bits indices to 16-bits.
+        num_bytes *= 2;
+
+      } else if (index_type != GeomEnums::NT_uint16 &&
+                 index_type != GeomEnums::NT_uint32) {
+        vulkandisplay_cat.error()
+          << "Unsupported index type: " << index_type;
+        return NULL;
+      }
+
+      void *data;
+      VkResult
+      err = vkMapMemory(_device, ibc->_memory, 0, num_bytes, 0, &data);
+      if (err || !data) {
+        vulkan_error(err, "Failed to map index buffer memory");
+        return false;
+      }
+
+      if (index_type == GeomEnums::NT_uint8) {
+        // Widen to 16-bits, as Vulkan doesn't support 8-bits indices.
+        uint16_t *ptr = (uint16_t *)data;
+        for (size_t i = 0; i < num_bytes; i += 2) {
+          *ptr++ = (uint16_t)*client_pointer++;
+        }
+      } else {
+        memcpy(data, client_pointer, num_bytes);
+      }
+
+      vkUnmapMemory(_device, ibc->_memory);
+
+      _data_transferred_pcollector.add_level(num_bytes);
+    }
+
+    ibc->mark_loaded(reader);
+  }
+  ibc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
+
+  return true;
+}
+
 /**
  * Frees the resources previously allocated via a call to prepare_data(),
  * including deleting the IndexBufferContext itself, if necessary.
@@ -1740,21 +1810,11 @@ do_draw_primitive(const GeomPrimitivePipelineReader *reader, bool force,
     VulkanIndexBufferContext *ibc;
     DCAST_INTO_R(ibc, reader->prepare_now(get_prepared_objects(), this), false);
 
-    VkIndexType index_type;
-    switch (reader->get_index_type()) {
-    case GeomEnums::NT_uint16:
-      index_type = VK_INDEX_TYPE_UINT16;
-      break;
-    case GeomEnums::NT_uint32:
-      index_type = VK_INDEX_TYPE_UINT32;
-      break;
-    default:
-      vulkandisplay_cat.error()
-        << "Unsupported index type: " << reader->get_index_type() << "\n";
+    if (!update_index_buffer(ibc, reader, force)) {
       return false;
     }
 
-    vkCmdBindIndexBuffer(_cmd, ibc->_buffer, 0, index_type);
+    vkCmdBindIndexBuffer(_cmd, ibc->_buffer, 0, ibc->_index_type);
     vkCmdDrawIndexed(_cmd, num_vertices, 1, 0, 0, 0);
   } else {
     // A non-indexed primitive.

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

@@ -16,6 +16,7 @@
 
 #include "config_vulkandisplay.h"
 
+class VulkanIndexBufferContext;
 class VulkanShaderContext;
 class VulkanTextureContext;
 class VulkanVertexBufferContext;
@@ -57,6 +58,9 @@ public:
   virtual void release_vertex_buffer(VertexBufferContext *vbc);
 
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
+  bool update_index_buffer(VulkanIndexBufferContext *ibc,
+                           const GeomPrimitivePipelineReader *reader,
+                           bool force);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
   virtual void dispatch_compute(int size_x, int size_y, int size_z);

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

@@ -32,6 +32,7 @@ public:
 public:
   VkBuffer _buffer;
   VkDeviceMemory _memory;
+  VkIndexType _index_type;
 
 public:
   static TypeHandle get_class_type() {