Add support for querying memory requirements directly from the device (#2470)

This commit is contained in:
Rua 2024-02-20 14:06:07 +01:00 committed by GitHub
parent 4349c8e004
commit 4074a91869
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 467 additions and 5 deletions

View File

@ -108,13 +108,11 @@ impl RawBuffer {
let mut external_memory_info_vk = None;
if !external_memory_handle_types.is_empty() {
let _ = external_memory_info_vk.insert(ash::vk::ExternalMemoryBufferCreateInfo {
let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryBufferCreateInfo {
handle_types: external_memory_handle_types.into(),
..Default::default()
});
}
if let Some(next) = external_memory_info_vk.as_mut() {
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}

View File

@ -108,12 +108,15 @@ use crate::{
AccelerationStructureBuildGeometryInfo, AccelerationStructureBuildSizesInfo,
AccelerationStructureBuildType, AccelerationStructureGeometries,
},
buffer::BufferCreateInfo,
descriptor_set::layout::{
DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorSetLayoutSupport,
},
image::{ImageCreateFlags, ImageCreateInfo, ImageTiling},
instance::{Instance, InstanceOwned, InstanceOwnedDebugWrapper},
macros::{impl_id_counter, vulkan_bitflags},
memory::ExternalMemoryHandleType,
memory::{allocator::DeviceLayout, ExternalMemoryHandleType, MemoryRequirements},
sync::Sharing,
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject,
};
@ -1004,6 +1007,467 @@ impl Device {
})
}
/// Returns the memory requirements that would apply for a buffer created with the specified
/// `create_info`.
///
/// The device API version must be at least 1.3, or the [`khr_maintenance4`] extension must
/// be enabled on the device.
///
/// [`khr_maintenance4`]: DeviceExtensions::khr_maintenance4
#[inline]
pub fn buffer_memory_requirements(
&self,
create_info: BufferCreateInfo,
) -> Result<MemoryRequirements, Box<ValidationError>> {
self.validate_buffer_memory_requirements(&create_info)?;
unsafe { Ok(self.buffer_memory_requirements_unchecked(create_info)) }
}
fn validate_buffer_memory_requirements(
&self,
create_info: &BufferCreateInfo,
) -> Result<(), Box<ValidationError>> {
if !(self.api_version() >= Version::V1_3 || self.enabled_extensions().khr_maintenance4) {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance")]),
]),
..Default::default()
}));
}
create_info
.validate(self)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn buffer_memory_requirements_unchecked(
&self,
create_info: BufferCreateInfo,
) -> MemoryRequirements {
let &BufferCreateInfo {
flags,
ref sharing,
size,
usage,
external_memory_handle_types,
_ne: _,
} = &create_info;
let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing {
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _),
Sharing::Concurrent(queue_family_indices) => (
ash::vk::SharingMode::CONCURRENT,
queue_family_indices.len() as u32,
queue_family_indices.as_ptr(),
),
};
let mut create_info_vk = ash::vk::BufferCreateInfo {
flags: flags.into(),
size,
usage: usage.into(),
sharing_mode,
queue_family_index_count,
p_queue_family_indices,
..Default::default()
};
let mut external_memory_info_vk = None;
if !external_memory_handle_types.is_empty() {
let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryBufferCreateInfo {
handle_types: external_memory_handle_types.into(),
..Default::default()
});
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
let info_vk = ash::vk::DeviceBufferMemoryRequirements {
p_create_info: &create_info_vk,
..Default::default()
};
let mut memory_requirements2_vk = ash::vk::MemoryRequirements2::default();
let mut memory_dedicated_requirements_vk = None;
// `khr_maintenance4` requires Vulkan 1.1,
// which means dedicated allocation support is always available.
{
let next = memory_dedicated_requirements_vk
.insert(ash::vk::MemoryDedicatedRequirements::default());
next.p_next = memory_requirements2_vk.p_next;
memory_requirements2_vk.p_next = next as *mut _ as *mut _;
}
unsafe {
let fns = self.fns();
if self.api_version() >= Version::V1_3 {
(fns.v1_3.get_device_buffer_memory_requirements)(
self.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
} else {
debug_assert!(self.enabled_extensions().khr_maintenance4);
(fns.khr_maintenance4
.get_device_buffer_memory_requirements_khr)(
self.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
}
}
MemoryRequirements {
layout: DeviceLayout::from_size_alignment(
memory_requirements2_vk.memory_requirements.size,
memory_requirements2_vk.memory_requirements.alignment,
)
.unwrap(),
memory_type_bits: memory_requirements2_vk.memory_requirements.memory_type_bits,
prefers_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.prefers_dedicated_allocation != 0),
requires_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.requires_dedicated_allocation != 0),
}
}
/// Returns the memory requirements that would apply for an image created with the specified
/// `create_info`.
///
/// If `create_info.flags` contains [`ImageCreateFlags::DISJOINT`], then `plane` must specify
/// the plane number of the format or memory plane (depending on tiling) that memory
/// requirements will be returned for. Otherwise, `plane` must be `None`.
///
/// The device API version must be at least 1.3, or the [`khr_maintenance4`] extension must
/// be enabled on the device.
///
/// [`khr_maintenance4`]: DeviceExtensions::khr_maintenance4
#[inline]
pub fn image_memory_requirements(
&self,
create_info: ImageCreateInfo,
plane: Option<usize>,
) -> Result<MemoryRequirements, Box<ValidationError>> {
self.validate_image_memory_requirements(&create_info, plane)?;
unsafe { Ok(self.image_memory_requirements_unchecked(create_info, plane)) }
}
fn validate_image_memory_requirements(
&self,
create_info: &ImageCreateInfo,
plane: Option<usize>,
) -> Result<(), Box<ValidationError>> {
if !(self.api_version() >= Version::V1_3 || self.enabled_extensions().khr_maintenance4) {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance")]),
]),
..Default::default()
}));
}
create_info
.validate(self)
.map_err(|err| err.add_context("create_info"))?;
let &ImageCreateInfo {
flags,
image_type: _,
format,
view_formats: _,
extent: _,
array_layers: _,
mip_levels: _,
samples: _,
tiling,
usage: _,
stencil_usage: _,
sharing: _,
initial_layout: _,
ref drm_format_modifiers,
drm_format_modifier_plane_layouts: _,
external_memory_handle_types: _,
_ne: _,
} = create_info;
if flags.intersects(ImageCreateFlags::DISJOINT) {
let Some(plane) = plane else {
return Err(Box::new(ValidationError {
problem: "`create_info.flags` contains `ImageCreateFlags::DISJOINT`, but \
`plane` is `None`"
.into(),
vuids: &[
"VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06419",
"VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06420",
],
..Default::default()
}));
};
match tiling {
ImageTiling::Linear | ImageTiling::Optimal => {
if plane >= format.planes().len() {
return Err(Box::new(ValidationError {
problem:
"`create_info.tiling` is not `ImageTiling::DrmFormatModifier`, \
but `plane` is not less than the number of planes in \
`create_info.format`"
.into(),
vuids: &["VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06419"],
..Default::default()
}));
}
}
ImageTiling::DrmFormatModifier => {
// TODO: handle the case where `drm_format_modifiers` contains multiple
// elements. See: https://github.com/KhronosGroup/Vulkan-Docs/issues/2309
if let &[drm_format_modifier] = drm_format_modifiers.as_slice() {
let format_properties =
unsafe { self.physical_device.format_properties_unchecked(format) };
let drm_format_modifier_properties = format_properties
.drm_format_modifier_properties
.iter()
.find(|properties| {
properties.drm_format_modifier == drm_format_modifier
})
.unwrap();
if plane
>= drm_format_modifier_properties.drm_format_modifier_plane_count
as usize
{
return Err(Box::new(ValidationError {
problem: "`create_info.drm_format_modifiers` has a length of 1, \
but `plane` is not less than `DrmFormatModifierProperties::\
drm_format_modifier_plane_count` for \
`drm_format_modifiers[0]`, as returned by \
`PhysicalDevice::format_properties` for `format`"
.into(),
vuids: &["VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06420"],
..Default::default()
}));
}
}
}
}
} else if plane.is_some() {
return Err(Box::new(ValidationError {
problem: "`create_info.flags` does not contain `ImageCreateFlags::DISJOINT`, but \
`plane` is `Some`"
.into(),
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn image_memory_requirements_unchecked(
&self,
create_info: ImageCreateInfo,
plane: Option<usize>,
) -> MemoryRequirements {
let &ImageCreateInfo {
flags,
image_type,
format,
ref view_formats,
extent,
array_layers,
mip_levels,
samples,
tiling,
usage,
stencil_usage,
ref sharing,
initial_layout,
ref drm_format_modifiers,
drm_format_modifier_plane_layouts: _,
external_memory_handle_types,
_ne: _,
} = &create_info;
let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing {
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _),
Sharing::Concurrent(queue_family_indices) => (
ash::vk::SharingMode::CONCURRENT,
queue_family_indices.len() as u32,
queue_family_indices.as_ptr(),
),
};
let mut create_info_vk = ash::vk::ImageCreateInfo {
flags: flags.into(),
image_type: image_type.into(),
format: format.into(),
extent: ash::vk::Extent3D {
width: extent[0],
height: extent[1],
depth: extent[2],
},
mip_levels,
array_layers,
samples: samples.into(),
tiling: tiling.into(),
usage: usage.into(),
sharing_mode,
queue_family_index_count,
p_queue_family_indices,
initial_layout: initial_layout.into(),
..Default::default()
};
let mut drm_format_modifier_list_info_vk = None;
let mut external_memory_info_vk = None;
let mut format_list_info_vk = None;
let format_list_view_formats_vk: Vec<_>;
let mut stencil_usage_info_vk = None;
if !drm_format_modifiers.is_empty() {
let next = drm_format_modifier_list_info_vk.insert(
ash::vk::ImageDrmFormatModifierListCreateInfoEXT {
drm_format_modifier_count: drm_format_modifiers.len() as u32,
p_drm_format_modifiers: drm_format_modifiers.as_ptr(),
..Default::default()
},
);
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
if !external_memory_handle_types.is_empty() {
let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryImageCreateInfo {
handle_types: external_memory_handle_types.into(),
..Default::default()
});
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
if !view_formats.is_empty() {
format_list_view_formats_vk = view_formats
.iter()
.copied()
.map(ash::vk::Format::from)
.collect();
let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo {
view_format_count: format_list_view_formats_vk.len() as u32,
p_view_formats: format_list_view_formats_vk.as_ptr(),
..Default::default()
});
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
if let Some(stencil_usage) = stencil_usage {
let next = stencil_usage_info_vk.insert(ash::vk::ImageStencilUsageCreateInfo {
stencil_usage: stencil_usage.into(),
..Default::default()
});
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
// This is currently necessary because of an issue with the spec. The plane aspect should
// only be needed if the image is disjoint, but the spec currently demands a valid aspect
// even for non-disjoint DRM format modifier images.
// See: https://github.com/KhronosGroup/Vulkan-Docs/issues/2309
// Replace this variable with ash::vk::ImageAspectFlags::NONE when resolved.
let default_aspect = if tiling == ImageTiling::DrmFormatModifier {
// Hopefully valid for any DrmFormatModifier image?
ash::vk::ImageAspectFlags::MEMORY_PLANE_0_EXT
} else {
ash::vk::ImageAspectFlags::NONE
};
let plane_aspect = plane.map_or(default_aspect, |plane| match tiling {
ImageTiling::Optimal | ImageTiling::Linear => match plane {
0 => ash::vk::ImageAspectFlags::PLANE_0,
1 => ash::vk::ImageAspectFlags::PLANE_1,
2 => ash::vk::ImageAspectFlags::PLANE_2,
_ => unreachable!(),
},
ImageTiling::DrmFormatModifier => match plane {
0 => ash::vk::ImageAspectFlags::MEMORY_PLANE_0_EXT,
1 => ash::vk::ImageAspectFlags::MEMORY_PLANE_1_EXT,
2 => ash::vk::ImageAspectFlags::MEMORY_PLANE_2_EXT,
3 => ash::vk::ImageAspectFlags::MEMORY_PLANE_3_EXT,
_ => unreachable!(),
},
});
let info_vk = ash::vk::DeviceImageMemoryRequirements {
p_create_info: &create_info_vk,
plane_aspect,
..Default::default()
};
let mut memory_requirements2_vk = ash::vk::MemoryRequirements2::default();
let mut memory_dedicated_requirements_vk = None;
// `khr_maintenance4` requires Vulkan 1.1,
// which means dedicated allocation support is always available.
{
let next = memory_dedicated_requirements_vk
.insert(ash::vk::MemoryDedicatedRequirements::default());
next.p_next = memory_requirements2_vk.p_next;
memory_requirements2_vk.p_next = next as *mut _ as *mut _;
}
unsafe {
let fns = self.fns();
if self.api_version() >= Version::V1_3 {
(fns.v1_3.get_device_image_memory_requirements)(
self.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
} else {
debug_assert!(self.enabled_extensions().khr_maintenance4);
(fns.khr_maintenance4
.get_device_image_memory_requirements_khr)(
self.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
}
}
MemoryRequirements {
layout: DeviceLayout::from_size_alignment(
memory_requirements2_vk.memory_requirements.size,
memory_requirements2_vk.memory_requirements.alignment,
)
.unwrap(),
memory_type_bits: memory_requirements2_vk.memory_requirements.memory_type_bits,
prefers_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.prefers_dedicated_allocation != 0),
requires_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.requires_dedicated_allocation != 0),
}
}
// TODO: image_sparse_memory_requirements
/// Retrieves the properties of an external file descriptor when imported as a given external
/// handle type.
///

View File

@ -2629,7 +2629,7 @@ impl ImageCreateInfo {
if format.planes().len() < 2 {
return Err(Box::new(ValidationError {
problem: "`flags` contains `ImageCreateFlags::DISJOINT`, but `format` \
is not a multi-planat format"
is not a multi-planar format"
.into(),
vuids: &["VUID-VkImageCreateInfo-format-01577"],
..Default::default()