Browse Source

vulkan: Multiview textures

See 07f9f9d89722a82234e0d111e61b7ff98e88e2db
rdb 2 years ago
parent
commit
01a1f42e2e

+ 10 - 6
panda/src/vulkandisplay/vulkanGraphicsBuffer.cxx

@@ -550,13 +550,15 @@ destroy_framebuffer() {
 
   // Destroy the resources held for each attachment.
   for (Attachment &attach : _attachments) {
-    if (attach._tc->_image_view != VK_NULL_HANDLE) {
+    if (!attach._tc->_image_views.empty()) {
       if (vkgsg->_last_frame_data != nullptr) {
-        vkgsg->_last_frame_data->_pending_destroy_image_views.push_back(attach._tc->_image_view);
+        vkgsg->_last_frame_data->_pending_destroy_image_views.insert(
+          vkgsg->_last_frame_data->_pending_destroy_image_views.end(),
+          attach._tc->_image_views.begin(), attach._tc->_image_views.end());
+        attach._tc->_image_views.clear();
       } else {
-        vkDestroyImageView(device, attach._tc->_image_view, nullptr);
+        attach._tc->destroy_views(device);
       }
-      attach._tc->_image_view = VK_NULL_HANDLE;
     }
 
     if (attach._tc->_image != VK_NULL_HANDLE) {
@@ -606,7 +608,7 @@ create_framebuffer() {
   VkImageView *attach_views = (VkImageView *)alloca(sizeof(VkImageView) * num_attachments);
 
   for (uint32_t i = 0; i < num_attachments; ++i) {
-    attach_views[i] = _attachments[i]._tc->_image_view;
+    attach_views[i] = _attachments[i]._tc->get_image_view(0);
   }
 
   VkFramebufferCreateInfo fb_info;
@@ -703,14 +705,16 @@ create_attachment(RenderTexturePlane plane, VkFormat format) {
   }
   tc->_aspect_mask = view_info.subresourceRange.aspectMask;
 
+  VkImageView image_view;
   VkResult err;
-  err = vkCreateImageView(device, &view_info, nullptr, &tc->_image_view);
+  err = vkCreateImageView(device, &view_info, nullptr, &image_view);
   if (err) {
     vulkan_error(err, "Failed to create image view for attachment");
     delete tc;
     return false;
   }
 
+  tc->_image_views.push_back(image_view);
   _attachments.push_back({tc, plane});
   return true;
 }

+ 110 - 61
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -772,7 +772,7 @@ allocate_memory(VulkanMemoryBlock &block, const VkMemoryRequirements &reqs,
  * call Texture::prepare().
  */
 TextureContext *VulkanGraphicsStateGuardian::
-prepare_texture(Texture *texture, int view) {
+prepare_texture(Texture *texture) {
   using std::swap;
 
   PStatTimer timer(_prepare_texture_pcollector);
@@ -786,6 +786,8 @@ prepare_texture(Texture *texture, int view) {
   extent.width = texture->get_x_size();
   extent.height = texture->get_y_size();
   extent.depth = 1;
+  int num_views = texture->get_num_views();
+  uint32_t num_layers_per_view = 1;
   uint32_t num_layers = 1;
   uint32_t num_levels = 1;
   bool is_buffer = false;
@@ -824,6 +826,9 @@ prepare_texture(Texture *texture, int view) {
   }
   const VkExtent3D orig_extent = extent;
 
+  num_layers_per_view = num_layers;
+  num_layers *= num_views;
+
   // Check if the format is actually supported.
   VkFormat format = get_image_format(texture);
   VkFormatProperties fmt_props;
@@ -962,6 +967,11 @@ prepare_texture(Texture *texture, int view) {
       tc->_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
     }
 
+    if (vulkandisplay_cat.is_debug()) {
+      vulkandisplay_cat.debug()
+        << "Created image " << tc->_image << " for texture " << *texture << "\n";
+    }
+
     // 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;
@@ -1051,21 +1061,22 @@ prepare_texture(Texture *texture, int view) {
     view_info.subresourceRange.baseMipLevel = 0;
     view_info.subresourceRange.levelCount = num_levels;
     view_info.subresourceRange.baseArrayLayer = 0;
-    view_info.subresourceRange.layerCount = num_layers;
-
-    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;
-    }
+    view_info.subresourceRange.layerCount = num_layers_per_view;
+
+    for (int view = 0; view < num_views; ++view) {
+      VkImageView image_view;
+      VkResult err;
+      err = vkCreateImageView(_device, &view_info, nullptr, &image_view);
+      if (err) {
+        vulkan_error(err, "Failed to create image view for texture");
+        tc->destroy_views(_device);
+        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";
+      tc->_image_views.push_back(image_view);
+      view_info.subresourceRange.baseArrayLayer += num_layers_per_view;
     }
   }
   else {
@@ -1087,15 +1098,23 @@ prepare_texture(Texture *texture, int view) {
 
     VkBuffer buffer;
     VulkanMemoryBlock block;
-    VkDeviceSize size = texture->get_expected_ram_image_size();
+    //VkDeviceSize view_size = texture->get_expected_ram_view_size();
+    VkDeviceSize view_size = texture->get_expected_ram_image_size() / (size_t)num_views;
     if (pack_bgr8) {
-      size = size / 3 * 4;
+      view_size = view_size / 3 * 4;
     }
-    if (!create_buffer(size, buffer, block, usage,
+    VkDeviceSize total_size = view_size * num_views;
+    if (!create_buffer(total_size, buffer, block, usage,
                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
       return nullptr;
     }
 
+    if (vulkandisplay_cat.is_debug()) {
+      vulkandisplay_cat.debug()
+        << "Created buffer " << buffer << " with size " << total_size
+        << " for texture " << *texture << "\n";
+    }
+
     VkBufferViewCreateInfo view_info;
     view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
     view_info.pNext = nullptr;
@@ -1103,27 +1122,32 @@ prepare_texture(Texture *texture, int view) {
     view_info.buffer = buffer;
     view_info.format = format;
     view_info.offset = 0;
-    view_info.range = VK_WHOLE_SIZE;
+    view_info.range = view_size;
+
+    small_vector<VkBufferView> buffer_views;
+    for (int view = 0; view < num_views; ++view) {
+      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");
+        for (VkBufferView buffer_view : buffer_views) {
+          vkDestroyBufferView(_device, buffer_view, nullptr);
+        }
+        vkDestroyBuffer(_device, buffer, nullptr);
+        delete tc;
+        return nullptr;
+      }
 
-    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;
+      buffer_views.push_back(buffer_view);
+      view_info.offset += view_size;
     }
 
-    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 = new VulkanTextureContext(get_prepared_objects(), texture);
     tc->_format = format;
     tc->_extent = extent;
     tc->_buffer = buffer;
-    tc->_buffer_view = buffer_view;
+    tc->_buffer_views = std::move(buffer_views);
     tc->_block = std::move(block);
     tc->_pack_bgr8 = pack_bgr8;
   }
@@ -1450,6 +1474,7 @@ update_texture(TextureContext *tc, bool force) {
       extent.depth = 1;
       arrayLayers = tex->get_z_size();
     }
+    arrayLayers *= tex->get_num_views();
 
     //VkFormat format = get_image_format(tex);
 
@@ -1485,28 +1510,52 @@ release_texture(TextureContext *tc) {
   VulkanTextureContext *vtc;
   DCAST_INTO_V(vtc, tc);
 
-  if (vtc->_image != VK_NULL_HANDLE && vulkandisplay_cat.is_debug()) {
-    vulkandisplay_cat.debug()
-      << "Deleting image " << vtc->_image << " and view " << vtc->_image_view << "\n";
-  }
-
-  if (vtc->_image_view != VK_NULL_HANDLE) {
-    _frame_data->_pending_destroy_image_views.push_back(vtc->_image_view);
-  }
   if (vtc->_image != VK_NULL_HANDLE) {
     _frame_data->_pending_destroy_images.push_back(vtc->_image);
-  }
 
-  if (vtc->_buffer != VK_NULL_HANDLE && vulkandisplay_cat.is_debug()) {
-    vulkandisplay_cat.debug()
-      << "Deleting buffer " << vtc->_buffer << " and view " << vtc->_buffer_view << "\n";
+    if (vulkandisplay_cat.is_debug()) {
+      std::ostream &out = vulkandisplay_cat.debug()
+        << "Scheduling image " << vtc->_image;
+
+      if (!vtc->_image_views.empty()) {
+        out << " with views";
+        for (VkImageView image_view : vtc->_image_views) {
+          out << " " << image_view;
+        }
+      }
+
+      out << " for deletion\n";
+    }
   }
 
-  if (vtc->_buffer_view != VK_NULL_HANDLE) {
-    _frame_data->_pending_destroy_buffer_views.push_back(vtc->_buffer_view);
+  if (!vtc->_image_views.empty()) {
+    _frame_data->_pending_destroy_image_views.insert(
+      _frame_data->_pending_destroy_image_views.end(),
+      vtc->_image_views.begin(), vtc->_image_views.end());
   }
+
   if (vtc->_buffer != VK_NULL_HANDLE) {
     _frame_data->_pending_destroy_buffers.push_back(vtc->_buffer);
+
+    if (vulkandisplay_cat.is_debug()) {
+      std::ostream &out = vulkandisplay_cat.debug()
+        << "Scheduling buffer " << vtc->_buffer;
+
+      if (!vtc->_buffer_views.empty()) {
+        out << " with views";
+        for (VkBufferView buffer_view : vtc->_buffer_views) {
+          out << " " << buffer_view;
+        }
+      }
+
+      out << " for deletion\n";
+    }
+  }
+
+  if (!vtc->_buffer_views.empty()) {
+    _frame_data->_pending_destroy_buffer_views.insert(
+      _frame_data->_pending_destroy_buffer_views.end(),
+      vtc->_buffer_views.begin(), vtc->_buffer_views.end());
   }
 
   // Make sure that the memory remains untouched until the frame is over.
@@ -1534,15 +1583,15 @@ bool VulkanGraphicsStateGuardian::
 extract_texture_data(Texture *tex) {
   nassertr(_frame_data != nullptr, false);
 
+  VulkanTextureContext *tc;
+  DCAST_INTO_R(tc, tex->prepare_now(get_prepared_objects(), this), false);
+
   bool success = true;
 
   // If we wanted to optimize this use-case, we could allocate a single buffer
   // to hold all texture views and copy that in one go.
   int num_views = tex->get_num_views();
   for (int view = 0; view < num_views; ++view) {
-    VulkanTextureContext *tc;
-    DCAST_INTO_R(tc, tex->prepare_now(view, get_prepared_objects(), this), false);
-
     if (!do_extract_image(tc, tex, view)) {
       success = false;
     }
@@ -2866,13 +2915,13 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
   PreparedGraphicsObjects *pgo = get_prepared_objects();
 
   VulkanTextureContext *tc;
-  DCAST_INTO_R(tc, tex->prepare_now(view, pgo, this), false);
+  DCAST_INTO_R(tc, tex->prepare_now(pgo, this), false);
 
   // Temporary, prepare_now should really deal with the resizing
   if (tc->_extent.width != (uint32_t)tex->get_x_size() ||
       tc->_extent.height != (uint32_t)tex->get_y_size()) {
     pgo->release_texture(tc);
-    DCAST_INTO_R(tc, tex->prepare_now(view, pgo, this), false);
+    DCAST_INTO_R(tc, tex->prepare_now(pgo, this), false);
   }
 
   nassertr(fbtc->_extent.width <= tc->_extent.width &&
@@ -3808,7 +3857,7 @@ update_lattr_descriptor_set(VkDescriptorSet ds, const LightAttrib *attr) {
 
     VkDescriptorImageInfo &image_info = image_infos[i];
     image_info.sampler = _shadow_sampler;
-    image_info.imageView = tc->_image_view;
+    image_info.imageView = tc->get_image_view(0);
     image_info.imageLayout = tc->_layout;
 
     VkWriteDescriptorSet &write = writes[i];
@@ -3866,7 +3915,7 @@ update_tattr_descriptor_set(VkDescriptorSet ds, const TextureAttrib *attr) {
     }
 
     VulkanTextureContext *tc;
-    DCAST_INTO_R(tc, texture->prepare_now(view, _prepared_objects, this), false);
+    DCAST_INTO_R(tc, texture->prepare_now(_prepared_objects, this), false);
 
     VulkanSamplerContext *sc;
     DCAST_INTO_R(sc, sampler.prepare_now(_prepared_objects, this), false);
@@ -3898,7 +3947,7 @@ update_tattr_descriptor_set(VkDescriptorSet ds, const TextureAttrib *attr) {
 
     VkDescriptorImageInfo &image_info = image_infos[i];
     image_info.sampler = sc->_sampler;
-    image_info.imageView = tc->_image_view;
+    image_info.imageView = tc->get_image_view(view);
     image_info.imageLayout = tc->_layout;
 
     VkWriteDescriptorSet &write = writes[i];
@@ -3981,7 +4030,7 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
     }
 
     VulkanTextureContext *tc;
-    DCAST_INTO_R(tc, texture->prepare_now(view, _prepared_objects, this), false);
+    DCAST_INTO_R(tc, texture->prepare_now(_prepared_objects, this), false);
 
     VulkanSamplerContext *sc;
     DCAST_INTO_R(sc, sampler.prepare_now(_prepared_objects, this), false);
@@ -4031,12 +4080,12 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
       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.imageView = tc->get_image_view(view);
       image_info.imageLayout = tc->_layout;
       write.pImageInfo = &image_info;
     } else {
       write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
-      write.pTexelBufferView = &tc->_buffer_view;
+      write.pTexelBufferView = &tc->get_buffer_view(view);
     }
 
     ++i;
@@ -4091,7 +4140,7 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
 
     VulkanTextureContext *tc;
     int view = get_current_tex_view_offset();
-    DCAST_INTO_R(tc, texture->prepare_now(view, _prepared_objects, this), false);
+    DCAST_INTO_R(tc, texture->prepare_now(_prepared_objects, this), false);
 
     tc->set_active(true);
     update_texture(tc, true);
@@ -4138,12 +4187,12 @@ update_sattr_descriptor_set(VkDescriptorSet ds, const ShaderAttrib *attr) {
       write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
       VkDescriptorImageInfo &image_info = image_infos[i];
       image_info.sampler = VK_NULL_HANDLE;
-      image_info.imageView = tc->_image_view;
+      image_info.imageView = tc->get_image_view(view);
       image_info.imageLayout = tc->_layout;
       write.pImageInfo = &image_info;
     } else {
       write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
-      write.pTexelBufferView = &tc->_buffer_view;
+      write.pTexelBufferView = &tc->get_buffer_view(view);
     }
 
     ++i;

+ 1 - 1
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.h

@@ -47,7 +47,7 @@ public:
   bool allocate_memory(VulkanMemoryBlock &block, const VkMemoryRequirements &reqs,
                        VkFlags required_flags, bool linear);
 
-  virtual TextureContext *prepare_texture(Texture *tex, int view);
+  virtual TextureContext *prepare_texture(Texture *tex);
   bool upload_texture(VulkanTextureContext *vtc);
   virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);

+ 22 - 20
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -812,18 +812,14 @@ destroy_swapchain() {
   for (SwapBuffer &buffer : _swap_buffers) {
     // Destroy the framebuffers that use the swapchain images.
     vkDestroyFramebuffer(device, buffer._framebuffer, nullptr);
-    vkDestroyImageView(device, buffer._tc->_image_view, nullptr);
-
+    buffer._tc->destroy_views(device);
     buffer._tc->update_data_size_bytes(0);
     delete buffer._tc;
   }
   _swap_buffers.clear();
 
   if (_ms_color_tc != nullptr) {
-    if (_ms_color_tc->_image_view != VK_NULL_HANDLE) {
-      vkDestroyImageView(device, _ms_color_tc->_image_view, nullptr);
-      _ms_color_tc->_image_view = VK_NULL_HANDLE;
-    }
+    _ms_color_tc->destroy_views(device);
 
     if (_ms_color_tc->_image != VK_NULL_HANDLE) {
       vkDestroyImage(device, _ms_color_tc->_image, nullptr);
@@ -835,10 +831,7 @@ destroy_swapchain() {
   }
 
   if (_depth_stencil_tc != nullptr) {
-    if (_depth_stencil_tc->_image_view != VK_NULL_HANDLE) {
-      vkDestroyImageView(device, _depth_stencil_tc->_image_view, nullptr);
-      _depth_stencil_tc->_image_view = VK_NULL_HANDLE;
-    }
+    _depth_stencil_tc->destroy_views(device);
 
     if (_depth_stencil_tc->_image != VK_NULL_HANDLE) {
       vkDestroyImage(device, _depth_stencil_tc->_image, nullptr);
@@ -986,15 +979,19 @@ create_swapchain() {
     view_info.subresourceRange.baseArrayLayer = 0;
     view_info.subresourceRange.layerCount = 1;
 
-    err = vkCreateImageView(device, &view_info, nullptr, &buffer._tc->_image_view);
+    VkImageView image_view;
+    err = vkCreateImageView(device, &view_info, nullptr, &image_view);
     if (err) {
       vulkan_error(err, "Failed to create image view for swapchain");
       return false;
     }
+
+    buffer._tc->_image_views.push_back(image_view);
   }
 
   // Now create a depth image.
   _depth_stencil_tc = nullptr;
+  VkImageView depth_stencil_view = VK_NULL_HANDLE;
   if (_depth_stencil_format != VK_FORMAT_UNDEFINED) {
     _depth_stencil_tc = vkgsg->create_image(VK_IMAGE_TYPE_2D,
       _depth_stencil_format, extent, 1, 1, _ms_count,
@@ -1026,15 +1023,18 @@ create_swapchain() {
       view_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
     }
 
-    err = vkCreateImageView(device, &view_info, nullptr, &_depth_stencil_tc->_image_view);
+    err = vkCreateImageView(device, &view_info, nullptr, &depth_stencil_view);
     if (err) {
       vulkan_error(err, "Failed to create image view for depth/stencil");
       return false;
     }
+
+    _depth_stencil_tc->_image_views.push_back(depth_stencil_view);
   }
 
   // Create a multisample color image.
   _ms_color_tc = nullptr;
+  VkImageView ms_color_view = VK_NULL_HANDLE;
   if (_ms_count != VK_SAMPLE_COUNT_1_BIT) {
     _ms_color_tc = vkgsg->create_image(VK_IMAGE_TYPE_2D,
       swapchain_info.imageFormat, extent, 1, 1, _ms_count,
@@ -1062,23 +1062,25 @@ create_swapchain() {
     view_info.subresourceRange.baseArrayLayer = 0;
     view_info.subresourceRange.layerCount = 1;
 
-    err = vkCreateImageView(device, &view_info, nullptr, &_ms_color_tc->_image_view);
+    err = vkCreateImageView(device, &view_info, nullptr, &ms_color_view);
     if (err) {
-      vulkan_error(err, "Failed to create image view for depth/stencil");
+      vulkan_error(err, "Failed to create image view for multisample color");
       return false;
     }
+
+    _ms_color_tc->_image_views.push_back(ms_color_view);
   }
 
   // Now finally create a framebuffer for each link in the swap chain.
   VkImageView attach_views[3];
   uint32_t num_views = 1;
 
-  if (_ms_color_tc != nullptr) {
-    attach_views[0] = _ms_color_tc->_image_view;
+  if (ms_color_view != VK_NULL_HANDLE) {
+    attach_views[0] = ms_color_view;
     ++num_views;
   }
-  if (_depth_stencil_tc != nullptr) {
-    attach_views[1] = _depth_stencil_tc->_image_view;
+  if (depth_stencil_view != nullptr) {
+    attach_views[1] = depth_stencil_view;
     ++num_views;
   }
 
@@ -1096,9 +1098,9 @@ create_swapchain() {
   for (uint32_t i = 0; i < num_images; ++i) {
     SwapBuffer &buffer = _swap_buffers[i];
     if (_ms_color_tc != nullptr) {
-      attach_views[num_views - 1] = buffer._tc->_image_view;
+      attach_views[num_views - 1] = buffer._tc->get_image_view(0);
     } else {
-      attach_views[0] = buffer._tc->_image_view;
+      attach_views[0] = buffer._tc->get_image_view(0);
     }
     err = vkCreateFramebuffer(device, &fb_info, nullptr, &buffer._framebuffer);
     if (err) {

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

@@ -16,8 +16,8 @@
  * image, allocate the memory and create an image view.
  */
 INLINE VulkanTextureContext::
-VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture, int view) :
-  TextureContext(pgo, texture, view),
+VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture) :
+  TextureContext(pgo, texture),
   //TODO: it is not clear to me what we should set srcStageMask to here.
   _stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) {
 }
@@ -28,7 +28,7 @@ VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture, int view) :
  */
 INLINE VulkanTextureContext::
 VulkanTextureContext(PreparedGraphicsObjects *pgo, VkImage image, VkFormat format) :
-  TextureContext(pgo, nullptr, 0),
+  TextureContext(pgo, nullptr),
   _format(format),
   _image(image),
   //TODO: it is not clear to me what we should set srcStageMask to here.
@@ -43,6 +43,30 @@ set_texture(Texture *texture) {
   _object = texture;
 }
 
+/**
+ * Returns the VkImageView handle for the given view of the texture.
+ */
+INLINE const VkImageView &VulkanTextureContext::
+get_image_view(int view) const {
+  if (!_image_views.empty()) {
+    return _image_views[std::min(std::max(view, 0), (int)_image_views.size() - 1)];
+  } else {
+    return VK_NULL_HANDLE;
+  }
+}
+
+/**
+ * Returns the VkBufferView handle for the given view of the texture.
+ */
+INLINE const VkBufferView &VulkanTextureContext::
+get_buffer_view(int view) const {
+  if (!_buffer_views.empty()) {
+    return _buffer_views[std::min(std::max(view, 0), (int)_buffer_views.size() - 1)];
+  } else {
+    return VK_NULL_HANDLE;
+  }
+}
+
 /**
  * Records a way that the image has been accessed.
  */

+ 16 - 0
panda/src/vulkandisplay/vulkanTextureContext.cxx

@@ -15,6 +15,22 @@
 
 TypeHandle VulkanTextureContext::_type_handle;
 
+/**
+ * Destroys the view handles associated with this context immediately.
+ */
+void VulkanTextureContext::
+destroy_views(VkDevice device) {
+  for (VkImageView image_view : _image_views) {
+    vkDestroyImageView(device, image_view, nullptr);
+  }
+  _image_views.clear();
+
+  for (VkBufferView buffer_view : _buffer_views) {
+    vkDestroyBufferView(device, buffer_view, nullptr);
+  }
+  _buffer_views.clear();
+}
+
 /**
  * Inserts commands to clear the image.
  */

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

@@ -16,20 +16,26 @@
 
 #include "config_vulkandisplay.h"
 #include "textureContext.h"
+#include "small_vector.h"
 
 /**
  * Manages a Vulkan image and device memory.
  */
 class EXPCL_VULKANDISPLAY VulkanTextureContext : public TextureContext {
 public:
-  INLINE VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture, int view);
+  INLINE VulkanTextureContext(PreparedGraphicsObjects *pgo, Texture *texture);
   INLINE VulkanTextureContext(PreparedGraphicsObjects *pgo, VkImage image, VkFormat format);
   ~VulkanTextureContext() {};
 
   ALLOC_DELETED_CHAIN(VulkanTextureContext);
 
+  void destroy_views(VkDevice device);
+
   INLINE void set_texture(Texture *texture);
 
+  INLINE const VkImageView &get_image_view(int view) const;
+  INLINE const VkBufferView &get_buffer_view(int view) const;
+
   INLINE void access(VkPipelineStageFlags stage_mask, VkAccessFlags access_mask);
   INLINE void discard();
 
@@ -53,9 +59,9 @@ public:
   // 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;
-  VkImageView _image_view = VK_NULL_HANDLE;
+  small_vector<VkImageView> _image_views;
   VkBuffer _buffer = VK_NULL_HANDLE;
-  VkBufferView _buffer_view = VK_NULL_HANDLE;
+  small_vector<VkBufferView> _buffer_views;
   VulkanMemoryBlock _block;
 
   VkImageLayout _layout = VK_IMAGE_LAYOUT_UNDEFINED;