Browse Source

vulkan: Support render-to-texture, consolidate fb config selection, fix render pass compat

rdb 1 year ago
parent
commit
c4436613c1

+ 137 - 172
panda/src/vulkandisplay/vulkanGraphicsBuffer.cxx

@@ -46,8 +46,7 @@ VulkanGraphicsBuffer::
  */
 bool VulkanGraphicsBuffer::
 get_supports_render_texture() const {
-  //TODO: add render-to-texture support.
-  return false;
+  return true;
 }
 
 /**
@@ -115,13 +114,15 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     }
   }
 
-  if (_framebuffer_size != _size) {
-    // Uh-oh, the window must have resized.  Recreate the framebuffer.
-    // Before destroying the old, make sure the queue is no longer rendering
-    // anything to it.
-    destroy_framebuffer();
-    if (!create_framebuffer()) {
-      return false;
+  {
+    CDReader cdata(_cycler);
+    if (cdata->_textures_seq != _last_textures_seq ||
+        _framebuffer_size != _size) {
+      // The buffer was resized or the attachments were changed.
+      destroy_framebuffer();
+      if (!create_framebuffer(cdata)) {
+        return false;
+      }
     }
   }
 
@@ -204,7 +205,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
   vkCmdBeginRenderPass(cmd, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
   vkgsg->_render_pass = _render_pass;
-  vkgsg->_fb_ms_count = VK_SAMPLE_COUNT_1_BIT;
+  vkgsg->_fb_config = _fb_config_id;
 
   return true;
 }
@@ -305,101 +306,12 @@ open_buffer() {
     return false;
   }
 
-  // Choose a suitable color format.  Sorted in order of preferability,
-  // preferring lower bpps over higher bpps, and preferring formats that pack
-  // bits in fewer channels (because if the user only requests red bits, they
-  // probably would prefer to maximize the amount of red bits rather than to
-  // have bits in other channels that they don't end up using)
-  static const struct {
-    int rgb, r, g, b, a;
-    bool has_float;
-    VkFormat format;
-  } formats[] = {
-    { 8,  8,  0,  0,  0, false, VK_FORMAT_R8_UNORM},
-    {16,  8,  8,  0,  0, false, VK_FORMAT_R8G8_UNORM},
-    {16,  5,  6,  5,  0, false, VK_FORMAT_R5G6B5_UNORM_PACK16},
-    {15,  5,  5,  5,  1, false, VK_FORMAT_A1R5G5B5_UNORM_PACK16},
-    {16, 16,  0,  0,  0,  true, VK_FORMAT_R16_SFLOAT},
-    {24,  8,  8,  8,  8, false, VK_FORMAT_B8G8R8A8_UNORM},
-    {32, 16, 16,  0,  0, false, VK_FORMAT_R16G16_SFLOAT},
-    {30, 10, 10, 10,  2, false, VK_FORMAT_A2B10G10R10_UNORM_PACK32},
-    {48, 16, 16, 16, 16,  true, VK_FORMAT_R16G16B16A16_SFLOAT},
-    {32, 32,  0,  0,  0,  true, VK_FORMAT_R32_SFLOAT},
-    {64, 32, 32,  0,  0,  true, VK_FORMAT_R32G32_SFLOAT},
-    {96, 32, 32, 32, 32,  true, VK_FORMAT_R32G32B32A32_SFLOAT},
-    {0}
-  };
-
-  _color_format = VK_FORMAT_UNDEFINED;
-
-  if (_fb_properties.get_srgb_color()) {
-    // This the only sRGB format.  Deal with it.
-    _color_format = VK_FORMAT_B8G8R8A8_SRGB;
-    _fb_properties.set_rgba_bits(8, 8, 8, 8);
-    _fb_properties.set_float_color(false);
-
-  } else if (_fb_properties.get_rgb_color() ||
-             _fb_properties.get_color_bits() > 0) {
-    for (int i = 0; formats[i].r; ++i) {
-      if (formats[i].r >= _fb_properties.get_red_bits() &&
-          formats[i].g >= _fb_properties.get_green_bits() &&
-          formats[i].b >= _fb_properties.get_blue_bits() &&
-          formats[i].a >= _fb_properties.get_alpha_bits() &&
-          formats[i].rgb >= _fb_properties.get_color_bits() &&
-          formats[i].has_float >= _fb_properties.get_float_color()) {
-
-        // This format meets the requirements.
-        _color_format = formats[i].format;
-        _fb_properties.set_rgba_bits(formats[i].r, formats[i].g,
-                                     formats[i].b, formats[i].a);
-        break;
-      }
-    }
-  }
-
-  // Choose a suitable depth/stencil format that satisfies the requirements.
-  VkFormatProperties fmt_props;
-  bool request_depth32 = _fb_properties.get_depth_bits() > 24 ||
-                         _fb_properties.get_float_depth();
-
-  if (_fb_properties.get_depth_bits() > 0 && _fb_properties.get_stencil_bits() > 0) {
-    // Vulkan requires support for at least of one of these two formats.
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT_S8_UINT, &fmt_props);
-    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D24_UNORM_S8_UINT, &fmt_props);
-    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-
-    if ((supports_depth32 && request_depth32) || !supports_depth24) {
-      _depth_stencil_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
-      _fb_properties.set_depth_bits(32);
-    } else {
-      _depth_stencil_format = VK_FORMAT_D24_UNORM_S8_UINT;
-      _fb_properties.set_depth_bits(24);
-    }
-    _fb_properties.set_stencil_bits(8);
+  _fb_config_id = vkgsg->choose_fb_config(_fb_config, _fb_properties);
 
+  if (_fb_properties.get_stencil_bits() > 0) {
     _depth_stencil_plane = RTP_depth_stencil;
-
-  } else if (_fb_properties.get_depth_bits() > 0) {
-    // Vulkan requires support for at least of one of these two formats.
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT, &fmt_props);
-    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_X8_D24_UNORM_PACK32, &fmt_props);
-    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-
-    if ((supports_depth32 && request_depth32) || !supports_depth24) {
-      _depth_stencil_format = VK_FORMAT_D32_SFLOAT;
-      _fb_properties.set_depth_bits(32);
-    } else {
-      _depth_stencil_format = VK_FORMAT_X8_D24_UNORM_PACK32;
-      _fb_properties.set_depth_bits(24);
-    }
-
-    _depth_stencil_plane = RTP_depth;
-
   } else {
-    _depth_stencil_format = VK_FORMAT_UNDEFINED;
-    _depth_stencil_plane = RTP_depth_stencil;
+    _depth_stencil_plane = RTP_depth;
   }
 
   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
@@ -408,7 +320,7 @@ open_buffer() {
     _size = host->get_size();
   }
 
-  return setup_render_pass() && create_framebuffer();
+  return setup_render_pass();
 }
 
 /**
@@ -461,10 +373,11 @@ setup_render_pass() {
   dependency.dstAccessMask = 0;
   dependency.dependencyFlags = 0;
 
-  if (_color_format != VK_FORMAT_UNDEFINED) {
+  bool have_color_reference = false;
+  if (!_fb_config._color_formats.empty()) {
     VkAttachmentDescription &attach = attachments[ai];
     attach.flags = 0;
-    attach.format = _color_format;
+    attach.format = _fb_config._color_formats[0];
     attach.samples = VK_SAMPLE_COUNT_1_BIT;
     attach.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
     attach.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@@ -491,12 +404,14 @@ setup_render_pass() {
                                 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
 
     color_reference.attachment = ai++;
+    have_color_reference = true;
   }
 
-  if (_depth_stencil_format != VK_FORMAT_UNDEFINED) {
+  bool have_depth_reference = false;
+  if (_fb_config._depth_format != VK_FORMAT_UNDEFINED) {
     VkAttachmentDescription &attach = attachments[ai];
     attach.flags = 0;
-    attach.format = _depth_stencil_format;
+    attach.format = _fb_config._depth_format;
     attach.samples = VK_SAMPLE_COUNT_1_BIT;
     attach.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
     attach.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@@ -528,6 +443,7 @@ setup_render_pass() {
     dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
 
     depth_reference.attachment = ai++;
+    have_depth_reference = true;
   }
 
   if (dependency.srcStageMask == 0) {
@@ -539,10 +455,10 @@ setup_render_pass() {
   subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
   subpass.inputAttachmentCount = 0;
   subpass.pInputAttachments = nullptr;
-  subpass.colorAttachmentCount = _color_format ? 1 : 0;
-  subpass.pColorAttachments = &color_reference;
+  subpass.colorAttachmentCount = have_color_reference ? 1 : 0;
+  subpass.pColorAttachments = have_color_reference ? &color_reference : nullptr;
   subpass.pResolveAttachments = nullptr;
-  subpass.pDepthStencilAttachment = _depth_stencil_format ? &depth_reference : nullptr;
+  subpass.pDepthStencilAttachment = have_depth_reference ? &depth_reference : nullptr;
   subpass.preserveAttachmentCount = 0;
   subpass.pPreserveAttachments = nullptr;
 
@@ -609,7 +525,9 @@ destroy_framebuffer() {
     } else {
       attach._tc->destroy_now(device);
     }
-    delete attach._tc;
+    if (attach._texture.is_null()) {
+      delete attach._tc;
+    }
   }
   _attachments.clear();
 
@@ -629,7 +547,7 @@ destroy_framebuffer() {
  * Creates or recreates the framebuffer.
  */
 bool VulkanGraphicsBuffer::
-create_framebuffer() {
+create_framebuffer(CDReader &cdata) {
   VulkanGraphicsPipe *vkpipe;
   VulkanGraphicsStateGuardian *vkgsg;
   DCAST_INTO_R(vkpipe, _pipe, false);
@@ -637,9 +555,28 @@ create_framebuffer() {
   VkDevice device = vkgsg->_device;
   VkResult err;
 
-  if (!create_attachment(RTP_color, _color_format) ||
-      !create_attachment(_depth_stencil_plane, _depth_stencil_format)) {
-    return false;
+  PT(Texture) color_texture;
+  PT(Texture) depth_texture;
+  for (const RenderTexture &rt : cdata->_textures) {
+    if (rt._rtm_mode == RTM_bind_or_copy || rt._rtm_mode == RTM_bind_layered) {
+      if (rt._plane == RTP_color) {
+        color_texture = rt._texture;
+      }
+      if (rt._plane == RTP_depth || rt._plane == RTP_depth_stencil) {
+        depth_texture = rt._texture;
+      }
+    }
+  }
+
+  if (!_fb_config._color_formats.empty()) {
+    if (!create_attachment(RTP_color, _fb_config._color_formats[0], color_texture)) {
+      return false;
+    }
+  }
+  if (_fb_config._depth_format != VK_FORMAT_UNDEFINED) {
+    if (!create_attachment(_depth_stencil_plane, _fb_config._depth_format, depth_texture)) {
+      return false;
+    }
   }
 
   uint32_t num_attachments = _attachments.size();
@@ -668,6 +605,7 @@ create_framebuffer() {
 
   _framebuffer_size = _size;
   _is_valid = true;
+  _last_textures_seq = cdata->_textures_seq;
   return true;
 }
 
@@ -676,7 +614,7 @@ create_framebuffer() {
  * @return Returns true on success.
  */
 bool VulkanGraphicsBuffer::
-create_attachment(RenderTexturePlane plane, VkFormat format) {
+create_attachment(RenderTexturePlane plane, VkFormat format, Texture *texture) {
   if (format == VK_FORMAT_UNDEFINED) {
     return true;
   }
@@ -693,66 +631,93 @@ create_attachment(RenderTexturePlane plane, VkFormat format) {
   extent.height = _size[1];
   extent.depth = 1;
 
-  VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
-  if (plane == RTP_depth || plane == RTP_stencil || plane == RTP_depth_stencil) {
-    usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
-  } else {
-    usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-  }
+  VulkanTextureContext *tc;
+  if (texture != nullptr) {
+    Texture::Format tex_format;
+    Texture::ComponentType tex_component_type;
+    nassertr_always(vkgsg->lookup_image_format(format, tex_format, tex_component_type), false);
 
-  VulkanTextureContext *tc = new VulkanTextureContext(vkgsg->get_prepared_objects());
-  if (!vkgsg->create_image(tc, VK_IMAGE_TYPE_2D, format, extent, 1, 1,
-                           VK_SAMPLE_COUNT_1_BIT, usage)) {
-    delete tc;
-    return false;
+    texture->set_format(tex_format);
+    texture->set_component_type(tex_component_type);
+    texture->set_render_to_texture(true);
+
+    DCAST_INTO_R(tc, texture->prepare_now(vkgsg->get_prepared_objects(), vkgsg), false);
+
+    if (tc->needs_recreation()) {
+      if (vkgsg->_frame_data != nullptr) {
+        tc->release(*vkgsg->_frame_data);
+      } else {
+        tc->release(*vkgsg->_last_frame_data);
+      }
+      if (!vkgsg->create_texture(tc)) {
+        return false;
+      }
+      tc->mark_loaded();
+    }
   }
+  else {
+    VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+    if (plane == RTP_depth || plane == RTP_stencil || plane == RTP_depth_stencil) {
+      usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+    } else {
+      usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    }
 
-  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.viewType = VK_IMAGE_VIEW_TYPE_2D;
-  view_info.format = tc->_format;
-  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;
-  view_info.subresourceRange.baseMipLevel = 0;
-  view_info.subresourceRange.levelCount = 1;
-  view_info.subresourceRange.baseArrayLayer = 0;
-  view_info.subresourceRange.layerCount = 1;
-
-  switch (plane) {
-  default:
-    view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    break;
-
-  case RTP_depth:
-    view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
-    break;
-
-  case RTP_stencil:
-    view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
-    break;
-
-  case RTP_depth_stencil:
-    view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
-    //                                      | VK_IMAGE_ASPECT_STENCIL_BIT;
-    break;
-  }
-  tc->_aspect_mask = view_info.subresourceRange.aspectMask;
-
-  VkImageView image_view;
-  VkResult err;
-  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 = new VulkanTextureContext(vkgsg->get_prepared_objects());
+    if (!vkgsg->create_image(tc, VK_IMAGE_TYPE_2D, format, extent, 1, 1,
+                             VK_SAMPLE_COUNT_1_BIT, usage)) {
+      delete tc;
+      return false;
+    }
+
+    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.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    view_info.format = tc->_format;
+    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;
+    view_info.subresourceRange.baseMipLevel = 0;
+    view_info.subresourceRange.levelCount = 1;
+    view_info.subresourceRange.baseArrayLayer = 0;
+    view_info.subresourceRange.layerCount = 1;
+
+    switch (plane) {
+    default:
+      view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+      break;
+
+    case RTP_depth:
+      view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+      break;
+
+    case RTP_stencil:
+      view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+      break;
+
+    case RTP_depth_stencil:
+      view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+      //                                      | VK_IMAGE_ASPECT_STENCIL_BIT;
+      break;
+    }
+    tc->_aspect_mask = view_info.subresourceRange.aspectMask;
+
+    VkImageView image_view;
+    VkResult err;
+    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);
   }
 
-  tc->_image_views.push_back(image_view);
-  _attachments.push_back({tc, plane});
+  _attachments.push_back({texture, tc, plane});
   return true;
 }

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

@@ -16,6 +16,7 @@
 
 #include "config_vulkandisplay.h"
 #include "graphicsBuffer.h"
+#include "vulkanGraphicsStateGuardian.h"
 
 class VulkanTextureContext;
 
@@ -46,9 +47,10 @@ protected:
   bool setup_render_pass();
 
   void destroy_framebuffer();
-  bool create_framebuffer();
+  bool create_framebuffer(CDReader &cdata);
 
-  bool create_attachment(RenderTexturePlane plane, VkFormat format);
+  bool create_attachment(RenderTexturePlane plane, VkFormat format,
+                         Texture *texture = nullptr);
 
 private:
   VkRenderPass _render_pass;
@@ -56,11 +58,14 @@ private:
   LVecBase2i _framebuffer_size;
   int _current_clear_mask;
 
-  VkFormat _color_format;
-  VkFormat _depth_stencil_format;
+  VulkanGraphicsStateGuardian::FbConfig _fb_config;
+  uint32_t _fb_config_id;
   RenderTexturePlane _depth_stencil_plane;
 
+  UpdateSeq _last_textures_seq;
+
   struct Attachment {
+    PT(Texture) _texture;
     VulkanTextureContext *_tc;
     RenderTexturePlane _plane;
   };

+ 173 - 16
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1091,13 +1091,24 @@ create_texture(VulkanTextureContext *tc) {
   VkFormatProperties fmt_props;
   vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
 
+  Texture::Format tex_format = texture->get_format();
+  bool is_depth = (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);
+
   bool supported;
   if (is_buffer) {
-    supported = (fmt_props.bufferFeatures & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) != 0
-             && (fmt_props.bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT) != 0;
+    supported = (fmt_props.bufferFeatures & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) != 0;
+    if (!is_depth && supported) {
+      supported = (fmt_props.bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT) != 0;
+    }
   } else {
-    supported = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0
-             && (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
+    supported = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
+    if (!is_depth && supported) {
+      supported = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
+    }
   }
 
   bool pack_bgr8 = false;
@@ -1165,6 +1176,7 @@ create_texture(VulkanTextureContext *tc) {
     vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, format, &fmt_props);
   }
 
+  bool render_to_texture = false;
   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;
@@ -1172,6 +1184,15 @@ create_texture(VulkanTextureContext *tc) {
       usage |= VK_IMAGE_USAGE_STORAGE_BIT;
     }
 
+    render_to_texture = texture->get_render_to_texture();
+    if (render_to_texture) {
+      if (is_depth) {
+        usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+      } else {
+        usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+      }
+    }
+
     VkImageFormatProperties img_props;
     if (vkGetPhysicalDeviceImageFormatProperties(vkpipe->_gpu, format, type,
                                                  VK_IMAGE_TILING_OPTIMAL, usage,
@@ -1260,13 +1281,9 @@ create_texture(VulkanTextureContext *tc) {
     tc->_mipmap_end = mipmap_end;
     tc->_generate_mipmaps = generate_mipmaps;
     tc->_pack_bgr8 = pack_bgr8;
+    tc->_supports_render_to_texture = render_to_texture;
 
-    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) {
+    if (is_depth) {
       tc->_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
     } else {
       tc->_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -1452,6 +1469,7 @@ create_texture(VulkanTextureContext *tc) {
     tc->_block = std::move(block);
     tc->_pack_bgr8 = pack_bgr8;
     tc->_swap_bgra8 = swap_bgra8;
+    tc->_supports_render_to_texture = render_to_texture;
   }
 
   // We can't upload it at this point because the texture lock is currently
@@ -3328,7 +3346,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
       return false;
     }
 
-    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, data_reader->get_format(), topology, 0, _fb_ms_count);
+    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, data_reader->get_format(), topology, 0, _fb_config);
     nassertr(pipeline != VK_NULL_HANDLE, false);
     _vkCmdBindPipeline(_frame_data->_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
   } else {
@@ -3394,7 +3412,7 @@ draw_patches(const GeomPrimitivePipelineReader *reader, bool force) {
   } else {
     // Bind a pipeline which has both the topology and number of patch control
     // points baked in.
-    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, _format, VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, patch_control_points, _fb_ms_count);
+    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, _format, VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, patch_control_points, _fb_config);
     nassertr(pipeline != VK_NULL_HANDLE, false);
     _vkCmdBindPipeline(_frame_data->_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
   }
@@ -3733,7 +3751,7 @@ do_draw_primitive_with_topology(const GeomPrimitivePipelineReader *reader,
     _vkCmdSetPrimitiveTopologyEXT(_frame_data->_cmd, topology);
     _vkCmdSetPrimitiveRestartEnableEXT(_frame_data->_cmd, primitive_restart_enable && reader->is_indexed());
   } else {
-    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, _format, topology, 0, _fb_ms_count);
+    VkPipeline pipeline = _current_sc->get_pipeline(this, _state_rs, _format, topology, 0, _fb_config);
     nassertr(pipeline != VK_NULL_HANDLE, false);
     _vkCmdBindPipeline(_frame_data->_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
   }
@@ -3894,6 +3912,143 @@ create_semaphore() {
   return semaphore;
 }
 
+/**
+ * Chooses a framebuffer configuration.
+ */
+uint32_t VulkanGraphicsStateGuardian::
+choose_fb_config(FbConfig &out, FrameBufferProperties &props,
+                 VkFormat preferred_format) {
+  VulkanGraphicsPipe *vkpipe;
+  DCAST_INTO_R(vkpipe, get_pipe(), 0);
+
+  static const struct {
+    int rgb, r, g, b, a;
+    bool has_float;
+    VkFormat format;
+  } formats[] = {
+    { 8,  8,  0,  0,  0, false, VK_FORMAT_R8_UNORM},
+    {16,  8,  8,  0,  0, false, VK_FORMAT_R8G8_UNORM},
+    {16,  5,  6,  5,  0, false, VK_FORMAT_R5G6B5_UNORM_PACK16},
+    {15,  5,  5,  5,  1, false, VK_FORMAT_A1R5G5B5_UNORM_PACK16},
+    {16, 16,  0,  0,  0,  true, VK_FORMAT_R16_SFLOAT},
+    {24,  8,  8,  8,  8, false, VK_FORMAT_B8G8R8A8_UNORM},
+    {32, 16, 16,  0,  0, false, VK_FORMAT_R16G16_SFLOAT},
+    {30, 10, 10, 10,  2, false, VK_FORMAT_A2B10G10R10_UNORM_PACK32},
+    {48, 16, 16, 16, 16,  true, VK_FORMAT_R16G16B16A16_SFLOAT},
+    {32, 32,  0,  0,  0,  true, VK_FORMAT_R32_SFLOAT},
+    {64, 32, 32,  0,  0,  true, VK_FORMAT_R32G32_SFLOAT},
+    {96, 32, 32, 32, 32,  true, VK_FORMAT_R32G32B32A32_SFLOAT},
+    {0}
+  };
+
+  if (preferred_format != VK_FORMAT_UNDEFINED) {
+    // Caller's responsibility to modify props.
+    out._color_formats.push_back(preferred_format);
+  }
+  else if (props.get_srgb_color()) {
+    // This the only sRGB format.  Deal with it.
+    out._color_formats.push_back(VK_FORMAT_B8G8R8A8_SRGB);
+    props.set_rgba_bits(8, 8, 8, 8);
+    props.set_float_color(false);
+  }
+  else if (props.get_rgb_color() || props.get_color_bits() > 0) {
+    for (int i = 0; formats[i].r; ++i) {
+      if (formats[i].r >= props.get_red_bits() &&
+          formats[i].g >= props.get_green_bits() &&
+          formats[i].b >= props.get_blue_bits() &&
+          formats[i].a >= props.get_alpha_bits() &&
+          formats[i].rgb >= props.get_color_bits() &&
+          formats[i].has_float >= props.get_float_color()) {
+
+        // This format meets the requirements.
+        out._color_formats.push_back(VK_FORMAT_B8G8R8A8_SRGB);
+        props.set_rgba_bits(formats[i].r, formats[i].g,
+                            formats[i].b, formats[i].a);
+        break;
+      }
+    }
+  }
+
+  // Choose a suitable depth/stencil format that satisfies the requirements.
+  VkFormatProperties fmt_props;
+  bool request_depth32 = props.get_depth_bits() > 24 ||
+                         props.get_float_depth();
+
+  if (props.get_depth_bits() > 0 && props.get_stencil_bits() > 0) {
+    // Vulkan requires support for at least of one of these two formats.
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT_S8_UINT, &fmt_props);
+    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D24_UNORM_S8_UINT, &fmt_props);
+    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+
+    if ((supports_depth32 && request_depth32) || !supports_depth24) {
+      out._depth_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
+      props.set_depth_bits(32);
+    } else {
+      out._depth_format = VK_FORMAT_D24_UNORM_S8_UINT;
+      props.set_depth_bits(24);
+    }
+    props.set_stencil_bits(8);
+  }
+  else if (props.get_depth_bits() > 0) {
+    // Vulkan requires support for at least of one of these two formats.
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT, &fmt_props);
+    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_X8_D24_UNORM_PACK32, &fmt_props);
+    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
+
+    if ((supports_depth32 && request_depth32) || !supports_depth24) {
+      out._depth_format = VK_FORMAT_D32_SFLOAT;
+      props.set_depth_bits(32);
+    } else {
+      out._depth_format = VK_FORMAT_X8_D24_UNORM_PACK32;
+      props.set_depth_bits(24);
+    }
+  }
+  else {
+    out._depth_format = VK_FORMAT_UNDEFINED;
+  }
+
+  // Choose a sample count.
+  if (props.get_multisamples() > 1) {
+    const VkPhysicalDeviceLimits &limits = vkpipe->_gpu_properties.limits;
+
+    VkSampleCountFlags supported = limits.framebufferColorSampleCounts;
+    if (props.get_depth_bits() > 0) {
+      supported &= limits.framebufferDepthSampleCounts;
+    }
+    if (props.get_stencil_bits() > 0) {
+      supported &= limits.framebufferStencilSampleCounts;
+    }
+
+    // Round up requested bits to next power of two, and flood down.
+    VkSampleCountFlags accepted = ::flood_bits_down((uint32_t)(props.get_multisamples() - 1) << 1u);
+
+    // Select the highest overlapping bit.
+    out._sample_count = (VkSampleCountFlagBits)(1u << ::get_highest_on_bit(accepted & supported));
+  } else {
+    out._sample_count = VK_SAMPLE_COUNT_1_BIT;
+  }
+  props.set_multisamples(out._sample_count);
+
+  // Make a unique identifier for this fb config, for easy hashing.
+  uint32_t id = 0;
+  while (id < _fb_configs.size()) {
+    if (out == _fb_configs[id]) {
+      break;
+    }
+    ++id;
+  }
+  if (id == _fb_configs.size()) {
+    _fb_configs.push_back(out);
+  }
+  if (vulkandisplay_cat.is_debug()) {
+    vulkandisplay_cat.debug()
+      << "Framebuffer config " << id << ": " << props << "\n";
+  }
+  return id;
+}
+
 /**
  * Creates a VkPipeline for the given RenderState+GeomVertexFormat combination.
  */
@@ -3907,7 +4062,7 @@ make_pipeline(VulkanShaderContext *sc,
       << " format=" << key._format
       << " topology=" << key._topology
       << " patch_control_points=" << key._patch_control_points
-      << " multisamples=" << key._multisamples
+      << " fb_config=" << key._fb_config
       << " color_type=" << key._color_type
       << " render_mode_attrib=" << key._render_mode_attrib
       << " cull_face_mode=" << key._cull_face_mode
@@ -3921,6 +4076,8 @@ make_pipeline(VulkanShaderContext *sc,
       << "\n";
   }
 
+  FbConfig fb_config = _fb_configs[key._fb_config];
+
   PStatTimer timer(_make_pipeline_pcollector);
 
   VkPipelineShaderStageCreateInfo stages[(size_t)Shader::Stage::COMPUTE + 1];
@@ -3939,7 +4096,7 @@ make_pipeline(VulkanShaderContext *sc,
   float alpha_test_ref;
 
   RenderAttrib::PandaCompareFunc alpha_test_mode = RenderAttrib::M_none;
-  if (key._alpha_test_attrib != nullptr) {
+  if (key._alpha_test_attrib != nullptr && !fb_config._color_formats.empty()) {
     alpha_test_mode = key._alpha_test_attrib->get_mode();
     if (alpha_test_mode != RenderAttrib::M_never) {
       alpha_test_ref = key._alpha_test_attrib->get_reference_alpha();
@@ -4255,7 +4412,7 @@ make_pipeline(VulkanShaderContext *sc,
   ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
   ms_info.pNext = nullptr;
   ms_info.flags = 0;
-  ms_info.rasterizationSamples = key._multisamples;
+  ms_info.rasterizationSamples = fb_config._sample_count;
   ms_info.sampleShadingEnable = VK_FALSE;
   ms_info.minSampleShading = 0.0;
   ms_info.pSampleMask = nullptr;

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

@@ -21,6 +21,7 @@
 #include "circularAllocator.h"
 
 class VulkanBufferContext;
+class VulkanGraphicsPipe;
 class VulkanIndexBufferContext;
 class VulkanSamplerContext;
 class VulkanShaderContext;
@@ -166,6 +167,10 @@ public:
 
   VkSemaphore create_semaphore();
 
+  struct FbConfig;
+  uint32_t choose_fb_config(FbConfig &out, FrameBufferProperties &props,
+                            VkFormat preferred_format = VK_FORMAT_UNDEFINED);
+
   VkPipeline make_pipeline(VulkanShaderContext *sc,
                            const VulkanShaderContext::PipelineKey &key);
   VkPipeline make_compute_pipeline(VulkanShaderContext *sc);
@@ -206,6 +211,28 @@ public:
   uint32_t _graphics_queue_family_index;
   PT(Texture) _white_texture;
 
+  struct FbConfig {
+    small_vector<VkFormat, 1> _color_formats;
+    VkFormat _depth_format = VK_FORMAT_UNDEFINED;
+    VkFormat _stencil_format = VK_FORMAT_UNDEFINED;
+    VkSampleCountFlagBits _sample_count = VK_SAMPLE_COUNT_1_BIT;
+
+    bool operator == (const FbConfig &other) const {
+      if (_color_formats.size() != other._color_formats.size() ||
+          _depth_format != other._depth_format ||
+          _stencil_format != other._stencil_format ||
+          _sample_count != other._sample_count) {
+        return false;
+      }
+      for (size_t i = 0; i < _color_formats.size(); ++i) {
+        if (_color_formats[i] != other._color_formats[i]) {
+          return false;
+        }
+      }
+      return true;
+    }
+  };
+
 private:
   VkQueue _queue = VK_NULL_HANDLE;
   VkQueue _dma_queue = VK_NULL_HANDLE;
@@ -242,7 +269,8 @@ private:
   VkRenderPass _render_pass = VK_NULL_HANDLE;
   VulkanTextureContext *_fb_color_tc = nullptr;
   VulkanTextureContext *_fb_depth_tc = nullptr;
-  VkSampleCountFlagBits _fb_ms_count = VK_SAMPLE_COUNT_1_BIT;
+  uint32_t _fb_config = 0;
+  pvector<FbConfig> _fb_configs;
 
   // Static "null" vertex buffer if nullDescriptor is not supported.
   VkBuffer _null_vertex_buffer = VK_NULL_HANDLE;

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

@@ -260,7 +260,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   vkgsg->_render_pass = _render_pass;
   vkgsg->_fb_color_tc = color_tc;
   vkgsg->_fb_depth_tc = _depth_stencil_tc;
-  vkgsg->_fb_ms_count = _ms_count;
+  vkgsg->_fb_config = _fb_config_id;
   return true;
 }
 
@@ -540,27 +540,6 @@ open_window() {
     return false;
   }
 
-  // Choose a sample count.
-  if (_fb_properties.get_multisamples() > 1) {
-    const VkPhysicalDeviceLimits &limits = vkpipe->_gpu_properties.limits;
-
-    VkSampleCountFlags supported = limits.framebufferColorSampleCounts;
-    if (_fb_properties.get_depth_bits() > 0) {
-      supported &= limits.framebufferDepthSampleCounts;
-    }
-    if (_fb_properties.get_stencil_bits() > 0) {
-      supported &= limits.framebufferStencilSampleCounts;
-    }
-
-    // Round up requested bits to next power of two, and flood down.
-    VkSampleCountFlags accepted = ::flood_bits_down((uint32_t)(_fb_properties.get_multisamples() - 1) << 1u);
-
-    // Select the highest overlapping bit.
-    _ms_count = (VkSampleCountFlagBits)(1u << ::get_highest_on_bit(accepted & supported));
-  } else {
-    _ms_count = VK_SAMPLE_COUNT_1_BIT;
-  }
-
   // Make sure we have a GSG, which manages a VkDevice.
   VulkanGraphicsStateGuardian *vkgsg;
   uint32_t queue_family_index = 0;
@@ -610,13 +589,8 @@ open_window() {
   // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the
   // surface has no preferred format.  Otherwise, at least one supported
   // format will be returned.
-  //TODO: add more logic for picking a suitable format.
   if (num_formats == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
-    if (_fb_properties.get_srgb_color()) {
-      _surface_format.format = VK_FORMAT_B8G8R8A8_SRGB;
-    } else {
-      _surface_format.format = VK_FORMAT_B8G8R8A8_UNORM;
-    }
+    _surface_format.format = VK_FORMAT_UNDEFINED;
     _surface_format.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
   }
   else {
@@ -644,55 +618,20 @@ open_window() {
       }
     }
   }
+  _fb_config_id = vkgsg->choose_fb_config(_fb_config, _fb_properties,
+                                          _surface_format.format);
 
-  // Choose a suitable depth/stencil format that satisfies the requirements.
-  VkFormatProperties fmt_props;
-  bool request_depth32 = _fb_properties.get_depth_bits() > 24 ||
-                         _fb_properties.get_float_depth();
-
-  if (_fb_properties.get_depth_bits() > 0 && _fb_properties.get_stencil_bits() > 0) {
-    // Vulkan requires support for at least of one of these two formats.
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT_S8_UINT, &fmt_props);
-    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D24_UNORM_S8_UINT, &fmt_props);
-    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-
-    if ((supports_depth32 && request_depth32) || !supports_depth24) {
-      _depth_stencil_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
-      _fb_properties.set_depth_bits(32);
-    } else {
-      _depth_stencil_format = VK_FORMAT_D24_UNORM_S8_UINT;
-      _fb_properties.set_depth_bits(24);
-    }
-    _fb_properties.set_stencil_bits(8);
-
+  if (_fb_properties.get_stencil_bits() > 0) {
     _depth_stencil_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT |
                                  VK_IMAGE_ASPECT_STENCIL_BIT;
-
-  } else if (_fb_properties.get_depth_bits() > 0) {
-    // Vulkan requires support for at least of one of these two formats.
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_D32_SFLOAT, &fmt_props);
-    bool supports_depth32 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-    vkGetPhysicalDeviceFormatProperties(vkpipe->_gpu, VK_FORMAT_X8_D24_UNORM_PACK32, &fmt_props);
-    bool supports_depth24 = (fmt_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
-
-    if ((supports_depth32 && request_depth32) || !supports_depth24) {
-      _depth_stencil_format = VK_FORMAT_D32_SFLOAT;
-      _fb_properties.set_depth_bits(32);
-    } else {
-      _depth_stencil_format = VK_FORMAT_X8_D24_UNORM_PACK32;
-      _fb_properties.set_depth_bits(24);
-    }
-
+  }
+  else if (_fb_properties.get_depth_bits() > 0) {
     _depth_stencil_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
-
-  } else {
-    _depth_stencil_format = VK_FORMAT_UNDEFINED;
+  }
+  else {
     _depth_stencil_aspect_mask = 0;
   }
 
-  _fb_properties.set_multisamples(_ms_count);
-
   // Don't create the swapchain yet if we haven't yet gotten the configure
   // notify event, since we don't know the final size yet.
 #ifdef HAVE_X11
@@ -757,7 +696,7 @@ setup_render_pass() {
   VkAttachmentDescription attachments[3];
   attachments[0].flags = 0;
   attachments[0].format = _surface_format.format;
-  attachments[0].samples = _ms_count;
+  attachments[0].samples = _fb_config._sample_count;
   attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
   attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
@@ -765,7 +704,7 @@ setup_render_pass() {
   attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
   attachments[0].finalLayout = _final_layout;
 
-  if (_ms_count > VK_SAMPLE_COUNT_1_BIT) {
+  if (_fb_config._sample_count > VK_SAMPLE_COUNT_1_BIT) {
     // If multisampling, we don't present this, but resolve it.
     attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
   }
@@ -792,10 +731,10 @@ setup_render_pass() {
   dependency.dependencyFlags = 0;
 
   size_t i = 1;
-  if (_depth_stencil_format) {
+  if (_fb_config._depth_format) {
     attachments[i].flags = 0;
-    attachments[i].format = _depth_stencil_format;
-    attachments[i].samples = _ms_count;
+    attachments[i].format = _fb_config._depth_format;
+    attachments[i].samples = _fb_config._sample_count;
     attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
     attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
     attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
@@ -828,7 +767,7 @@ setup_render_pass() {
   }
 
   // Also create an attachment reference for the resolve target.
-  if (_ms_count > VK_SAMPLE_COUNT_1_BIT) {
+  if (_fb_config._sample_count > VK_SAMPLE_COUNT_1_BIT) {
     attachments[i].flags = 0;
     attachments[i].format = _surface_format.format;
     attachments[i].samples = VK_SAMPLE_COUNT_1_BIT;
@@ -1077,12 +1016,12 @@ create_swapchain() {
   // Now create a depth image.
   _depth_stencil_tc = nullptr;
   VkImageView depth_stencil_view = VK_NULL_HANDLE;
-  if (_depth_stencil_format != VK_FORMAT_UNDEFINED) {
+  if (_fb_config._depth_format != VK_FORMAT_UNDEFINED) {
     _depth_stencil_tc = new VulkanTextureContext(pgo);
     _depth_stencil_tc->_aspect_mask = _depth_stencil_aspect_mask;
 
     if (!vkgsg->create_image(_depth_stencil_tc, VK_IMAGE_TYPE_2D,
-                             _depth_stencil_format, extent, 1, 1, _ms_count,
+                             _fb_config._depth_format, extent, 1, 1, _fb_config._sample_count,
                              VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
       delete _depth_stencil_tc;
       _depth_stencil_tc = nullptr;
@@ -1095,7 +1034,7 @@ create_swapchain() {
     view_info.flags = 0;
     view_info.image = _depth_stencil_tc->_image;
     view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-    view_info.format = _depth_stencil_format;
+    view_info.format = _fb_config._depth_format;
     view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
     view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
     view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
@@ -1122,11 +1061,11 @@ create_swapchain() {
   // Create a multisample color image.
   _ms_color_tc = nullptr;
   VkImageView ms_color_view = VK_NULL_HANDLE;
-  if (_ms_count != VK_SAMPLE_COUNT_1_BIT) {
+  if (_fb_config._sample_count != VK_SAMPLE_COUNT_1_BIT) {
     _ms_color_tc = new VulkanTextureContext(pgo);
     if (!vkgsg->create_image(_ms_color_tc, VK_IMAGE_TYPE_2D,
                              swapchain_info.imageFormat, extent, 1, 1,
-                             _ms_count, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
+                             _fb_config._sample_count, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
       delete _ms_color_tc;
       _ms_color_tc = nullptr;
       return false;

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

@@ -15,6 +15,7 @@
 #define VULKANGRAPHICSWINDOW_H
 
 #include "config_vulkandisplay.h"
+#include "vulkanGraphicsStateGuardian.h"
 
 #ifdef _WIN32
 #include "winGraphicsWindow.h"
@@ -75,6 +76,8 @@ private:
 
   LVecBase2i _swapchain_size;
   VkSurfaceFormatKHR _surface_format;
+  VulkanGraphicsStateGuardian::FbConfig _fb_config;
+  uint32_t _fb_config_id;
 
   struct SwapBuffer {
     VulkanTextureContext *_tc;
@@ -87,10 +90,7 @@ private:
   VkImageLayout _final_layout;
 
   VulkanTextureContext *_ms_color_tc = nullptr;
-  VkSampleCountFlagBits _ms_count = VK_SAMPLE_COUNT_1_BIT;
-
   VulkanTextureContext *_depth_stencil_tc = nullptr;
-  VkFormat _depth_stencil_format;
   VkImageAspectFlags _depth_stencil_aspect_mask;
 
 public:

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

@@ -48,7 +48,7 @@ operator ==(const PipelineKey &other) const {
   return _format == other._format
       && _topology == other._topology
       && _patch_control_points == other._patch_control_points
-      && _multisamples == other._multisamples
+      && _fb_config == other._fb_config
       && _color_type == other._color_type
       && _render_mode_attrib == other._render_mode_attrib
       && _cull_face_mode == other._cull_face_mode
@@ -75,8 +75,8 @@ operator < (const PipelineKey &other) const {
   if (_patch_control_points != other._patch_control_points) {
     return _patch_control_points < other._patch_control_points;
   }
-  if (_multisamples != other._multisamples) {
-    return _multisamples < other._multisamples;
+  if (_fb_config != other._fb_config) {
+    return _fb_config < other._fb_config;
   }
   if (_color_type != other._color_type) {
     return _color_type < other._color_type;

+ 2 - 2
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -891,12 +891,12 @@ update_dynamic_uniforms(VulkanGraphicsStateGuardian *gsg, int altered) {
 VkPipeline VulkanShaderContext::
 get_pipeline(VulkanGraphicsStateGuardian *gsg, const RenderState *state,
              const GeomVertexFormat *format, VkPrimitiveTopology topology,
-             uint32_t patch_control_points, VkSampleCountFlagBits multisamples) {
+             uint32_t patch_control_points, uint32_t fb_config) {
   PipelineKey key;
   key._format = format;
   key._topology = topology;
   key._patch_control_points = patch_control_points;
-  key._multisamples = multisamples;
+  key._fb_config = fb_config;
 
   const ColorAttrib *color_attr;
   state->get_attrib_def(color_attr);

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

@@ -75,7 +75,7 @@ public:
                           const GeomVertexFormat *format,
                           VkPrimitiveTopology topology,
                           uint32_t patch_control_points,
-                          VkSampleCountFlagBits multisamples);
+                          uint32_t fb_config);
   VkPipeline get_compute_pipeline(VulkanGraphicsStateGuardian *gsg);
 
   /**
@@ -89,7 +89,7 @@ public:
     CPT(GeomVertexFormat) _format;
     VkPrimitiveTopology _topology;
     uint32_t _patch_control_points;
-    VkSampleCountFlagBits _multisamples;
+    uint32_t _fb_config;
 
     ColorAttrib::Type _color_type;
     CPT(RenderModeAttrib) _render_mode_attrib;

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

@@ -23,6 +23,10 @@ TypeHandle VulkanTextureContext::_type_handle;
 bool VulkanTextureContext::
 needs_recreation() const {
   Texture *tex = get_texture();
+  if (tex->get_render_to_texture() && !_supports_render_to_texture) {
+    return true;
+  }
+
   int num_views = tex->get_num_views();
 
   VkExtent3D extent;

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

@@ -61,6 +61,7 @@ public:
   bool _generate_mipmaps = false;
   bool _pack_bgr8 = false;
   bool _swap_bgra8 = false;
+  bool _supports_render_to_texture = 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.