소스 검색

vulkan: Add support for buffer textures

rdb 5 년 전
부모
커밋
0f0749901b

+ 464 - 314
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -322,7 +322,7 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
   _supports_3d_texture = true;
   _supports_3d_texture = true;
   _supports_2d_texture_array = true;
   _supports_2d_texture_array = true;
   _supports_cube_map = true;
   _supports_cube_map = true;
-  _supports_buffer_texture = false; //TODO: add support.
+  _supports_buffer_texture = true;
   _supports_cube_map_array = (features.imageCubeArray != VK_FALSE);
   _supports_cube_map_array = (features.imageCubeArray != VK_FALSE);
   _supports_tex_non_pow2 = true;
   _supports_tex_non_pow2 = true;
   _supports_texture_srgb = true;
   _supports_texture_srgb = true;
@@ -644,6 +644,7 @@ prepare_texture(Texture *texture, int view) {
   extent.depth = 1;
   extent.depth = 1;
   uint32_t num_layers = 1;
   uint32_t num_layers = 1;
   uint32_t num_levels = 1;
   uint32_t num_levels = 1;
+  bool is_buffer = false;
 
 
   switch (texture->get_texture_type()) {
   switch (texture->get_texture_type()) {
   case Texture::TT_1d_texture:
   case Texture::TT_1d_texture:
@@ -668,8 +669,8 @@ prepare_texture(Texture *texture, int view) {
     break;
     break;
 
 
   case Texture::TT_buffer_texture:
   case Texture::TT_buffer_texture:
-    // Not yet supported.
-    return nullptr;
+    is_buffer = true;
+    break;
   }
   }
   const VkExtent3D orig_extent = extent;
   const VkExtent3D orig_extent = extent;
 
 
@@ -678,8 +679,15 @@ prepare_texture(Texture *texture, int view) {
   VkFormatProperties fmt_props;
   VkFormatProperties fmt_props;
   vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
   vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
 
 
+  bool supported;
+  if (is_buffer) {
+    supported = (fmt_props.bufferFeatures & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) != 0;
+  } else {
+    supported = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
+  }
+
   bool pack_bgr8 = false;
   bool pack_bgr8 = false;
-  if ((fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0) {
+  if (!supported) {
     // Not supported.  Can we convert it to a format that is supported?
     // Not supported.  Can we convert it to a format that is supported?
     switch (format) {
     switch (format) {
     case VK_FORMAT_B8G8R8_UNORM:
     case VK_FORMAT_B8G8R8_UNORM:
@@ -702,207 +710,262 @@ prepare_texture(Texture *texture, int view) {
     vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
     vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
   }
   }
 
 
-  VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-
-  // Check that the size is actually supported.
-  int mipmap_begin = 0;
-  VkImageFormatProperties img_props;
-  vkGetPhysicalDeviceImageFormatProperties(vkpipe->_gpu, format, type,
-                                           VK_IMAGE_TILING_OPTIMAL, usage,
-                                           flags, &img_props);
-  if (num_layers > img_props.maxArrayLayers) {
-    //TODO: more elegant solution to reduce layer count.
-    vulkandisplay_cat.error()
-      << "Texture has too many layers, this format has a maximum of "
-      << num_layers << "\n";
-    return nullptr;
-  }
-  while (extent.width > img_props.maxExtent.width ||
-         extent.height > img_props.maxExtent.height ||
-         extent.depth > img_props.maxExtent.depth) {
-    // Reduce the size by bumping the first mipmap level uploaded.
-    extent.width = std::max(1U, extent.width >> 1);
-    extent.height = std::max(1U, extent.height >> 1);
-    extent.depth = std::max(1U, extent.depth >> 1);
-    ++mipmap_begin;
-  }
+  VulkanTextureContext *tc;
+  if (!is_buffer) {
+    // Image texture.  Is the size supported for this format?
+    VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    VkImageFormatProperties img_props;
+    vkGetPhysicalDeviceImageFormatProperties(vkpipe->_gpu, format, type,
+                                             VK_IMAGE_TILING_OPTIMAL, usage,
+                                             flags, &img_props);
+    if (num_layers > img_props.maxArrayLayers) {
+      //TODO: more elegant solution to reduce layer count.
+      vulkandisplay_cat.error()
+        << "Texture has too many layers, this format has a maximum of "
+        << num_layers << "\n";
+      return nullptr;
+    }
+    int mipmap_begin = 0;
+    while (extent.width > img_props.maxExtent.width ||
+           extent.height > img_props.maxExtent.height ||
+           extent.depth > img_props.maxExtent.depth) {
+      // Reduce the size by bumping the first mipmap level uploaded.
+      extent.width = std::max(1U, extent.width >> 1);
+      extent.height = std::max(1U, extent.height >> 1);
+      extent.depth = std::max(1U, extent.depth >> 1);
+      ++mipmap_begin;
+    }
+
+    if (mipmap_begin != 0) {
+      vulkandisplay_cat.info()
+        << "Reducing image " << texture->get_name() << " from "
+        << orig_extent.width << " x " << orig_extent.height << " x "
+        << orig_extent.depth << " to " << extent.width << " x "
+        << extent.height << " x " << extent.depth << "\n";
+
+      if (!texture->has_ram_mipmap_image(mipmap_begin)) {
+        // Ugh, and to do this, we have to generate mipmaps on the CPU.
+        texture->generate_ram_mipmap_images();
+      }
+    }
 
 
-  if (mipmap_begin != 0) {
-    vulkandisplay_cat.info()
-      << "Reducing image " << texture->get_name() << " from "
-      << orig_extent.width << " x " << orig_extent.height << " x "
-      << orig_extent.depth << " to " << extent.width << " x "
-      << extent.height << " x " << extent.depth << "\n";
+    int mipmap_end = mipmap_begin + 1;
+    if (texture->uses_mipmaps()) {
+      mipmap_end = texture->get_expected_num_mipmap_levels();
+      nassertr(mipmap_end > mipmap_begin, nullptr);
+    }
 
 
-    if (!texture->has_ram_mipmap_image(mipmap_begin)) {
-      // Ugh, and to do this, we have to generate mipmaps on the CPU.
-      texture->generate_ram_mipmap_images();
+    // Do we need to generate any mipmaps?
+    bool generate_mipmaps = false;
+    for (int i = mipmap_begin; i < mipmap_end; ++i) {
+      if (!texture->has_ram_mipmap_image(i)) {
+        generate_mipmaps = true;
+        break;
+      }
     }
     }
-  }
 
 
-  int mipmap_end = mipmap_begin + 1;
-  if (texture->uses_mipmaps()) {
-    mipmap_end = texture->get_expected_num_mipmap_levels();
-    nassertr(mipmap_end > mipmap_begin, nullptr);
-  }
+    if (generate_mipmaps) {
+      if ((fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0 ||
+          (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0 ||
+          !driver_generate_mipmaps) {
+        // Hang on, we don't support blitting using this format.  We'll have to
+        // generate the mipmaps on the CPU instead.
+        texture->generate_ram_mipmap_images();
 
 
-  // Do we need to generate any mipmaps?
-  bool generate_mipmaps = false;
-  for (int i = mipmap_begin; i < mipmap_end; ++i) {
-    if (!texture->has_ram_mipmap_image(i)) {
-      generate_mipmaps = true;
-      break;
+        // We now have as many levels as we're going to get.
+        mipmap_end = texture->get_num_ram_mipmap_images();
+        generate_mipmaps = false;
+      } else {
+        // We'll be generating mipmaps from it, so mark it as transfer source.
+        usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+      }
     }
     }
-  }
 
 
-  if (generate_mipmaps) {
-    if ((fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0 ||
-        (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0 ||
-        !driver_generate_mipmaps) {
-      // Hang on, we don't support blitting using this format.  We'll have to
-      // generate the mipmaps on the CPU instead.
-      texture->generate_ram_mipmap_images();
-
-      // We now have as many levels as we're going to get.
-      mipmap_end = texture->get_num_ram_mipmap_images();
-      generate_mipmaps = false;
+    num_levels = mipmap_end - mipmap_begin;
+    if (num_levels > img_props.maxMipLevels) {
+      mipmap_end -= num_levels - img_props.maxMipLevels;
+      num_levels = img_props.maxMipLevels;
+    }
+
+    tc = create_image(type, format, extent, num_levels, num_layers,
+                      VK_SAMPLE_COUNT_1_BIT, usage, flags);
+    nassertr_always(tc != nullptr, nullptr);
+    tc->set_texture(texture);
+    tc->_mipmap_begin = mipmap_begin;
+    tc->_mipmap_end = mipmap_end;
+    tc->_generate_mipmaps = generate_mipmaps;
+    tc->_pack_bgr8 = pack_bgr8;
+
+    Texture::Format tex_format = texture->get_format();
+    if (tex_format == Texture::F_depth_stencil ||
+        tex_format == Texture::F_depth_component ||
+        tex_format == Texture::F_depth_component16 ||
+        tex_format == Texture::F_depth_component24 ||
+        tex_format == Texture::F_depth_component32) {
+      tc->_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
     } else {
     } else {
-      // We'll be generating mipmaps from it, so mark it as transfer source.
-      usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+      tc->_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
     }
     }
-  }
 
 
-  num_levels = mipmap_end - mipmap_begin;
-  if (num_levels > img_props.maxMipLevels) {
-    mipmap_end -= num_levels - img_props.maxMipLevels;
-    num_levels = img_props.maxMipLevels;
-  }
+    // Now we'll create an image view that describes how we interpret the image.
+    VkImageViewCreateInfo view_info;
+    view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    view_info.pNext = nullptr;
+    view_info.flags = 0;
+    view_info.image = tc->_image;
 
 
-  VulkanTextureContext *tc;
-  tc = create_image(type, format, extent, num_levels, num_layers,
-                    VK_SAMPLE_COUNT_1_BIT, usage, flags);
-  nassertr_always(tc != nullptr, nullptr);
-  tc->set_texture(texture);
-  tc->_mipmap_begin = mipmap_begin;
-  tc->_mipmap_end = mipmap_end;
-  tc->_generate_mipmaps = generate_mipmaps;
-  tc->_pack_bgr8 = pack_bgr8;
-
-  Texture::Format tex_format = texture->get_format();
-  if (tex_format == Texture::F_depth_stencil ||
-      tex_format == Texture::F_depth_component ||
-      tex_format == Texture::F_depth_component16 ||
-      tex_format == Texture::F_depth_component24 ||
-      tex_format == Texture::F_depth_component32) {
-    tc->_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
-  } else {
-    tc->_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
-  }
+    switch (texture->get_texture_type()) {
+    case Texture::TT_1d_texture:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_1D;
+      break;
+    case Texture::TT_2d_texture:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+      break;
+    case Texture::TT_3d_texture:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
+      break;
+    case Texture::TT_2d_texture_array:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
+      break;
+    case Texture::TT_cube_map:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
+      break;
+    case Texture::TT_buffer_texture: //TODO: figure out buffer textures in Vulkan.
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_1D;
+      break;
+    case Texture::TT_cube_map_array:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
+      break;
+    case Texture::TT_1d_texture_array:
+      view_info.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
+      break;
+    }
 
 
-  // Now we'll create an image view that describes how we interpret the image.
-  VkImageViewCreateInfo view_info;
-  view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-  view_info.pNext = nullptr;
-  view_info.flags = 0;
-  view_info.image = tc->_image;
+    view_info.format = format;
 
 
-  switch (texture->get_texture_type()) {
-  case Texture::TT_1d_texture:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_1D;
-    break;
-  case Texture::TT_2d_texture:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-    break;
-  case Texture::TT_3d_texture:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
-    break;
-  case Texture::TT_2d_texture_array:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
-    break;
-  case Texture::TT_cube_map:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
-    break;
-  case Texture::TT_buffer_texture: //TODO: figure out buffer textures in Vulkan.
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_1D;
-    break;
-  case Texture::TT_cube_map_array:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
-    break;
-  case Texture::TT_1d_texture_array:
-    view_info.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
-    break;
-  }
+    // We use the swizzle mask to emulate deprecated formats.
+    switch (texture->get_format()) {
+    case Texture::F_green:
+      view_info.components.r = VK_COMPONENT_SWIZZLE_ZERO;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_ZERO;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_ONE;
+      break;
 
 
-  view_info.format = format;
+    case Texture::F_blue:
+      view_info.components.r = VK_COMPONENT_SWIZZLE_ZERO;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_ZERO;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_ZERO;
+      break;
 
 
-  // We use the swizzle mask to emulate deprecated formats.
-  switch (texture->get_format()) {
-  case Texture::F_green:
-    view_info.components.r = VK_COMPONENT_SWIZZLE_ZERO;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_ZERO;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_ONE;
-    break;
+    case Texture::F_alpha:
+      //FIXME: better solution for fixing black text issue
+      view_info.components.r = VK_COMPONENT_SWIZZLE_ONE;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_ONE;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_ONE;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_R;
+      break;
 
 
-  case Texture::F_blue:
-    view_info.components.r = VK_COMPONENT_SWIZZLE_ZERO;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_ZERO;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_ZERO;
-    break;
+    case Texture::F_luminance:
+    case Texture::F_sluminance:
+      view_info.components.r = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_ONE;
+      break;
 
 
-  case Texture::F_alpha:
-    //FIXME: better solution for fixing black text issue
-    view_info.components.r = VK_COMPONENT_SWIZZLE_ONE;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_ONE;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_ONE;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_R;
-    break;
+    case Texture::F_luminance_alpha:
+      // F_sluminance_alpha can't be emulated using R8G8 and a swizzle mask
+      // because we need the second channel to be linear.  Beh.
+      view_info.components.r = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_R;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_G;
+      break;
 
 
-  case Texture::F_luminance:
-  case Texture::F_sluminance:
-    view_info.components.r = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_ONE;
-    break;
+    default:
+      view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+      view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+      view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+      view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+      break;
+    }
 
 
-  case Texture::F_luminance_alpha:
-    // F_sluminance_alpha can't be emulated using R8G8 and a swizzle mask
-    // because we need the second channel to be linear.  Beh.
-    view_info.components.r = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_R;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_G;
-    break;
+    view_info.subresourceRange.aspectMask = tc->_aspect_mask;
+    view_info.subresourceRange.baseMipLevel = 0;
+    view_info.subresourceRange.levelCount = num_levels;
+    view_info.subresourceRange.baseArrayLayer = 0;
+    view_info.subresourceRange.layerCount = num_layers;
 
 
-  default:
-    view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
-    view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
-    view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
-    view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
-    break;
+    VkResult err;
+    err = vkCreateImageView(_device, &view_info, nullptr, &tc->_image_view);
+    if (err) {
+      vulkan_error(err, "Failed to create image view for texture");
+      vkDestroyImage(_device, tc->_image, nullptr);
+      delete tc;
+      return nullptr;
+    }
+
+    if (vulkandisplay_cat.is_debug()) {
+      vulkandisplay_cat.debug()
+        << "Created image " << tc->_image << " and view " << tc->_image_view
+        << " for texture " << *texture << "\n";
+    }
   }
   }
+  else {
+    // Buffer texture.
+    if (extent.width > _max_buffer_texture_size) {
+      vulkandisplay_cat.error()
+        << "Buffer texture size " << extent.width << " is too large, maximum size is "
+        << _max_buffer_texture_size << " texels\n";
+      return nullptr;
+    }
 
 
-  view_info.subresourceRange.aspectMask = tc->_aspect_mask;
-  view_info.subresourceRange.baseMipLevel = 0;
-  view_info.subresourceRange.levelCount = num_levels;
-  view_info.subresourceRange.baseArrayLayer = 0;
-  view_info.subresourceRange.layerCount = num_layers;
+    VkBufferUsageFlags usage;
+    usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
 
 
-  VkResult err;
-  err = vkCreateImageView(_device, &view_info, nullptr, &tc->_image_view);
-  if (err) {
-    vulkan_error(err, "Failed to create image view for texture");
-    vkDestroyImage(_device, tc->_image, nullptr);
-    delete tc;
-    return nullptr;
-  }
+    VkBuffer buffer;
+    VulkanMemoryBlock block;
+    VkDeviceSize size = texture->get_expected_ram_image_size();
+    if (pack_bgr8) {
+      size = size / 3 * 4;
+    }
+    if (!create_buffer(size, buffer, block, usage,
+                       VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
+      return nullptr;
+    }
 
 
-  if (vulkandisplay_cat.is_debug()) {
-    vulkandisplay_cat.debug()
-      << "Created image " << tc->_image << " and view " << tc->_image_view
-      << " for texture " << *texture << "\n";
+    VkBufferViewCreateInfo view_info;
+    view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
+    view_info.pNext = nullptr;
+    view_info.flags = 0;
+    view_info.buffer = buffer;
+    view_info.format = format;
+    view_info.offset = 0;
+    view_info.range = VK_WHOLE_SIZE;
+
+    VkBufferView buffer_view;
+    VkResult err;
+    err = vkCreateBufferView(_device, &view_info, nullptr, &buffer_view);
+    if (err) {
+      vulkan_error(err, "Failed to create buffer view for texture");
+      return nullptr;
+    }
+
+    if (vulkandisplay_cat.is_debug()) {
+      vulkandisplay_cat.debug()
+        << "Created buffer " << buffer << " and view " << buffer_view
+        << " for texture " << *texture << "\n";
+    }
+
+    tc = new VulkanTextureContext(get_prepared_objects(), texture, view);
+    tc->_format = format;
+    tc->_extent = extent;
+    tc->_buffer = buffer;
+    tc->_buffer_view = buffer_view;
+    tc->_block = std::move(block);
+    tc->_pack_bgr8 = pack_bgr8;
   }
   }
 
 
   // We can't upload it at this point because the texture lock is currently
   // We can't upload it at this point because the texture lock is currently
@@ -930,22 +993,41 @@ upload_texture(VulkanTextureContext *tc) {
     if (texture->has_clear_color()) {
     if (texture->has_clear_color()) {
       // No, but we have to clear it to a solid color.
       // No, but we have to clear it to a solid color.
       LColor col = texture->get_clear_color();
       LColor col = texture->get_clear_color();
-      if (tc->_aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
+      if (tc->_buffer != VK_NULL_HANDLE) {
+        // Vulkan can only clear buffers using a multiple of 4 bytes.  If it's
+        // all zeroes or texels are 4 bytes, it's easy.
+        if (col == LColor::zero()) {
+          tc->clear_buffer(_transfer_cmd, 0);
+          return true;
+        }
+        vector_uchar data = texture->get_clear_data();
+        if (data.size() == sizeof(uint32_t)) {
+          tc->clear_buffer(_transfer_cmd, *(uint32_t *)&data[0]);
+          return true;
+        }
+        // Otherwise, make a RAM mipmap image and fall through.
+        texture->make_ram_image();
+      }
+      else if (tc->_aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
         VkClearColorValue value; //TODO: handle integer clears?
         VkClearColorValue value; //TODO: handle integer clears?
         value.float32[0] = col[0];
         value.float32[0] = col[0];
         value.float32[1] = col[1];
         value.float32[1] = col[1];
         value.float32[2] = col[2];
         value.float32[2] = col[2];
         value.float32[3] = col[3];
         value.float32[3] = col[3];
         tc->clear_color_image(_transfer_cmd, value);
         tc->clear_color_image(_transfer_cmd, value);
-      } else {
+        return true;
+      }
+      else {
         VkClearDepthStencilValue value;
         VkClearDepthStencilValue value;
         value.depth = col[0];
         value.depth = col[0];
         value.stencil = 0;
         value.stencil = 0;
         tc->clear_depth_stencil_image(_transfer_cmd, value);
         tc->clear_depth_stencil_image(_transfer_cmd, value);
+        return true;
       }
       }
+    } else {
+      // No clear color means we get an uninitialized image.
+      return true;
     }
     }
-
-    return true;
   }
   }
 
 
   // Create the staging buffer, where we will write the image to on the CPU.
   // Create the staging buffer, where we will write the image to on the CPU.
@@ -991,146 +1073,183 @@ upload_texture(VulkanTextureContext *tc) {
     return false;
     return false;
   }
   }
 
 
-  // Issue a command to transition the image into a layout optimal for
-  // transferring into.
-  tc->transition(_transfer_cmd, _graphics_queue_family_index,
-    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-    VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
+  if (tc->_image != VK_NULL_HANDLE) {
+    // Issue a command to transition the image into a layout optimal for
+    // transferring into.
+    tc->transition(_transfer_cmd, _graphics_queue_family_index,
+      VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+      VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
 
 
-  // Schedule a copy from our staging buffer to the image.
-  VkBufferImageCopy region = {};
-  region.imageSubresource.aspectMask = tc->_aspect_mask;
-  region.imageSubresource.mipLevel = 0;
-  region.imageSubresource.layerCount = tc->_array_layers;
-  region.imageExtent = tc->_extent;
+    // Schedule a copy from our staging buffer to the image.
+    VkBufferImageCopy region = {};
+    region.imageSubresource.aspectMask = tc->_aspect_mask;
+    region.imageSubresource.mipLevel = 0;
+    region.imageSubresource.layerCount = tc->_array_layers;
+    region.imageExtent = tc->_extent;
+
+    VkImageBlit blit = {};
+    blit.srcSubresource = region.imageSubresource;
+    blit.srcOffsets[1].x = region.imageExtent.width;
+    blit.srcOffsets[1].y = region.imageExtent.height;
+    blit.srcOffsets[1].z = region.imageExtent.depth;
+    blit.dstSubresource = blit.srcSubresource;
+
+    VkImageMemoryBarrier barrier = {};
+    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    barrier.pNext = nullptr;
+    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+    barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+    barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    barrier.image = image;
+    barrier.subresourceRange.aspectMask = tc->_aspect_mask;
+    barrier.subresourceRange.baseMipLevel = 0;
+    barrier.subresourceRange.levelCount = 1;
+    barrier.subresourceRange.baseArrayLayer = 0;
+    barrier.subresourceRange.layerCount = tc->_array_layers;
+
+    SparseArray levels_in_dst_optimal_layout;
+    levels_in_dst_optimal_layout.set_range(0, tc->_mip_levels);
+
+    for (int n = tc->_mipmap_begin; n < tc->_mipmap_end; ++n) {
+      // Get a pointer to the RAM image data.
+      const uint8_t *src = (const uint8_t *)texture->get_ram_mipmap_pointer((int)n);
+      size_t src_size;
+      CPTA_uchar ptimage;
+      if (src == nullptr) {
+        ptimage = texture->get_ram_mipmap_image(n);
+        src = (const uint8_t *)ptimage.p();
+        src_size = ptimage.size();
+      } else {
+        // It's a "pointer texture"; we trust it to be the expected size.
+        src_size = texture->get_expected_ram_mipmap_image_size((int)n);
+      }
 
 
-  VkImageBlit blit = {};
-  blit.srcSubresource = region.imageSubresource;
-  blit.srcOffsets[1].x = region.imageExtent.width;
-  blit.srcOffsets[1].y = region.imageExtent.height;
-  blit.srcOffsets[1].z = region.imageExtent.depth;
-  blit.dstSubresource = blit.srcSubresource;
-
-  VkImageMemoryBarrier barrier = {};
-  barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-  barrier.pNext = nullptr;
-  barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-  barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-  barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
-  barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
-  barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-  barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-  barrier.image = image;
-  barrier.subresourceRange.aspectMask = tc->_aspect_mask;
-  barrier.subresourceRange.baseMipLevel = 0;
-  barrier.subresourceRange.levelCount = 1;
-  barrier.subresourceRange.baseArrayLayer = 0;
-  barrier.subresourceRange.layerCount = tc->_array_layers;
-
-  SparseArray levels_in_dst_optimal_layout;
-  levels_in_dst_optimal_layout.set_range(0, tc->_mip_levels);
+      if (src == nullptr) {
+        // There's no image for this level.  Are we supposed to generate it?
+        if (n > 0 && tc->_generate_mipmaps) {
+          // Transition the previous mipmap level to optimal read layout.
+          barrier.subresourceRange.baseMipLevel = blit.srcSubresource.mipLevel;
+          vkCmdPipelineBarrier(_transfer_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
+                               VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
+                               0, nullptr, 0, nullptr, 1, &barrier);
+          levels_in_dst_optimal_layout.clear_bit(barrier.subresourceRange.baseMipLevel);
+
+          blit.dstSubresource.mipLevel = region.imageSubresource.mipLevel;
+          blit.dstOffsets[1].x = region.imageExtent.width;
+          blit.dstOffsets[1].y = region.imageExtent.height;
+          blit.dstOffsets[1].z = region.imageExtent.depth;
+          vkCmdBlitImage(_transfer_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                         image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
+                         VK_FILTER_LINEAR);
+
+          blit.srcSubresource.mipLevel = blit.dstSubresource.mipLevel;
+          blit.srcOffsets[1] = blit.dstOffsets[1];
 
 
-  for (int n = tc->_mipmap_begin; n < tc->_mipmap_end; ++n) {
-    // Get a pointer to the RAM image data.
-    const uint8_t *src = (const uint8_t *)texture->get_ram_mipmap_pointer((int)n);
-    size_t src_size;
-    CPTA_uchar ptimage;
-    if (src == nullptr) {
-      ptimage = texture->get_ram_mipmap_image(n);
-      src = (const uint8_t *)ptimage.p();
-      src_size = ptimage.size();
-    } else {
-      // It's a "pointer texture"; we trust it to be the expected size.
-      src_size = texture->get_expected_ram_mipmap_image_size((int)n);
-    }
+        } else {
+          // No, shoot.  Um, panic?
+          vulkandisplay_cat.warning() << "No RAM mipmap level " << n
+            << " found for texture " << texture->get_name() << "\n";
+        }
 
 
-    if (src == nullptr) {
-      // There's no image for this level.  Are we supposed to generate it?
-      if (n > 0 && tc->_generate_mipmaps) {
-        // Transition the previous mipmap level to optimal read layout.
-        barrier.subresourceRange.baseMipLevel = blit.srcSubresource.mipLevel;
-        vkCmdPipelineBarrier(_transfer_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
-                             VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
-                             0, nullptr, 0, nullptr, 1, &barrier);
-        levels_in_dst_optimal_layout.clear_bit(barrier.subresourceRange.baseMipLevel);
+      } else {
+        // We do have an image.  This means we can write it to the appropriate
+        // location in the staging buffer, and schedule a copy to the image.
+        nassertr(buffer != VK_NULL_HANDLE, false);
+
+        // Pad for optimal alignment.
+        VkDeviceSize remain = region.bufferOffset % optimal_align;
+        if (remain > 0) {
+          region.bufferOffset += optimal_align - remain;
+        }
+        nassertr(region.bufferOffset + src_size <= block.get_size(), false);
 
 
-        blit.dstSubresource.mipLevel = region.imageSubresource.mipLevel;
-        blit.dstOffsets[1].x = region.imageExtent.width;
-        blit.dstOffsets[1].y = region.imageExtent.height;
-        blit.dstOffsets[1].z = region.imageExtent.depth;
-        vkCmdBlitImage(_transfer_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                       image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
-                       VK_FILTER_LINEAR);
+        uint8_t *dest = (uint8_t *)data + region.bufferOffset;
 
 
-        blit.srcSubresource.mipLevel = blit.dstSubresource.mipLevel;
-        blit.srcOffsets[1] = blit.dstOffsets[1];
+        if (tc->_pack_bgr8) {
+          // Pack RGB data into RGBA, since most cards don't support RGB8.
+          const uint8_t *src_end = src + src_size;
+          uint32_t *dest32 = (uint32_t *)dest;
+          nassertr(((uintptr_t)dest32 & 0x3) == 0, false);
 
 
-      } else {
-        // No, shoot.  Um, panic?
-        vulkandisplay_cat.warning() << "No RAM mipmap level " << n
-          << " found for texture " << texture->get_name() << "\n";
-      }
+          for (; src < src_end; src += 3) {
+            *dest32++ = 0xff000000 | (src[0] << 16) | (src[1] << 8) | src[2];
+          }
+          src_size = src_size / 3 * 4;
+        } else {
+          memcpy(dest, src, src_size);
+        }
 
 
-    } else {
-      // We do have an image.  This means we can write it to the appropriate
-      // location in the staging buffer, and schedule a copy to the image.
-      nassertr(buffer != VK_NULL_HANDLE, false);
+        // Schedule a copy from the staging buffer.
+        vkCmdCopyBufferToImage(_transfer_cmd, buffer, image,
+                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+        region.bufferOffset += src_size;
+        _data_transferred_pcollector.add_level(src_size);
 
 
-      // Pad for optimal alignment.
-      VkDeviceSize remain = region.bufferOffset % optimal_align;
-      if (remain > 0) {
-        region.bufferOffset += optimal_align - remain;
+        blit.srcSubresource.mipLevel = region.imageSubresource.mipLevel;
+        blit.srcOffsets[1].x = region.imageExtent.width;
+        blit.srcOffsets[1].y = region.imageExtent.height;
+        blit.srcOffsets[1].z = region.imageExtent.depth;
       }
       }
-      nassertr(region.bufferOffset + src_size <= block.get_size(), false);
-
-      uint8_t *dest = (uint8_t *)data + region.bufferOffset;
 
 
-      if (tc->_pack_bgr8) {
-        // Pack RGB data into RGBA, since most cards don't support RGB8.
-        const uint8_t *src_end = src + src_size;
-        uint32_t *dest32 = (uint32_t *)dest;
-        nassertr(((uintptr_t)dest32 & 0x3) == 0, false);
+      region.imageExtent.width = std::max(1U, region.imageExtent.width >> 1);
+      region.imageExtent.height = std::max(1U, region.imageExtent.height >> 1);
+      region.imageExtent.depth = std::max(1U, region.imageExtent.depth >> 1);
+      ++region.imageSubresource.mipLevel;
+    }
 
 
-        for (; src < src_end; src += 3) {
-          *dest32++ = 0xff000000 | (src[0] << 16) | (src[1] << 8) | src[2];
-        }
-        src_size = src_size / 3 * 4;
-      } else {
-        memcpy(dest, src, src_size);
-      }
+    data.unmap();
 
 
-      // Schedule a copy from the staging buffer.
-      vkCmdCopyBufferToImage(_transfer_cmd, buffer, image,
-                             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
-      region.bufferOffset += src_size;
-      _data_transferred_pcollector.add_level(src_size);
+    // Now, ensure that all the mipmap levels are in the same layout.
+    if (!levels_in_dst_optimal_layout.has_all_of(0, tc->_mip_levels)) {
+      for (size_t ri = 0; ri < levels_in_dst_optimal_layout.get_num_subranges(); ++ri) {
+        barrier.subresourceRange.baseMipLevel = levels_in_dst_optimal_layout.get_subrange_begin(ri);
+        barrier.subresourceRange.levelCount = levels_in_dst_optimal_layout.get_subrange_end(ri)
+                                            - levels_in_dst_optimal_layout.get_subrange_begin(ri);
 
 
-      blit.srcSubresource.mipLevel = region.imageSubresource.mipLevel;
-      blit.srcOffsets[1].x = region.imageExtent.width;
-      blit.srcOffsets[1].y = region.imageExtent.height;
-      blit.srcOffsets[1].z = region.imageExtent.depth;
+        vkCmdPipelineBarrier(_transfer_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
+                             VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
+                             nullptr, 1, &barrier);
+      }
+      tc->_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
     }
     }
-
-    region.imageExtent.width = std::max(1U, region.imageExtent.width >> 1);
-    region.imageExtent.height = std::max(1U, region.imageExtent.height >> 1);
-    region.imageExtent.depth = std::max(1U, region.imageExtent.depth >> 1);
-    ++region.imageSubresource.mipLevel;
   }
   }
+  else if (tc->_buffer != VK_NULL_HANDLE) {
+    // Get a pointer to the RAM image data.
+    const uint8_t *src = (const uint8_t *)texture->get_ram_mipmap_pointer(0);
+    size_t src_size;
+    CPTA_uchar ptimage;
+    if (src == nullptr) {
+      ptimage = texture->get_ram_mipmap_image(0);
+      src = (const uint8_t *)ptimage.p();
+      src_size = ptimage.size();
+    } else {
+      // It's a "pointer texture"; we trust it to be the expected size.
+      src_size = texture->get_expected_ram_mipmap_image_size(0);
+    }
 
 
-  data.unmap();
-
-  // Now, ensure that all the mipmap levels are in the same layout.
-  if (!levels_in_dst_optimal_layout.has_all_of(0, tc->_mip_levels)) {
-    for (size_t ri = 0; ri < levels_in_dst_optimal_layout.get_num_subranges(); ++ri) {
-      barrier.subresourceRange.baseMipLevel = levels_in_dst_optimal_layout.get_subrange_begin(ri);
-      barrier.subresourceRange.levelCount = levels_in_dst_optimal_layout.get_subrange_end(ri)
-                                          - levels_in_dst_optimal_layout.get_subrange_begin(ri);
+    if (tc->_pack_bgr8) {
+      // Pack RGB data into RGBA, since most cards don't support RGB8.
+      const uint8_t *src_end = src + src_size;
+      uint32_t *dest32 = (uint32_t *)data;
+      nassertr(((uintptr_t)dest32 & 0x3) == 0, false);
 
 
-      vkCmdPipelineBarrier(_transfer_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
-                           VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
-                           nullptr, 1, &barrier);
+      for (; src < src_end; src += 3) {
+        *dest32++ = 0xff000000 | (src[0] << 16) | (src[1] << 8) | src[2];
+      }
+      src_size = src_size / 3 * 4;
+    } else {
+      memcpy(data, src, src_size);
     }
     }
-    tc->_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+    data.unmap();
+
+    VkBufferCopy region;
+    region.srcOffset = 0;
+    region.dstOffset = 0;
+    region.size = src_size;
+    vkCmdCopyBuffer(_transfer_cmd, buffer, tc->_buffer, 1, &region);
   }
   }
 
 
   tc->mark_loaded();
   tc->mark_loaded();
@@ -1218,7 +1337,7 @@ release_texture(TextureContext *tc) {
   VulkanTextureContext *vtc;
   VulkanTextureContext *vtc;
   DCAST_INTO_V(vtc, tc);
   DCAST_INTO_V(vtc, tc);
 
 
-  if (vulkandisplay_cat.is_debug()) {
+  if (vtc->_image != VK_NULL_HANDLE && vulkandisplay_cat.is_debug()) {
     vulkandisplay_cat.debug()
     vulkandisplay_cat.debug()
       << "Deleting image " << vtc->_image << " and view " << vtc->_image_view << "\n";
       << "Deleting image " << vtc->_image << " and view " << vtc->_image_view << "\n";
   }
   }
@@ -1232,6 +1351,20 @@ release_texture(TextureContext *tc) {
     vtc->_image = VK_NULL_HANDLE;
     vtc->_image = VK_NULL_HANDLE;
   }
   }
 
 
+  if (vtc->_buffer != VK_NULL_HANDLE && vulkandisplay_cat.is_debug()) {
+    vulkandisplay_cat.debug()
+      << "Deleting buffer " << vtc->_buffer << " and view " << vtc->_buffer_view << "\n";
+  }
+
+  if (vtc->_buffer_view != VK_NULL_HANDLE) {
+    vkDestroyBufferView(_device, vtc->_buffer_view, nullptr);
+    vtc->_buffer_view = VK_NULL_HANDLE;
+  }
+  if (vtc->_buffer != VK_NULL_HANDLE) {
+    vkDestroyBuffer(_device, vtc->_buffer, nullptr);
+    vtc->_buffer = VK_NULL_HANDLE;
+  }
+
   delete vtc;
   delete vtc;
 }
 }
 
 
@@ -3404,6 +3537,17 @@ update_tattr_descriptor_set(VkDescriptorSet ds, const TextureAttrib *attr) {
       sampler = attr->get_on_sampler(stage);
       sampler = attr->get_on_sampler(stage);
       view += stage->get_tex_view_offset();
       view += stage->get_tex_view_offset();
       texture = attr->get_on_texture(stage);
       texture = attr->get_on_texture(stage);
+
+      // We can't support this easily, because texel buffers need a different
+      // descriptor type, so we'd need a different layout as well, which ruins
+      // the whole convenience of doing TextureAttrib this way.  However, it's
+      // not a limitation that people are likely to run into anyway.
+      if (texture->get_texture_type() == Texture::TT_buffer_texture) {
+        vulkandisplay_cat.error()
+          << "Buffer textures are not supported via texture stages.  Use shader inputs instead.\n";
+        texture = _white_texture;
+        view = 0;
+      }
     } else {
     } else {
       texture = _white_texture;
       texture = _white_texture;
     }
     }
@@ -3559,11 +3703,6 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
                    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                    stage_flags, VK_ACCESS_SHADER_READ_BIT);
                    stage_flags, VK_ACCESS_SHADER_READ_BIT);
 
 
-    VkDescriptorImageInfo &image_info = image_infos[i];
-    image_info.sampler = sc->_sampler;
-    image_info.imageView = tc->_image_view;
-    image_info.imageLayout = tc->_layout;
-
     VkWriteDescriptorSet &write = writes[i];
     VkWriteDescriptorSet &write = writes[i];
     write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
     write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
     write.pNext = nullptr;
     write.pNext = nullptr;
@@ -3571,11 +3710,22 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
     write.dstBinding = i;
     write.dstBinding = i;
     write.dstArrayElement = 0;
     write.dstArrayElement = 0;
     write.descriptorCount = 1;
     write.descriptorCount = 1;
-    write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-    write.pImageInfo = &image_info;
+    write.pImageInfo = nullptr;
     write.pBufferInfo = nullptr;
     write.pBufferInfo = nullptr;
     write.pTexelBufferView = nullptr;
     write.pTexelBufferView = nullptr;
 
 
+    if (spec._desired_type != Texture::TT_buffer_texture) {
+      write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+      VkDescriptorImageInfo &image_info = image_infos[i];
+      image_info.sampler = sc->_sampler;
+      image_info.imageView = tc->_image_view;
+      image_info.imageLayout = tc->_layout;
+      write.pImageInfo = &image_info;
+    } else {
+      write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+      write.pTexelBufferView = &tc->_buffer_view;
+    }
+
     ++i;
     ++i;
   }
   }
 
 

+ 5 - 1
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -203,6 +203,7 @@ make_shader_attrib_descriptor_set_layout(VkDevice device) {
     ++i;
     ++i;
   }
   }
 
 
+  // Then the named texture inputs.
   for (const Shader::ShaderTexSpec &spec : _shader->_tex_spec) {
   for (const Shader::ShaderTexSpec &spec : _shader->_tex_spec) {
     if (spec._part != Shader::STO_named_input || spec._id._location < 0) {
     if (spec._part != Shader::STO_named_input || spec._id._location < 0) {
       continue;
       continue;
@@ -210,7 +211,10 @@ make_shader_attrib_descriptor_set_layout(VkDevice device) {
 
 
     VkDescriptorSetLayoutBinding &binding = bindings[i];
     VkDescriptorSetLayoutBinding &binding = bindings[i];
     binding.binding = i;
     binding.binding = i;
-    binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    binding.descriptorType =
+      (spec._desired_type == Texture::TT_buffer_texture)
+        ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+        : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
     binding.descriptorCount = 1;
     binding.descriptorCount = 1;
     binding.stageFlags = spec._id._stage_mask;
     binding.stageFlags = spec._id._stage_mask;
     binding.pImmutableSamplers = nullptr;
     binding.pImmutableSamplers = nullptr;

+ 1 - 3
panda/src/vulkandisplay/vulkanTextureContext.I

@@ -18,8 +18,7 @@
 INLINE VulkanTextureContext::
 INLINE VulkanTextureContext::
 VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture, int view) :
 VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture, int view) :
   TextureContext(pgo, texture, view),
   TextureContext(pgo, texture, view),
-  _pack_bgr8(false),
-    //TODO: it is not clear to me what we should set srcStageMask to here.
+  //TODO: it is not clear to me what we should set srcStageMask to here.
   _stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
   _stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
 }
 }
 
 
@@ -32,7 +31,6 @@ VulkanTextureContext(PreparedGraphicsObjects *pgo, VkImage image, VkFormat forma
   TextureContext(pgo, nullptr, 0),
   TextureContext(pgo, nullptr, 0),
   _format(format),
   _format(format),
   _image(image),
   _image(image),
-  _pack_bgr8(false),
   //TODO: it is not clear to me what we should set srcStageMask to here.
   //TODO: it is not clear to me what we should set srcStageMask to here.
   _stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
   _stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
 }
 }

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

@@ -63,6 +63,16 @@ clear_depth_stencil_image(VkCommandBuffer cmd, const VkClearDepthStencilValue &v
   vkCmdClearDepthStencilImage(cmd, _image, _layout, &value, 1, &range);
   vkCmdClearDepthStencilImage(cmd, _image, _layout, &value, 1, &range);
 }
 }
 
 
+/**
+ * Inserts commands to clear the image.
+ */
+void VulkanTextureContext::
+clear_buffer(VkCommandBuffer cmd, uint32_t fill) {
+  nassertv(_buffer != VK_NULL_HANDLE);
+
+  vkCmdFillBuffer(cmd, _buffer, 0, VK_WHOLE_SIZE, fill);
+}
+
 /**
 /**
  * Issues a command to transition the image to a new layout or queue family.
  * Issues a command to transition the image to a new layout or queue family.
  * Does not (yet) do inter-queue synchronization.
  * Does not (yet) do inter-queue synchronization.
@@ -71,7 +81,10 @@ void VulkanTextureContext::
 transition(VkCommandBuffer cmd, uint32_t queue_family, VkImageLayout layout,
 transition(VkCommandBuffer cmd, uint32_t queue_family, VkImageLayout layout,
            VkPipelineStageFlags dst_stage_mask, VkAccessFlags dst_access_mask) {
            VkPipelineStageFlags dst_stage_mask, VkAccessFlags dst_access_mask) {
 
 
-  nassertv(_image != VK_NULL_HANDLE);
+  if (_image == VK_NULL_HANDLE) {
+    return;
+  }
+
   if (_layout == layout) {
   if (_layout == layout) {
     return;
     return;
   }
   }

+ 9 - 4
panda/src/vulkandisplay/vulkanTextureContext.h

@@ -35,6 +35,7 @@ public:
 
 
   void clear_color_image(VkCommandBuffer cmd, const VkClearColorValue &value);
   void clear_color_image(VkCommandBuffer cmd, const VkClearColorValue &value);
   void clear_depth_stencil_image(VkCommandBuffer cmd, const VkClearDepthStencilValue &value);
   void clear_depth_stencil_image(VkCommandBuffer cmd, const VkClearDepthStencilValue &value);
+  void clear_buffer(VkCommandBuffer cmd, uint32_t fill);
 
 
   void transition(VkCommandBuffer cmd, uint32_t queue_family, VkImageLayout layout,
   void transition(VkCommandBuffer cmd, uint32_t queue_family, VkImageLayout layout,
                   VkPipelineStageFlags dst_stage_mask, VkAccessFlags dst_access_mask);
                   VkPipelineStageFlags dst_stage_mask, VkAccessFlags dst_access_mask);
@@ -42,15 +43,19 @@ public:
 public:
 public:
   VkFormat _format;
   VkFormat _format;
   VkExtent3D _extent;
   VkExtent3D _extent;
-  int _mipmap_begin, _mipmap_end;
+  int _mipmap_begin = 0, _mipmap_end = 1;
   uint32_t _mip_levels = 1;
   uint32_t _mip_levels = 1;
   uint32_t _array_layers = 1;
   uint32_t _array_layers = 1;
-  VkImageAspectFlags _aspect_mask;
-  bool _generate_mipmaps;
-  bool _pack_bgr8;
+  VkImageAspectFlags _aspect_mask = 0;
+  bool _generate_mipmaps = false;
+  bool _pack_bgr8 = false;
 
 
+  // Depending on whether it's a buffer texture or image texture, either the
+  // image and image view or buffer and buffer view will be set.
   VkImage _image = VK_NULL_HANDLE;
   VkImage _image = VK_NULL_HANDLE;
   VkImageView _image_view = VK_NULL_HANDLE;
   VkImageView _image_view = VK_NULL_HANDLE;
+  VkBuffer _buffer = VK_NULL_HANDLE;
+  VkBufferView _buffer_view = VK_NULL_HANDLE;
   VulkanMemoryBlock _block;
   VulkanMemoryBlock _block;
 
 
   VkImageLayout _layout = VK_IMAGE_LAYOUT_UNDEFINED;
   VkImageLayout _layout = VK_IMAGE_LAYOUT_UNDEFINED;