[vk] remove usage of imageless framebuffers

Imageless framebuffers are not needed, we know which views will be attached to the render pass already. Even if we wanted to more aggressively cache imageless framebuffers that wouldn't be possible since they still require specifiying view usage, texture usage and view formats.

Removing usage of imageless framebuffers simplifies the code substantially.
This commit is contained in:
teoxoy 2025-04-23 15:10:52 +02:00 committed by Teodor Tanasoaia
parent 2bb8325f85
commit d4b46d6099
6 changed files with 22 additions and 199 deletions

View File

@ -44,9 +44,6 @@ pub struct PhysicalDeviceFeatures {
pub(super) descriptor_indexing:
Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT<'static>>,
/// Features provided by `VK_KHR_imageless_framebuffer`, promoted to Vulkan 1.2.
imageless_framebuffer: Option<vk::PhysicalDeviceImagelessFramebufferFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_timeline_semaphore`, promoted to Vulkan 1.2
timeline_semaphore: Option<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR<'static>>,
@ -138,9 +135,6 @@ impl PhysicalDeviceFeatures {
if let Some(ref mut feature) = self.descriptor_indexing {
info = info.push_next(feature);
}
if let Some(ref mut feature) = self.imageless_framebuffer {
info = info.push_next(feature);
}
if let Some(ref mut feature) = self.timeline_semaphore {
info = info.push_next(feature);
}
@ -329,16 +323,6 @@ impl PhysicalDeviceFeatures {
} else {
None
},
imageless_framebuffer: if device_api_version >= vk::API_VERSION_1_2
|| enabled_extensions.contains(&khr::imageless_framebuffer::NAME)
{
Some(
vk::PhysicalDeviceImagelessFramebufferFeaturesKHR::default()
.imageless_framebuffer(private_caps.imageless_framebuffers),
)
} else {
None
},
timeline_semaphore: if device_api_version >= vk::API_VERSION_1_2
|| enabled_extensions.contains(&khr::timeline_semaphore::NAME)
{
@ -976,15 +960,6 @@ impl PhysicalDeviceProperties {
extensions.push(khr::image_format_list::NAME);
}
// Optional `VK_KHR_imageless_framebuffer`
if self.supports_extension(khr::imageless_framebuffer::NAME) {
extensions.push(khr::imageless_framebuffer::NAME);
// Require `VK_KHR_maintenance2` due to it being a dependency
if self.device_api_version < vk::API_VERSION_1_1 {
extensions.push(khr::maintenance2::NAME);
}
}
// Optional `VK_KHR_driver_properties`
if self.supports_extension(khr::driver_properties::NAME) {
extensions.push(khr::driver_properties::NAME);
@ -1420,15 +1395,6 @@ impl super::InstanceShared {
features2 = features2.push_next(next);
}
// `VK_KHR_imageless_framebuffer` is promoted to 1.2, but has no
// changes, so we can keep using the extension unconditionally.
if capabilities.supports_extension(khr::imageless_framebuffer::NAME) {
let next = features
.imageless_framebuffer
.insert(vk::PhysicalDeviceImagelessFramebufferFeaturesKHR::default());
features2 = features2.push_next(next);
}
// `VK_KHR_timeline_semaphore` is promoted to 1.2, but has no
// changes, so we can keep using the extension unconditionally.
if capabilities.supports_extension(khr::timeline_semaphore::NAME) {
@ -1655,12 +1621,6 @@ impl super::Instance {
}
let private_caps = super::PrivateCapabilities {
imageless_framebuffers: match phd_features.imageless_framebuffer {
Some(features) => features.imageless_framebuffer == vk::TRUE,
None => phd_features
.imageless_framebuffer
.is_some_and(|ext| ext.imageless_framebuffer != 0),
},
image_view_usage: phd_capabilities.device_api_version >= vk::API_VERSION_1_1
|| phd_capabilities.supports_extension(khr::maintenance2::NAME),
timeline_semaphores: match phd_features.timeline_semaphore {

View File

@ -714,7 +714,6 @@ impl crate::CommandEncoder for super::CommandEncoder {
) -> Result<(), crate::DeviceError> {
let mut vk_clear_values =
ArrayVec::<vk::ClearValue, { super::MAX_TOTAL_ATTACHMENTS }>::new();
let mut vk_image_views = ArrayVec::<vk::ImageView, { super::MAX_TOTAL_ATTACHMENTS }>::new();
let mut rp_key = super::RenderPassKey::default();
let mut fb_key = super::FramebufferKey {
attachments: ArrayVec::default(),
@ -728,7 +727,6 @@ impl crate::CommandEncoder for super::CommandEncoder {
vk_clear_values.push(vk::ClearValue {
color: unsafe { cat.make_vk_clear_color() },
});
vk_image_views.push(cat.target.view.raw);
let color = super::ColorAttachmentKey {
base: cat.target.make_attachment_key(cat.ops, caps),
resolve: cat.resolve_target.as_ref().map(|target| {
@ -737,11 +735,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
};
rp_key.colors.push(Some(color));
fb_key.attachments.push(cat.target.view.attachment.clone());
fb_key.attachments.push(cat.target.view.raw);
if let Some(ref at) = cat.resolve_target {
vk_clear_values.push(unsafe { mem::zeroed() });
vk_image_views.push(at.view.raw);
fb_key.attachments.push(at.view.attachment.clone());
fb_key.attachments.push(at.view.raw);
}
// Assert this attachment is valid for the detected multiview, as a sanity check
@ -763,12 +760,11 @@ impl crate::CommandEncoder for super::CommandEncoder {
stencil: ds.clear_value.1,
},
});
vk_image_views.push(ds.target.view.raw);
rp_key.depth_stencil = Some(super::DepthStencilAttachmentKey {
base: ds.target.make_attachment_key(ds.depth_ops, caps),
stencil_ops: ds.stencil_ops,
});
fb_key.attachments.push(ds.target.view.attachment.clone());
fb_key.attachments.push(ds.target.view.raw);
// Assert this attachment is valid for the detected multiview, as a sanity check
// The driver crash for this is really bad on AMD, so the check is worth it
@ -801,19 +797,11 @@ impl crate::CommandEncoder for super::CommandEncoder {
.make_framebuffer(fb_key, raw_pass, desc.label)
.unwrap();
let mut vk_info = vk::RenderPassBeginInfo::default()
let vk_info = vk::RenderPassBeginInfo::default()
.render_pass(raw_pass)
.render_area(render_area)
.clear_values(&vk_clear_values)
.framebuffer(raw_framebuffer);
let mut vk_attachment_info = if caps.imageless_framebuffers {
Some(vk::RenderPassAttachmentBeginInfo::default().attachments(&vk_image_views))
} else {
None
};
if let Some(attachment_info) = vk_attachment_info.as_mut() {
vk_info = vk_info.push_next(attachment_info);
}
if let Some(label) = desc.label {
unsafe { self.begin_debug_marker(label) };

View File

@ -188,8 +188,8 @@ impl crate::Attachment<'_, super::TextureView> {
caps: &super::PrivateCapabilities,
) -> super::AttachmentKey {
super::AttachmentKey {
format: caps.map_texture_format(self.view.attachment.view_format),
layout: derive_image_layout(self.usage, self.view.attachment.view_format),
format: caps.map_texture_format(self.view.view_format),
layout: derive_image_layout(self.usage, self.view.view_format),
ops,
}
}
@ -201,7 +201,6 @@ impl crate::ColorAttachment<'_, super::TextureView> {
match self
.target
.view
.attachment
.view_format
.sample_type(None, None)
.unwrap()

View File

@ -211,72 +211,18 @@ impl super::DeviceShared {
Ok(match self.framebuffers.lock().entry(key) {
Entry::Occupied(e) => *e.get(),
Entry::Vacant(e) => {
let vk_views = e
.key()
.attachments
.iter()
.map(|at| at.raw)
.collect::<ArrayVec<_, { super::MAX_TOTAL_ATTACHMENTS }>>();
let vk_view_formats = e
.key()
.attachments
.iter()
.map(|at| self.private_caps.map_texture_format(at.view_format))
.collect::<ArrayVec<_, { super::MAX_TOTAL_ATTACHMENTS }>>();
let vk_view_formats_list = e
.key()
.attachments
.iter()
.map(|at| at.raw_view_formats.clone())
.collect::<ArrayVec<_, { super::MAX_TOTAL_ATTACHMENTS }>>();
let vk_image_infos = e
.key()
.attachments
.iter()
.enumerate()
.map(|(i, at)| {
let mut info = vk::FramebufferAttachmentImageInfo::default()
.usage(conv::map_texture_usage(at.view_usage))
.flags(at.raw_image_flags)
.width(e.key().extent.width)
.height(e.key().extent.height)
.layer_count(e.key().extent.depth_or_array_layers);
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#VUID-VkRenderPassBeginInfo-framebuffer-03214
if vk_view_formats_list[i].is_empty() {
info = info.view_formats(&vk_view_formats[i..i + 1]);
} else {
info = info.view_formats(&vk_view_formats_list[i]);
};
info
})
.collect::<ArrayVec<_, { super::MAX_TOTAL_ATTACHMENTS }>>();
let mut vk_attachment_info = vk::FramebufferAttachmentsCreateInfo::default()
.attachment_image_infos(&vk_image_infos);
let mut vk_info = vk::FramebufferCreateInfo::default()
let vk_info = vk::FramebufferCreateInfo::default()
.render_pass(raw_pass)
.width(e.key().extent.width)
.height(e.key().extent.height)
.layers(e.key().extent.depth_or_array_layers);
.layers(e.key().extent.depth_or_array_layers)
.attachments(&e.key().attachments);
if self.private_caps.imageless_framebuffers {
//TODO: https://github.com/MaikKlein/ash/issues/450
vk_info = vk_info
.flags(vk::FramebufferCreateFlags::IMAGELESS_KHR)
.push_next(&mut vk_attachment_info);
vk_info.attachment_count = e.key().attachments.len() as u32;
} else {
vk_info = vk_info.attachments(&vk_views);
let raw = unsafe { self.raw.create_framebuffer(&vk_info, None).unwrap() };
if let Some(label) = pass_label {
unsafe { self.set_object_name(raw, label) };
}
*e.insert(unsafe {
let raw = self.raw.create_framebuffer(&vk_info, None).unwrap();
if let Some(label) = pass_label {
self.set_object_name(raw, label);
}
raw
})
*e.insert(raw)
}
})
}
@ -558,7 +504,6 @@ impl super::Device {
let original_format = self.shared.private_caps.map_texture_format(config.format);
let mut raw_flags = vk::SwapchainCreateFlagsKHR::empty();
let mut raw_view_formats: Vec<vk::Format> = vec![];
let mut wgt_view_formats = vec![];
if !config.view_formats.is_empty() {
raw_flags |= vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT;
raw_view_formats = config
@ -567,9 +512,6 @@ impl super::Device {
.map(|f| self.shared.private_caps.map_texture_format(*f))
.collect();
raw_view_formats.push(original_format);
wgt_view_formats.clone_from(&config.view_formats);
wgt_view_formats.push(config.format);
}
let mut info = vk::SwapchainCreateInfoKHR::default()
@ -639,12 +581,10 @@ impl super::Device {
Ok(super::Swapchain {
raw,
raw_flags,
functor,
device: Arc::clone(&self.shared),
images,
config: config.clone(),
view_formats: wgt_view_formats,
surface_semaphores,
next_semaphore_index: 0,
next_present_time: None,
@ -686,11 +626,8 @@ impl super::Device {
drop_guard,
external_memory: None,
block: None,
usage: desc.usage,
format: desc.format,
raw_flags: vk::ImageCreateFlags::empty(),
copy_size: desc.copy_extent(),
view_formats,
}
}
@ -734,11 +671,8 @@ impl super::Device {
let original_format = self.shared.private_caps.map_texture_format(desc.format);
let mut vk_view_formats = vec![];
let mut wgt_view_formats = vec![];
if !desc.view_formats.is_empty() {
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
wgt_view_formats.clone_from(&desc.view_formats);
wgt_view_formats.push(desc.format);
if self.shared.private_caps.image_format_list {
vk_view_formats = desc
@ -788,8 +722,6 @@ impl super::Device {
raw,
requirements: req,
copy_size,
view_formats: wgt_view_formats,
raw_flags,
})
}
@ -851,11 +783,8 @@ impl super::Device {
drop_guard: None,
external_memory: Some(memory),
block: None,
usage: desc.usage,
format: desc.format,
raw_flags: image.raw_flags,
copy_size: image.copy_size,
view_formats: image.view_formats,
})
}
@ -1326,11 +1255,8 @@ impl crate::Device for super::Device {
drop_guard: None,
external_memory: None,
block: Some(block),
usage: desc.usage,
format: desc.format,
raw_flags: image.raw_flags,
copy_size: image.copy_size,
view_formats: image.view_formats,
})
}
unsafe fn destroy_texture(&self, texture: super::Texture) {
@ -1369,14 +1295,11 @@ impl crate::Device for super::Device {
NonZeroU32::new(subresource_range.layer_count).expect("Unexpected zero layer count");
let mut image_view_info;
let view_usage = if self.shared.private_caps.image_view_usage && !desc.usage.is_empty() {
if self.shared.private_caps.image_view_usage && !desc.usage.is_empty() {
image_view_info =
vk::ImageViewUsageCreateInfo::default().usage(conv::map_texture_usage(desc.usage));
vk_info = vk_info.push_next(&mut image_view_info);
desc.usage
} else {
texture.usage
};
}
let raw = unsafe { self.shared.raw.create_image_view(&vk_info, None) }
.map_err(super::map_host_device_oom_and_ioca_err)?;
@ -1385,39 +1308,23 @@ impl crate::Device for super::Device {
unsafe { self.shared.set_object_name(raw, label) };
}
let attachment = super::FramebufferAttachment {
raw: if self.shared.private_caps.imageless_framebuffers {
vk::ImageView::null()
} else {
raw
},
raw_image_flags: texture.raw_flags,
view_usage,
view_format: desc.format,
raw_view_formats: texture
.view_formats
.iter()
.map(|tf| self.shared.private_caps.map_texture_format(*tf))
.collect(),
};
self.counters.texture_views.add(1);
Ok(super::TextureView {
raw,
layers,
attachment,
view_format: desc.format,
})
}
unsafe fn destroy_texture_view(&self, view: super::TextureView) {
if !self.shared.private_caps.imageless_framebuffers {
{
let mut fbuf_lock = self.shared.framebuffers.lock();
for (key, &raw_fbuf) in fbuf_lock.iter() {
if key.attachments.iter().any(|at| at.raw == view.raw) {
if key.attachments.iter().any(|at_view| *at_view == view.raw) {
unsafe { self.shared.raw.destroy_framebuffer(raw_fbuf, None) };
}
}
fbuf_lock.retain(|key, _| !key.attachments.iter().any(|at| at.raw == view.raw));
fbuf_lock.retain(|key, _| !key.attachments.iter().any(|at_view| *at_view == view.raw));
}
unsafe { self.shared.raw.destroy_image_view(view.raw, None) };
@ -1855,7 +1762,7 @@ impl crate::Device for super::Device {
|binding| {
let layout = conv::derive_image_layout(
binding.usage,
binding.view.attachment.view_format,
binding.view.view_format,
);
vk::DescriptorImageInfo::default()
.image_view(binding.view.raw)
@ -3177,6 +3084,4 @@ struct ImageWithoutMemory {
raw: vk::Image,
requirements: vk::MemoryRequirements,
copy_size: crate::CopyExtent,
view_formats: Vec<wgt::TextureFormat>,
raw_flags: vk::ImageCreateFlags,
}

View File

@ -1108,16 +1108,6 @@ impl crate::Surface for super::Surface {
return Err(crate::SurfaceError::Outdated);
}
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#VUID-VkRenderPassBeginInfo-framebuffer-03209
let raw_flags = if swapchain
.raw_flags
.contains(vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT)
{
vk::ImageCreateFlags::MUTABLE_FORMAT | vk::ImageCreateFlags::EXTENDED_USAGE
} else {
vk::ImageCreateFlags::empty()
};
let texture = super::SurfaceTexture {
index,
texture: super::Texture {
@ -1125,15 +1115,12 @@ impl crate::Surface for super::Surface {
drop_guard: None,
block: None,
external_memory: None,
usage: swapchain.config.usage,
format: swapchain.config.format,
raw_flags,
copy_size: crate::CopyExtent {
width: swapchain.config.extent.width,
height: swapchain.config.extent.height,
depth: 1,
},
view_formats: swapchain.view_formats.clone(),
},
surface_semaphores: swapchain_semaphores_arc,
};

View File

@ -340,12 +340,10 @@ impl SwapchainImageSemaphores {
struct Swapchain {
raw: vk::SwapchainKHR,
raw_flags: vk::SwapchainCreateFlagsKHR,
functor: khr::swapchain::Device,
device: Arc<DeviceShared>,
images: Vec<vk::Image>,
config: crate::SurfaceConfiguration,
view_formats: Vec<wgt::TextureFormat>,
/// One wait semaphore per swapchain image. This will be associated with the
/// surface texture, and later collected during submission.
///
@ -488,7 +486,6 @@ struct RayTracingDeviceExtensionFunctions {
/// device geometry, but affect the code paths taken internally.
#[derive(Clone, Debug)]
struct PrivateCapabilities {
imageless_framebuffers: bool,
image_view_usage: bool,
timeline_semaphores: bool,
texture_d24: bool,
@ -605,19 +602,9 @@ struct RenderPassKey {
multiview: Option<NonZeroU32>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct FramebufferAttachment {
/// Can be NULL if the framebuffer is image-less
raw: vk::ImageView,
raw_image_flags: vk::ImageCreateFlags,
view_usage: wgt::TextureUses,
view_format: wgt::TextureFormat,
raw_view_formats: Vec<vk::Format>,
}
#[derive(Clone, Eq, Hash, PartialEq)]
struct FramebufferKey {
attachments: ArrayVec<FramebufferAttachment, { MAX_TOTAL_ATTACHMENTS }>,
attachments: ArrayVec<vk::ImageView, { MAX_TOTAL_ATTACHMENTS }>,
extent: wgt::Extent3d,
sample_count: u32,
}
@ -797,11 +784,8 @@ pub struct Texture {
drop_guard: Option<crate::DropGuard>,
external_memory: Option<vk::DeviceMemory>,
block: Option<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
usage: wgt::TextureUses,
format: wgt::TextureFormat,
raw_flags: vk::ImageCreateFlags,
copy_size: crate::CopyExtent,
view_formats: Vec<wgt::TextureFormat>,
}
impl crate::DynTexture for Texture {}
@ -819,7 +803,7 @@ impl Texture {
pub struct TextureView {
raw: vk::ImageView,
layers: NonZeroU32,
attachment: FramebufferAttachment,
view_format: wgt::TextureFormat,
}
impl crate::DynTextureView for TextureView {}