Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sparse binding support #2609

Merged
merged 18 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge assume_bound, make sparse image memory requirements available…
… for raw images
  • Loading branch information
Rua committed Nov 30, 2024
commit 3c185a3b59a59620176e1dca2ed0f13fceada509
34 changes: 12 additions & 22 deletions vulkano/src/buffer/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,19 +522,15 @@ impl RawBuffer {
Ok(Buffer::from_raw(self, BufferMemory::Normal(allocation)))
}

/// Assume this buffer has memory bound to it.
/// Converts a raw buffer into a full buffer without binding any memory.
///
/// # Safety
///
/// - The buffer must have memory bound to it.
pub unsafe fn assume_bound(self) -> Buffer {
Buffer::from_raw(self, BufferMemory::External)
}

/// Converts a raw buffer, that was created with the [`BufferCreateInfo::SPARSE_BINDING`] flag,
/// into a full buffer without binding any memory.
/// If `self.flags()` does not contain [`BufferCreateFlags::SPARSE_BINDING`]:
///
/// # Safety
/// - The buffer must already have a suitable memory allocation bound to it.
///
/// If `self.flags()` does contain [`BufferCreateFlags::SPARSE_BINDING`]:
///
/// - If `self.flags()` does not contain [`BufferCreateFlags::SPARSE_RESIDENCY`], then the
/// buffer must be fully bound with memory before its memory is accessed by the device.
Expand All @@ -543,20 +539,14 @@ impl RawBuffer {
/// as determined by the [`Properties::residency_non_resident_strict`] device property.
///
/// [`Properties::residency_non_resident_strict`]: crate::device::Properties::residency_non_resident_strict
pub unsafe fn into_buffer_sparse(self) -> Result<Buffer, (Validated<VulkanError>, RawBuffer)> {
if !self.flags().intersects(BufferCreateFlags::SPARSE_BINDING) {
return Err((
Box::new(ValidationError {
context: "self.flags()".into(),
problem: "does not contain `BufferCreateFlags::SPARSE_BINDING`".into(),
..Default::default()
})
.into(),
self,
));
}
pub unsafe fn assume_bound(self) -> Buffer {
let memory = if self.flags().intersects(BufferCreateFlags::SPARSE_BINDING) {
BufferMemory::Sparse
} else {
BufferMemory::External
};

Ok(Buffer::from_raw(self, BufferMemory::Sparse))
Buffer::from_raw(self, memory)
}

/// Returns the memory requirements for this buffer.
Expand Down
22 changes: 16 additions & 6 deletions vulkano/src/image/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub enum ImageMemory {
/// The image is backed by sparse memory, bound with [`bind_sparse`].
///
/// [`bind_sparse`]: crate::device::QueueGuard::bind_sparse
Sparse(Vec<SparseImageMemoryRequirements>),
Sparse,

/// The image is backed by memory owned by a [`Swapchain`].
Swapchain {
Expand Down Expand Up @@ -247,14 +247,24 @@ impl Image {
/// Returns the memory requirements for this image.
///
/// - If the image is a swapchain image, this returns a slice with a length of 0.
/// - If `self.flags().disjoint` is not set, this returns a slice with a length of 1.
/// - If `self.flags().disjoint` is set, this returns a slice with a length equal to
/// `self.format().planes().len()`.
/// - If `self.flags()` does not contain `ImageCreateFlags::DISJOINT`, this returns a slice
/// with a length of 1.
/// - If `self.flags()` does contain `ImageCreateFlags::DISJOINT`, this returns a slice with a
/// length equal to `self.format().unwrap().planes().len()`.
#[inline]
pub fn memory_requirements(&self) -> &[MemoryRequirements] {
self.inner.memory_requirements()
}

/// Returns the sparse memory requirements for this image.
///
/// If `self.flags()` does not contain both `ImageCreateFlags::SPARSE_BINDING` and
/// `ImageCreateFlags::SPARSE_RESIDENCY`, this returns an empty slice.
#[inline]
pub fn sparse_memory_requirements(&self) -> &[SparseImageMemoryRequirements] {
self.inner.sparse_memory_requirements()
}

/// Returns the flags the image was created with.
#[inline]
pub fn flags(&self) -> ImageCreateFlags {
Expand Down Expand Up @@ -510,7 +520,7 @@ impl Image {

pub(crate) unsafe fn layout_initialized(&self) {
match &self.memory {
ImageMemory::Normal(..) | ImageMemory::Sparse(..) | ImageMemory::External => {
ImageMemory::Normal(..) | ImageMemory::Sparse | ImageMemory::External => {
self.is_layout_initialized.store(true, Ordering::Release);
}
ImageMemory::Swapchain {
Expand All @@ -524,7 +534,7 @@ impl Image {

pub(crate) fn is_layout_initialized(&self) -> bool {
match &self.memory {
ImageMemory::Normal(..) | ImageMemory::Sparse(..) | ImageMemory::External => {
ImageMemory::Normal(..) | ImageMemory::Sparse | ImageMemory::External => {
self.is_layout_initialized.load(Ordering::Acquire)
}
ImageMemory::Swapchain {
Expand Down
117 changes: 47 additions & 70 deletions vulkano/src/image/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub struct RawImage {
external_memory_handle_types: ExternalMemoryHandleTypes,

memory_requirements: SmallVec<[MemoryRequirements; 4]>,
sparse_memory_requirements: Vec<SparseImageMemoryRequirements>,
needs_destruction: bool, // `vkDestroyImage` is called only if true.
subresource_layout: OnceCache<(ImageAspect, u32, u32), SubresourceLayout>,
}
Expand Down Expand Up @@ -235,6 +236,14 @@ impl RawImage {
smallvec![]
};

let sparse_memory_requirements = if flags
.contains(ImageCreateFlags::SPARSE_BINDING | ImageCreateFlags::SPARSE_RESIDENCY)
{
Self::get_sparse_memory_requirements(&device, handle)
} else {
Vec::new()
};

Ok(RawImage {
handle,
device: InstanceOwnedDebugWrapper(device),
Expand All @@ -258,6 +267,7 @@ impl RawImage {
external_memory_handle_types,

memory_requirements,
sparse_memory_requirements,
needs_destruction,
subresource_layout: OnceCache::new(),
})
Expand Down Expand Up @@ -360,19 +370,16 @@ impl RawImage {
)
}

fn get_sparse_memory_requirements(&self) -> Vec<SparseImageMemoryRequirements> {
if !self.flags.intersects(ImageCreateFlags::SPARSE_RESIDENCY) {
return Vec::new();
}

let device = &self.device;
let fns = self.device.fns();
unsafe fn get_sparse_memory_requirements(
device: &Device,
handle: ash::vk::Image,
) -> Vec<SparseImageMemoryRequirements> {
let fns = device.fns();

if device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_get_memory_requirements2
{
let info2_vk =
ash::vk::ImageSparseMemoryRequirementsInfo2::default().image(self.handle);
let info2_vk = ash::vk::ImageSparseMemoryRequirementsInfo2::default().image(handle);

let mut count = 0;

Expand Down Expand Up @@ -403,7 +410,7 @@ impl RawImage {
if device.api_version() >= Version::V1_1 {
unsafe {
(fns.v1_1.get_image_sparse_memory_requirements2)(
self.device.handle(),
device.handle(),
&info2_vk,
&mut count,
requirements2_vk.as_mut_ptr(),
Expand All @@ -413,7 +420,7 @@ impl RawImage {
unsafe {
(fns.khr_get_memory_requirements2
.get_image_sparse_memory_requirements2_khr)(
self.device.handle(),
device.handle(),
&info2_vk,
&mut count,
requirements2_vk.as_mut_ptr(),
Expand All @@ -432,7 +439,7 @@ impl RawImage {
unsafe {
(fns.v1_0.get_image_sparse_memory_requirements)(
device.handle(),
self.handle,
handle,
&mut count,
ptr::null_mut(),
)
Expand All @@ -444,7 +451,7 @@ impl RawImage {
unsafe {
(fns.v1_0.get_image_sparse_memory_requirements)(
device.handle(),
self.handle,
handle,
&mut count,
requirements_vk.as_mut_ptr(),
)
Expand Down Expand Up @@ -969,11 +976,16 @@ impl RawImage {
))
}

/// Converts a raw image, that was created with the [`ImageCreateInfo::SPARSE_BINDING`] flag,
/// into a full image without binding any memory.
/// Converts a raw image into a full image without binding any memory.
///
/// # Safety
///
/// - The image must already have a suitable memory allocation bound to it.
///
Rua marked this conversation as resolved.
Show resolved Hide resolved
/// If `self.flags()` does not contain [`ImageCreateFlags::SPARSE_BINDING`]:
///
/// If `self.flags()` does contain [`ImageCreateFlags::SPARSE_BINDING`]:
///
/// - If `self.flags()` does not contain [`ImageCreateFlags::SPARSE_RESIDENCY`], then the image
/// must be fully bound with memory before any memory is accessed by the device.
/// - If `self.flags()` contains [`ImageCreateFlags::SPARSE_RESIDENCY`], then if the sparse
Expand All @@ -984,27 +996,15 @@ impl RawImage {
/// as determined by the [`Properties::residency_non_resident_strict`] device property.
///
/// [`Properties::residency_non_resident_strict`]: crate::device::Properties::residency_non_resident_strict
pub unsafe fn into_image_sparse(self) -> Result<Image, (Validated<VulkanError>, RawImage)> {
if !self.flags().intersects(ImageCreateFlags::SPARSE_BINDING) {
return Err((
Box::new(ValidationError {
context: "self.flags()".into(),
problem: "does not contain `ImageCreateFlags::SPARSE_BINDING`".into(),
..Default::default()
})
.into(),
self,
));
}

let sparse_image_memory_requirements = self.get_sparse_memory_requirements();
pub unsafe fn assume_bound(self) -> Image {
let memory = if self.flags().intersects(ImageCreateFlags::SPARSE_BINDING) {
ImageMemory::Sparse
} else {
ImageMemory::External
};
let layout = self.default_layout();

Ok(Image::from_raw(
self,
ImageMemory::Sparse(sparse_image_memory_requirements),
layout,
))
Image::from_raw(self, memory, layout)
}

fn default_layout(&self) -> ImageLayout {
Expand Down Expand Up @@ -1033,50 +1033,27 @@ impl RawImage {
}
}

/// Assume that this image already has memory backing it.
///
/// # Safety
///
/// - The image must be backed by suitable memory allocations.
pub unsafe fn assume_bound(self) -> Image {
let usage = self
.usage
.difference(ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST);

let layout = if usage.intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
&& usage
.difference(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
.is_empty()
{
ImageLayout::ShaderReadOnlyOptimal
} else if usage.intersects(ImageUsage::COLOR_ATTACHMENT)
&& usage.difference(ImageUsage::COLOR_ATTACHMENT).is_empty()
{
ImageLayout::ColorAttachmentOptimal
} else if usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
&& usage
.difference(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
.is_empty()
{
ImageLayout::DepthStencilAttachmentOptimal
} else {
ImageLayout::General
};

Image::from_raw(self, ImageMemory::External, layout)
}

/// Returns the memory requirements for this image.
///
/// - If the image is a swapchain image, this returns a slice with a length of 0.
/// - If `self.flags().disjoint` is not set, this returns a slice with a length of 1.
/// - If `self.flags().disjoint` is set, this returns a slice with a length equal to
/// `self.format().unwrap().planes().len()`.
/// - If `self.flags()` does not contain `ImageCreateFlags::DISJOINT`, this returns a slice
/// with a length of 1.
/// - If `self.flags()` does contain `ImageCreateFlags::DISJOINT`, this returns a slice with a
/// length equal to `self.format().unwrap().planes().len()`.
#[inline]
pub fn memory_requirements(&self) -> &[MemoryRequirements] {
&self.memory_requirements
}

/// Returns the sparse memory requirements for this image.
///
/// If `self.flags()` does not contain both `ImageCreateFlags::SPARSE_BINDING` and
/// `ImageCreateFlags::SPARSE_RESIDENCY`, this returns an empty slice.
#[inline]
pub fn sparse_memory_requirements(&self) -> &[SparseImageMemoryRequirements] {
&self.sparse_memory_requirements
}

/// Returns the flags the image was created with.
#[inline]
pub fn flags(&self) -> ImageCreateFlags {
Expand Down
26 changes: 9 additions & 17 deletions vulkano/src/memory/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::{
buffer::{Buffer, BufferCreateFlags},
device::{Device, DeviceOwned},
image::{
mip_level_extent, Image, ImageAspects, ImageCreateFlags, ImageMemory,
SparseImageFormatProperties, SparseImageMemoryRequirements,
mip_level_extent, Image, ImageAspects, ImageCreateFlags, SparseImageFormatProperties,
SparseImageMemoryRequirements,
},
memory::{is_aligned, MemoryRequirements},
sync::semaphore::Semaphore,
Expand Down Expand Up @@ -599,16 +599,11 @@ impl SparseImageOpaqueMemoryBindInfo {
prefers_dedicated_allocation: _,
requires_dedicated_allocation: _,
} = &image.memory_requirements()[0]; // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/2259
let metadata_memory_requirements = match image.memory() {
ImageMemory::Sparse(sparse_memory_requirements) => {
sparse_memory_requirements.iter().find(|reqs| {
reqs.format_properties
.aspects
.intersects(ImageAspects::METADATA)
})
}
_ => unreachable!(),
};
let metadata_memory_requirements = image.sparse_memory_requirements().iter().find(|reqs| {
reqs.format_properties
.aspects
.intersects(ImageAspects::METADATA)
});
let external_memory_handle_types = image.external_memory_handle_types();

for (index, bind) in binds.iter().enumerate() {
Expand Down Expand Up @@ -1060,10 +1055,6 @@ impl SparseImageMemoryBindInfo {
}));
}

let sparse_memory_requirements = match image.memory() {
ImageMemory::Sparse(sparse_memory_requirements) => sparse_memory_requirements,
_ => unreachable!(),
};
let image_format_subsampled_extent = image
.format()
.ycbcr_chroma_sampling()
Expand Down Expand Up @@ -1131,7 +1122,8 @@ impl SparseImageMemoryBindInfo {
image_mip_tail_size: _,
image_mip_tail_offset: _,
image_mip_tail_stride: _,
} = sparse_memory_requirements
} = image
.sparse_memory_requirements()
.iter()
.find(|reqs| reqs.format_properties.aspects == aspects)
.ok_or_else(|| {
Expand Down