use self::allocator::{
align_up, AllocationHandle, AllocationType, DeviceLayout, MemoryAlloc, MemoryAllocator,
Suballocation,
};
pub use self::{alignment::*, device_memory::*};
use crate::{
buffer::sys::RawBuffer,
device::{Device, DeviceOwned, DeviceOwnedDebugWrapper},
image::sys::RawImage,
macros::vulkan_bitflags,
sync::HostAccessError,
DeviceSize, Validated, ValidationError, Version, VulkanError, VulkanObject,
};
use std::{
cmp,
mem::ManuallyDrop,
num::NonZeroU64,
ops::{Bound, Range, RangeBounds, RangeTo},
ptr::NonNull,
sync::Arc,
};
mod alignment;
pub mod allocator;
mod device_memory;
pub mod sparse;
#[derive(Debug)]
pub struct ResourceMemory {
device_memory: ManuallyDrop<DeviceOwnedDebugWrapper<Arc<DeviceMemory>>>,
offset: DeviceSize,
size: DeviceSize,
allocation_type: AllocationType,
allocation_handle: AllocationHandle,
suballocation_handle: Option<AllocationHandle>,
allocator: Option<Arc<dyn MemoryAllocator>>,
}
impl ResourceMemory {
pub fn new_dedicated(device_memory: DeviceMemory) -> Self {
unsafe { Self::new_dedicated_unchecked(Arc::new(device_memory)) }
}
pub unsafe fn new_dedicated_unchecked(device_memory: Arc<DeviceMemory>) -> Self {
let size = device_memory.allocation_size();
unsafe { Self::from_device_memory_unchecked(device_memory, 0, size) }
}
pub unsafe fn from_device_memory_unchecked(
device_memory: Arc<DeviceMemory>,
offset: DeviceSize,
size: DeviceSize,
) -> Self {
assert!(offset <= device_memory.allocation_size());
assert!(size <= device_memory.allocation_size() - offset);
ResourceMemory {
offset,
size,
allocation_type: AllocationType::Unknown,
allocation_handle: AllocationHandle::null(),
suballocation_handle: None,
allocator: None,
device_memory: ManuallyDrop::new(DeviceOwnedDebugWrapper(device_memory)),
}
}
#[inline]
pub unsafe fn from_allocation(
allocator: Arc<dyn MemoryAllocator>,
allocation: MemoryAlloc,
) -> Self {
if let Some(suballocation) = allocation.suballocation {
ResourceMemory {
offset: suballocation.offset,
size: suballocation.size,
allocation_type: suballocation.allocation_type,
allocation_handle: allocation.allocation_handle,
suballocation_handle: Some(suballocation.handle),
allocator: Some(allocator),
device_memory: ManuallyDrop::new(DeviceOwnedDebugWrapper(allocation.device_memory)),
}
} else {
ResourceMemory {
offset: 0,
size: allocation.device_memory.allocation_size(),
allocation_type: AllocationType::Unknown,
allocation_handle: allocation.allocation_handle,
suballocation_handle: None,
allocator: Some(allocator),
device_memory: ManuallyDrop::new(DeviceOwnedDebugWrapper(allocation.device_memory)),
}
}
}
#[inline]
pub fn device_memory(&self) -> &Arc<DeviceMemory> {
&self.device_memory
}
#[inline]
pub fn offset(&self) -> DeviceSize {
self.offset
}
#[inline]
pub fn size(&self) -> DeviceSize {
self.size
}
#[inline]
pub fn allocation_type(&self) -> AllocationType {
self.allocation_type
}
fn suballocation(&self) -> Option<Suballocation> {
self.suballocation_handle.map(|handle| Suballocation {
offset: self.offset,
size: self.size,
allocation_type: self.allocation_type,
handle,
})
}
#[inline]
pub fn mapped_slice(
&self,
range: impl RangeBounds<DeviceSize>,
) -> Option<Result<NonNull<[u8]>, HostAccessError>> {
let mut range = self::range(range, ..self.size())?;
range.start += self.offset();
range.end += self.offset();
let res = if let Some(state) = self.device_memory().mapping_state() {
state.slice(range).ok_or(HostAccessError::OutOfMappedRange)
} else {
Err(HostAccessError::NotHostMapped)
};
Some(res)
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn mapped_slice_unchecked(
&self,
range: impl RangeBounds<DeviceSize>,
) -> Result<NonNull<[u8]>, HostAccessError> {
let mut range = range_unchecked(range, ..self.size());
range.start += self.offset();
range.end += self.offset();
if let Some(state) = self.device_memory().mapping_state() {
state.slice(range).ok_or(HostAccessError::OutOfMappedRange)
} else {
Err(HostAccessError::NotHostMapped)
}
}
#[doc(hidden)]
#[inline]
pub fn atom_size(&self) -> Option<DeviceAlignment> {
let memory = self.device_memory();
(!memory.is_coherent()).then_some(memory.atom_size())
}
#[inline]
pub unsafe fn invalidate_range(
&self,
memory_range: MappedMemoryRange,
) -> Result<(), Validated<VulkanError>> {
self.validate_memory_range(&memory_range)?;
unsafe {
self.device_memory()
.invalidate_range(self.create_memory_range(memory_range))
}
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn invalidate_range_unchecked(
&self,
memory_range: MappedMemoryRange,
) -> Result<(), VulkanError> {
unsafe {
self.device_memory()
.invalidate_range_unchecked(self.create_memory_range(memory_range))
}
}
#[inline]
pub unsafe fn flush_range(
&self,
memory_range: MappedMemoryRange,
) -> Result<(), Validated<VulkanError>> {
self.validate_memory_range(&memory_range)?;
unsafe {
self.device_memory()
.flush_range(self.create_memory_range(memory_range))
}
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn flush_range_unchecked(
&self,
memory_range: MappedMemoryRange,
) -> Result<(), VulkanError> {
unsafe {
self.device_memory()
.flush_range_unchecked(self.create_memory_range(memory_range))
}
}
fn validate_memory_range(
&self,
memory_range: &MappedMemoryRange,
) -> Result<(), Box<ValidationError>> {
let &MappedMemoryRange {
offset,
size,
_ne: _,
} = memory_range;
if !(offset <= self.size() && size <= self.size() - offset) {
return Err(Box::new(ValidationError {
context: "memory_range".into(),
problem: "is not contained within the allocation".into(),
..Default::default()
}));
}
Ok(())
}
fn create_memory_range(&self, memory_range: MappedMemoryRange) -> MappedMemoryRange {
let MappedMemoryRange {
mut offset,
mut size,
_ne: _,
} = memory_range;
let memory = self.device_memory();
offset += self.offset();
if memory_range.offset + size == self.size() {
let end = cmp::min(
align_up(offset + size, memory.atom_size()),
memory.allocation_size(),
);
size = end - offset;
}
MappedMemoryRange {
offset,
size,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn to_vk_bind_buffer_memory_info(
&self,
buffer_vk: ash::vk::Buffer,
) -> ash::vk::BindBufferMemoryInfo<'static> {
let &Self {
ref device_memory,
offset,
..
} = self;
ash::vk::BindBufferMemoryInfo::default()
.buffer(buffer_vk)
.memory(device_memory.handle())
.memory_offset(offset)
}
pub(crate) fn to_vk_bind_image_memory_info(
&self,
image_vk: ash::vk::Image,
) -> ash::vk::BindImageMemoryInfo<'static> {
let &Self {
ref device_memory,
offset,
..
} = self;
ash::vk::BindImageMemoryInfo::default()
.image(image_vk)
.memory(device_memory.handle())
.memory_offset(offset)
}
}
impl Drop for ResourceMemory {
#[inline]
fn drop(&mut self) {
let device_memory = unsafe { ManuallyDrop::take(&mut self.device_memory) }.0;
if let Some(allocator) = &self.allocator {
let allocation = MemoryAlloc {
device_memory,
suballocation: self.suballocation(),
allocation_handle: self.allocation_handle,
};
unsafe { allocator.deallocate(allocation) };
}
}
}
unsafe impl DeviceOwned for ResourceMemory {
#[inline]
fn device(&self) -> &Arc<Device> {
self.device_memory().device()
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MemoryProperties {
pub memory_types: Vec<MemoryType>,
pub memory_heaps: Vec<MemoryHeap>,
}
impl MemoryProperties {
pub(crate) fn to_mut_vk2() -> ash::vk::PhysicalDeviceMemoryProperties2KHR<'static> {
ash::vk::PhysicalDeviceMemoryProperties2KHR::default()
}
pub(crate) fn from_vk2(val_vk: &ash::vk::PhysicalDeviceMemoryProperties2<'_>) -> Self {
let &ash::vk::PhysicalDeviceMemoryProperties2 {
ref memory_properties,
..
} = val_vk;
Self::from_vk(memory_properties)
}
pub(crate) fn from_vk(val_vk: &ash::vk::PhysicalDeviceMemoryProperties) -> Self {
let memory_types_vk = val_vk.memory_types_as_slice();
let memory_heaps_vk = val_vk.memory_heaps_as_slice();
Self {
memory_types: memory_types_vk.iter().map(MemoryType::from_vk).collect(),
memory_heaps: memory_heaps_vk.iter().map(MemoryHeap::from_vk).collect(),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MemoryType {
pub property_flags: MemoryPropertyFlags,
pub heap_index: u32,
}
impl MemoryType {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn from_vk(val_vk: &ash::vk::MemoryType) -> Self {
let &ash::vk::MemoryType {
property_flags,
heap_index,
} = val_vk;
MemoryType {
property_flags: property_flags.into(),
heap_index,
}
}
}
vulkan_bitflags! {
#[non_exhaustive]
MemoryPropertyFlags = MemoryPropertyFlags(u32);
DEVICE_LOCAL = DEVICE_LOCAL,
HOST_VISIBLE = HOST_VISIBLE,
HOST_COHERENT = HOST_COHERENT,
HOST_CACHED = HOST_CACHED,
LAZILY_ALLOCATED = LAZILY_ALLOCATED,
PROTECTED = PROTECTED
RequiresOneOf([
RequiresAllOf([APIVersion(V1_1)]),
]),
DEVICE_COHERENT = DEVICE_COHERENT_AMD
RequiresOneOf([
RequiresAllOf([DeviceExtension(amd_device_coherent_memory)]),
]),
DEVICE_UNCACHED = DEVICE_UNCACHED_AMD
RequiresOneOf([
RequiresAllOf([DeviceExtension(amd_device_coherent_memory)]),
]),
RDMA_CAPABLE = RDMA_CAPABLE_NV
RequiresOneOf([
RequiresAllOf([DeviceExtension(nv_external_memory_rdma)]),
]),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MemoryHeap {
pub size: DeviceSize,
pub flags: MemoryHeapFlags,
}
impl MemoryHeap {
pub(crate) fn from_vk(val_vk: &ash::vk::MemoryHeap) -> Self {
let &ash::vk::MemoryHeap { size, flags } = val_vk;
Self {
size,
flags: flags.into(),
}
}
}
vulkan_bitflags! {
#[non_exhaustive]
MemoryHeapFlags = MemoryHeapFlags(u32);
DEVICE_LOCAL = DEVICE_LOCAL,
MULTI_INSTANCE = MULTI_INSTANCE
RequiresOneOf([
RequiresAllOf([APIVersion(V1_1)]),
RequiresAllOf([InstanceExtension(khr_device_group_creation)]),
]),
}
#[derive(Clone, Copy, Debug)]
pub struct MemoryRequirements {
pub layout: DeviceLayout,
pub memory_type_bits: u32,
pub prefers_dedicated_allocation: bool,
pub requires_dedicated_allocation: bool,
}
impl MemoryRequirements {
pub(crate) fn to_mut_vk2(
extensions_vk: &mut MemoryRequirements2ExtensionsVk,
) -> ash::vk::MemoryRequirements2<'_> {
let mut val_vk = ash::vk::MemoryRequirements2::default();
let MemoryRequirements2ExtensionsVk { dedicated_vk } = extensions_vk;
if let Some(next) = dedicated_vk {
val_vk = val_vk.push_next(next);
}
val_vk
}
pub(crate) fn to_mut_vk2_extensions(device: &Device) -> MemoryRequirements2ExtensionsVk {
let dedicated_vk = (device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_dedicated_allocation)
.then(|| {
debug_assert!(
device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_get_memory_requirements2
);
ash::vk::MemoryDedicatedRequirements::default()
});
MemoryRequirements2ExtensionsVk { dedicated_vk }
}
pub(crate) fn from_vk2(
val_vk: &ash::vk::MemoryRequirements2<'_>,
extensions_vk: &MemoryRequirements2ExtensionsVk,
) -> Self {
let &ash::vk::MemoryRequirements2 {
memory_requirements:
ash::vk::MemoryRequirements {
size,
alignment,
memory_type_bits,
},
..
} = val_vk;
let mut val = Self {
layout: DeviceLayout::from_size_alignment(size, alignment).unwrap(),
memory_type_bits,
prefers_dedicated_allocation: false,
requires_dedicated_allocation: false,
};
let MemoryRequirements2ExtensionsVk { dedicated_vk } = extensions_vk;
if let Some(val_vk) = dedicated_vk {
let &ash::vk::MemoryDedicatedRequirements {
prefers_dedicated_allocation,
requires_dedicated_allocation,
..
} = val_vk;
val = Self {
prefers_dedicated_allocation: prefers_dedicated_allocation != 0,
requires_dedicated_allocation: requires_dedicated_allocation != 0,
..val
};
}
val
}
}
pub(crate) struct MemoryRequirements2ExtensionsVk {
pub(crate) dedicated_vk: Option<ash::vk::MemoryDedicatedRequirements<'static>>,
}
#[derive(Clone, Copy, Debug)]
pub enum DedicatedAllocation<'a> {
Buffer(&'a RawBuffer),
Image(&'a RawImage),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DedicatedTo {
Buffer(NonZeroU64),
Image(NonZeroU64),
}
impl From<DedicatedAllocation<'_>> for DedicatedTo {
fn from(dedicated_allocation: DedicatedAllocation<'_>) -> Self {
match dedicated_allocation {
DedicatedAllocation::Buffer(buffer) => Self::Buffer(buffer.id()),
DedicatedAllocation::Image(image) => Self::Image(image.id()),
}
}
}
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct ExternalMemoryProperties {
pub dedicated_only: bool,
pub exportable: bool,
pub importable: bool,
pub export_from_imported_handle_types: ExternalMemoryHandleTypes,
pub compatible_handle_types: ExternalMemoryHandleTypes,
}
impl ExternalMemoryProperties {
pub(crate) fn from_vk(val_vk: &ash::vk::ExternalMemoryProperties) -> Self {
let &ash::vk::ExternalMemoryProperties {
external_memory_features,
export_from_imported_handle_types,
compatible_handle_types,
} = val_vk;
Self {
dedicated_only: external_memory_features
.intersects(ash::vk::ExternalMemoryFeatureFlags::DEDICATED_ONLY),
exportable: external_memory_features
.intersects(ash::vk::ExternalMemoryFeatureFlags::EXPORTABLE),
importable: external_memory_features
.intersects(ash::vk::ExternalMemoryFeatureFlags::IMPORTABLE),
export_from_imported_handle_types: export_from_imported_handle_types.into(),
compatible_handle_types: compatible_handle_types.into(),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MemoryFdProperties {
pub memory_type_bits: u32,
}
impl MemoryFdProperties {
pub(crate) fn to_mut_vk() -> ash::vk::MemoryFdPropertiesKHR<'static> {
ash::vk::MemoryFdPropertiesKHR::default()
}
pub(crate) fn from_vk(val_vk: &ash::vk::MemoryFdPropertiesKHR<'_>) -> Self {
let &ash::vk::MemoryFdPropertiesKHR {
memory_type_bits, ..
} = val_vk;
Self { memory_type_bits }
}
}
#[inline(always)]
pub(crate) fn is_aligned(offset: DeviceSize, alignment: DeviceAlignment) -> bool {
offset & (alignment.as_devicesize() - 1) == 0
}
pub(crate) fn range(
range: impl RangeBounds<DeviceSize>,
bounds: RangeTo<DeviceSize>,
) -> Option<Range<DeviceSize>> {
let len = bounds.end;
let start = match range.start_bound() {
Bound::Included(&start) => start,
Bound::Excluded(start) => start.checked_add(1)?,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(end) => end.checked_add(1)?,
Bound::Excluded(&end) => end,
Bound::Unbounded => len,
};
(start <= end && end <= len).then_some(Range { start, end })
}
pub(crate) fn range_unchecked(
range: impl RangeBounds<DeviceSize>,
bounds: RangeTo<DeviceSize>,
) -> Range<DeviceSize> {
let len = bounds.end;
let start = match range.start_bound() {
Bound::Included(&start) => start,
Bound::Excluded(start) => start + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(end) => end + 1,
Bound::Excluded(&end) => end,
Bound::Unbounded => len,
};
debug_assert!(start <= end && end <= len);
Range { start, end }
}