use super::DedicatedAllocation;
use crate::{
check_errors,
device::{physical::MemoryType, Device, DeviceOwned},
DeviceSize, Error, OomError, Version, VulkanObject,
};
use std::{
error,
ffi::c_void,
fmt,
fs::File,
hash::{Hash, Hasher},
mem::MaybeUninit,
ops::{BitOr, Range},
ptr, slice,
sync::{Arc, Mutex},
};
#[derive(Debug)]
pub struct DeviceMemory {
handle: ash::vk::DeviceMemory,
device: Arc<Device>,
allocation_size: DeviceSize,
memory_type_index: u32,
export_handle_types: ExternalMemoryHandleTypes,
mapped: Mutex<bool>,
}
impl DeviceMemory {
pub fn allocate(
device: Arc<Device>,
mut allocate_info: MemoryAllocateInfo,
) -> Result<Self, DeviceMemoryAllocationError> {
Self::validate(&device, &mut allocate_info, None)?;
let handle = unsafe { Self::create(&device, &allocate_info, None)? };
let MemoryAllocateInfo {
allocation_size,
memory_type_index,
dedicated_allocation,
export_handle_types,
_ne: _,
} = allocate_info;
Ok(DeviceMemory {
handle,
device,
allocation_size,
memory_type_index,
export_handle_types,
mapped: Mutex::new(false),
})
}
pub unsafe fn import(
device: Arc<Device>,
mut allocate_info: MemoryAllocateInfo,
mut import_info: MemoryImportInfo,
) -> Result<Self, DeviceMemoryAllocationError> {
Self::validate(&device, &mut allocate_info, Some(&mut import_info))?;
let handle = Self::create(&device, &allocate_info, Some(import_info))?;
let MemoryAllocateInfo {
allocation_size,
memory_type_index,
dedicated_allocation,
export_handle_types,
_ne: _,
} = allocate_info;
Ok(DeviceMemory {
handle,
device,
allocation_size,
memory_type_index,
export_handle_types,
mapped: Mutex::new(false),
})
}
fn validate(
device: &Device,
allocate_info: &mut MemoryAllocateInfo,
import_info: Option<&mut MemoryImportInfo>,
) -> Result<(), DeviceMemoryAllocationError> {
let &mut MemoryAllocateInfo {
allocation_size,
memory_type_index,
ref mut dedicated_allocation,
export_handle_types,
_ne: _,
} = allocate_info;
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_dedicated_allocation)
{
*dedicated_allocation = None;
}
let memory_type = device
.physical_device()
.memory_type_by_id(memory_type_index)
.ok_or_else(|| DeviceMemoryAllocationError::MemoryTypeIndexOutOfRange {
memory_type_index,
memory_type_count: device.physical_device().memory_types().len() as u32,
})?;
if memory_type.is_protected() && !device.enabled_features().protected_memory {
return Err(DeviceMemoryAllocationError::FeatureNotEnabled {
feature: "protected_memory",
reason: "selected memory type is protected",
});
}
assert!(allocation_size != 0);
let heap_size = memory_type.heap().size();
if heap_size != 0 && allocation_size > heap_size {
return Err(DeviceMemoryAllocationError::MemoryTypeHeapSizeExceeded {
allocation_size,
heap_size,
});
}
if let Some(dedicated_allocation) = dedicated_allocation {
match dedicated_allocation {
DedicatedAllocation::Buffer(buffer) => {
assert_eq!(device, buffer.device().as_ref());
let required_size = buffer.memory_requirements().size;
if allocation_size != required_size {
return Err(
DeviceMemoryAllocationError::DedicatedAllocationSizeMismatch {
allocation_size,
required_size,
},
);
}
}
DedicatedAllocation::Image(image) => {
assert_eq!(device, image.device().as_ref());
let required_size = image.memory_requirements().size;
if allocation_size != required_size {
return Err(
DeviceMemoryAllocationError::DedicatedAllocationSizeMismatch {
allocation_size,
required_size,
},
);
}
}
}
}
if export_handle_types.opaque_fd && !device.enabled_extensions().khr_external_memory_fd {
return Err(DeviceMemoryAllocationError::ExtensionNotEnabled {
extension: "khr_external_memory_fd",
reason: "`export_handle_types.opaque_fd` was set",
});
}
if export_handle_types.dma_buf && !device.enabled_extensions().ext_external_memory_dma_buf {
return Err(DeviceMemoryAllocationError::ExtensionNotEnabled {
extension: "ext_external_memory_dma_buf",
reason: "`export_handle_types.dma_buf` was set",
});
}
if let Some(import_info) = import_info {
match import_info {
&mut MemoryImportInfo::Fd {
handle_type,
ref file,
} => {
if !device.enabled_extensions().khr_external_memory_fd {
return Err(DeviceMemoryAllocationError::ExtensionNotEnabled {
extension: "khr_external_memory_fd",
reason: "`import_info` was `MemoryImportInfo::Fd`",
});
}
#[cfg(not(unix))]
unreachable!(
"`khr_external_memory_fd` was somehow enabled on a non-Unix system"
);
#[cfg(unix)]
{
match handle_type {
ExternalMemoryHandleType::OpaqueFd => {
}
ExternalMemoryHandleType::DmaBuf => {
if !device.enabled_extensions().ext_external_memory_dma_buf {
return Err(DeviceMemoryAllocationError::ExtensionNotEnabled {
extension: "ext_external_memory_dma_buf",
reason: "`import_info` was `MemoryImportInfo::Fd` and `handle_type` was `ExternalMemoryHandleType::DmaBuf`"
});
}
}
_ => {
return Err(
DeviceMemoryAllocationError::ImportFdHandleTypeNotSupported {
handle_type,
},
)
}
}
}
}
}
}
Ok(())
}
unsafe fn create(
device: &Device,
allocate_info: &MemoryAllocateInfo,
import_info: Option<MemoryImportInfo>,
) -> Result<ash::vk::DeviceMemory, DeviceMemoryAllocationError> {
let &MemoryAllocateInfo {
allocation_size,
memory_type_index,
dedicated_allocation,
export_handle_types,
_ne: _,
} = allocate_info;
let mut allocate_info = ash::vk::MemoryAllocateInfo::builder()
.allocation_size(allocation_size)
.memory_type_index(memory_type_index);
let mut dedicated_allocate_info = if let Some(dedicated_allocation) = dedicated_allocation {
Some(match dedicated_allocation {
DedicatedAllocation::Buffer(buffer) => ash::vk::MemoryDedicatedAllocateInfo {
buffer: buffer.internal_object(),
..Default::default()
},
DedicatedAllocation::Image(image) => ash::vk::MemoryDedicatedAllocateInfo {
image: image.internal_object(),
..Default::default()
},
})
} else {
None
};
if let Some(info) = dedicated_allocate_info.as_mut() {
allocate_info = allocate_info.push_next(info);
}
let mut export_allocate_info = if export_handle_types != ExternalMemoryHandleTypes::none() {
Some(ash::vk::ExportMemoryAllocateInfo {
handle_types: export_handle_types.into(),
..Default::default()
})
} else {
None
};
if let Some(info) = export_allocate_info.as_mut() {
allocate_info = allocate_info.push_next(info);
}
#[cfg(unix)]
let mut import_fd_info = match import_info {
Some(MemoryImportInfo::Fd { handle_type, file }) => {
use std::os::unix::io::IntoRawFd;
Some(ash::vk::ImportMemoryFdInfoKHR {
handle_type: handle_type.into(),
fd: file.into_raw_fd(),
..Default::default()
})
}
_ => None,
};
#[cfg(unix)]
if let Some(info) = import_fd_info.as_mut() {
allocate_info = allocate_info.push_next(info);
}
let mut allocation_count = device.allocation_count().lock().expect("Poisoned mutex");
if *allocation_count
>= device
.physical_device()
.properties()
.max_memory_allocation_count
{
return Err(DeviceMemoryAllocationError::TooManyObjects);
}
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.allocate_memory(
device.internal_object(),
&allocate_info.build(),
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
*allocation_count += 1;
Ok(handle)
}
#[inline]
pub fn memory_type(&self) -> MemoryType {
self.device
.physical_device()
.memory_type_by_id(self.memory_type_index)
.unwrap()
}
#[inline]
pub fn allocation_size(&self) -> DeviceSize {
self.allocation_size
}
#[inline]
pub fn export_fd(
&self,
handle_type: ExternalMemoryHandleType,
) -> Result<std::fs::File, DeviceMemoryExportError> {
if !matches!(
handle_type,
ExternalMemoryHandleType::OpaqueFd | ExternalMemoryHandleType::DmaBuf
) {
return Err(DeviceMemoryExportError::HandleTypeNotSupported { handle_type });
}
if !ash::vk::ExternalMemoryHandleTypeFlags::from(self.export_handle_types)
.intersects(ash::vk::ExternalMemoryHandleTypeFlags::from(handle_type))
{
return Err(DeviceMemoryExportError::HandleTypeNotSupported { handle_type });
}
debug_assert!(self.device().enabled_extensions().khr_external_memory_fd);
#[cfg(not(unix))]
unreachable!("`khr_external_memory_fd` was somehow enabled on a non-Unix system");
#[cfg(unix)]
{
use std::os::unix::io::FromRawFd;
let fd = unsafe {
let fns = self.device.fns();
let info = ash::vk::MemoryGetFdInfoKHR {
memory: self.handle,
handle_type: handle_type.into(),
..Default::default()
};
let mut output = MaybeUninit::uninit();
check_errors(fns.khr_external_memory_fd.get_memory_fd_khr(
self.device.internal_object(),
&info,
output.as_mut_ptr(),
))?;
output.assume_init()
};
let file = unsafe { std::fs::File::from_raw_fd(fd) };
Ok(file)
}
}
}
impl Drop for DeviceMemory {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0
.free_memory(self.device.internal_object(), self.handle, ptr::null());
let mut allocation_count = self
.device
.allocation_count()
.lock()
.expect("Poisoned mutex");
*allocation_count -= 1;
}
}
}
unsafe impl VulkanObject for DeviceMemory {
type Object = ash::vk::DeviceMemory;
#[inline]
fn internal_object(&self) -> ash::vk::DeviceMemory {
self.handle
}
}
unsafe impl DeviceOwned for DeviceMemory {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for DeviceMemory {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for DeviceMemory {}
impl Hash for DeviceMemory {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device.hash(state);
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeviceMemoryAllocationError {
OomError(OomError),
TooManyObjects,
MemoryMapError(MemoryMapError),
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
DedicatedAllocationSizeMismatch {
allocation_size: DeviceSize,
required_size: DeviceSize,
},
ImportFdHandleTypeNotSupported {
handle_type: ExternalMemoryHandleType,
},
MemoryTypeHeapSizeExceeded {
allocation_size: DeviceSize,
heap_size: DeviceSize,
},
MemoryTypeIndexOutOfRange {
memory_type_index: u32,
memory_type_count: u32,
},
SpecViolation(u32),
ImplicitSpecViolation(&'static str),
}
impl error::Error for DeviceMemoryAllocationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
Self::MemoryMapError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for DeviceMemoryAllocationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::TooManyObjects => {
write!(fmt, "the maximum number of allocations has been exceeded")
}
Self::MemoryMapError(_) => write!(fmt, "error occurred when mapping the memory"),
Self::ExtensionNotEnabled { extension, reason } => write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
),
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
Self::DedicatedAllocationSizeMismatch { allocation_size, required_size } => write!(
fmt,
"`dedicated_allocation` was `Some`, but the provided `allocation_size` ({}) was different from the required size of the buffer or image ({})",
allocation_size, required_size,
),
Self::ImportFdHandleTypeNotSupported { handle_type } => write!(
fmt,
"the provided `MemoryImportInfo::Fd::handle_type` ({:?}) is not supported for file descriptors",
handle_type,
),
Self::MemoryTypeHeapSizeExceeded { allocation_size, heap_size } => write!(
fmt,
"the provided `allocation_size` ({}) was greater than the memory type's heap size ({})",
allocation_size, heap_size,
),
Self::MemoryTypeIndexOutOfRange { memory_type_index, memory_type_count } => write!(
fmt,
"the provided `memory_type_index` ({}) was not less than the number of memory types in the physical device ({})",
memory_type_index, memory_type_count,
),
Self::SpecViolation(u) => {
write!(fmt, "valid usage ID check {} failed", u)
}
Self::ImplicitSpecViolation(e) => {
write!(fmt, "Implicit spec violation failed {}", e)
}
}
}
}
impl From<Error> for DeviceMemoryAllocationError {
#[inline]
fn from(err: Error) -> Self {
match err {
e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => Self::OomError(e.into()),
Error::TooManyObjects => Self::TooManyObjects,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for DeviceMemoryAllocationError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<MemoryMapError> for DeviceMemoryAllocationError {
#[inline]
fn from(err: MemoryMapError) -> Self {
Self::MemoryMapError(err)
}
}
#[derive(Clone, Debug)]
pub struct MemoryAllocateInfo<'d> {
pub allocation_size: DeviceSize,
pub memory_type_index: u32,
pub dedicated_allocation: Option<DedicatedAllocation<'d>>,
pub export_handle_types: ExternalMemoryHandleTypes,
pub _ne: crate::NonExhaustive,
}
impl Default for MemoryAllocateInfo<'static> {
#[inline]
fn default() -> Self {
Self {
allocation_size: 0,
memory_type_index: u32::MAX,
dedicated_allocation: None,
export_handle_types: ExternalMemoryHandleTypes::none(),
_ne: crate::NonExhaustive(()),
}
}
}
impl<'d> MemoryAllocateInfo<'d> {
pub fn dedicated_allocation(dedicated_allocation: DedicatedAllocation<'d>) -> Self {
Self {
allocation_size: 0,
memory_type_index: u32::MAX,
dedicated_allocation: Some(dedicated_allocation),
export_handle_types: ExternalMemoryHandleTypes::none(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum MemoryImportInfo {
Fd {
handle_type: ExternalMemoryHandleType,
file: File,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ExternalMemoryHandleType {
OpaqueFd = ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD.as_raw(),
OpaqueWin32 = ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32.as_raw(),
OpaqueWin32Kmt = ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KMT.as_raw(),
D3D11Texture = ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE.as_raw(),
D3D11TextureKmt = ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT.as_raw(),
D3D12Heap = ash::vk::ExternalMemoryHandleTypeFlags::D3D12_HEAP.as_raw(),
D3D12Resource = ash::vk::ExternalMemoryHandleTypeFlags::D3D12_RESOURCE.as_raw(),
DmaBuf = ash::vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT.as_raw(),
AndroidHardwareBuffer =
ash::vk::ExternalMemoryHandleTypeFlags::ANDROID_HARDWARE_BUFFER_ANDROID.as_raw(),
HostAllocation = ash::vk::ExternalMemoryHandleTypeFlags::HOST_ALLOCATION_EXT.as_raw(),
HostMappedForeignMemory =
ash::vk::ExternalMemoryHandleTypeFlags::HOST_MAPPED_FOREIGN_MEMORY_EXT.as_raw(),
}
impl From<ExternalMemoryHandleType> for ash::vk::ExternalMemoryHandleTypeFlags {
fn from(val: ExternalMemoryHandleType) -> Self {
Self::from_raw(val as u32)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ExternalMemoryHandleTypes {
pub opaque_fd: bool,
pub opaque_win32: bool,
pub opaque_win32_kmt: bool,
pub d3d11_texture: bool,
pub d3d11_texture_kmt: bool,
pub d3d12_heap: bool,
pub d3d12_resource: bool,
pub dma_buf: bool,
pub android_hardware_buffer: bool,
pub host_allocation: bool,
pub host_mapped_foreign_memory: bool,
}
impl ExternalMemoryHandleTypes {
#[inline]
pub fn none() -> Self {
ExternalMemoryHandleTypes {
opaque_fd: false,
opaque_win32: false,
opaque_win32_kmt: false,
d3d11_texture: false,
d3d11_texture_kmt: false,
d3d12_heap: false,
d3d12_resource: false,
dma_buf: false,
android_hardware_buffer: false,
host_allocation: false,
host_mapped_foreign_memory: false,
}
}
#[inline]
pub fn posix() -> ExternalMemoryHandleTypes {
ExternalMemoryHandleTypes {
opaque_fd: true,
..ExternalMemoryHandleTypes::none()
}
}
#[inline]
pub fn is_empty(&self) -> bool {
let ExternalMemoryHandleTypes {
opaque_fd,
opaque_win32,
opaque_win32_kmt,
d3d11_texture,
d3d11_texture_kmt,
d3d12_heap,
d3d12_resource,
dma_buf,
android_hardware_buffer,
host_allocation,
host_mapped_foreign_memory,
} = *self;
!(opaque_fd
|| opaque_win32
|| opaque_win32_kmt
|| d3d11_texture
|| d3d11_texture_kmt
|| d3d12_heap
|| d3d12_resource
|| dma_buf
|| android_hardware_buffer
|| host_allocation
|| host_mapped_foreign_memory)
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = ExternalMemoryHandleType> {
let ExternalMemoryHandleTypes {
opaque_fd,
opaque_win32,
opaque_win32_kmt,
d3d11_texture,
d3d11_texture_kmt,
d3d12_heap,
d3d12_resource,
dma_buf,
android_hardware_buffer,
host_allocation,
host_mapped_foreign_memory,
} = *self;
[
opaque_fd.then(|| ExternalMemoryHandleType::OpaqueFd),
opaque_win32.then(|| ExternalMemoryHandleType::OpaqueWin32),
opaque_win32_kmt.then(|| ExternalMemoryHandleType::OpaqueWin32Kmt),
d3d11_texture.then(|| ExternalMemoryHandleType::D3D11Texture),
d3d11_texture_kmt.then(|| ExternalMemoryHandleType::D3D11TextureKmt),
d3d12_heap.then(|| ExternalMemoryHandleType::D3D12Heap),
d3d12_resource.then(|| ExternalMemoryHandleType::D3D12Resource),
dma_buf.then(|| ExternalMemoryHandleType::DmaBuf),
android_hardware_buffer.then(|| ExternalMemoryHandleType::AndroidHardwareBuffer),
host_allocation.then(|| ExternalMemoryHandleType::HostAllocation),
host_mapped_foreign_memory.then(|| ExternalMemoryHandleType::HostMappedForeignMemory),
]
.into_iter()
.flatten()
}
}
impl From<ExternalMemoryHandleTypes> for ash::vk::ExternalMemoryHandleTypeFlags {
#[inline]
fn from(val: ExternalMemoryHandleTypes) -> Self {
let mut result = ash::vk::ExternalMemoryHandleTypeFlags::empty();
if val.opaque_fd {
result |= ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD;
}
if val.opaque_win32 {
result |= ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32;
}
if val.opaque_win32_kmt {
result |= ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KMT;
}
if val.d3d11_texture {
result |= ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE;
}
if val.d3d11_texture_kmt {
result |= ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT;
}
if val.d3d12_heap {
result |= ash::vk::ExternalMemoryHandleTypeFlags::D3D12_HEAP;
}
if val.d3d12_resource {
result |= ash::vk::ExternalMemoryHandleTypeFlags::D3D12_RESOURCE;
}
if val.dma_buf {
result |= ash::vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT;
}
if val.android_hardware_buffer {
result |= ash::vk::ExternalMemoryHandleTypeFlags::ANDROID_HARDWARE_BUFFER_ANDROID;
}
if val.host_allocation {
result |= ash::vk::ExternalMemoryHandleTypeFlags::HOST_ALLOCATION_EXT;
}
if val.host_mapped_foreign_memory {
result |= ash::vk::ExternalMemoryHandleTypeFlags::HOST_MAPPED_FOREIGN_MEMORY_EXT
}
result
}
}
impl From<ash::vk::ExternalMemoryHandleTypeFlags> for ExternalMemoryHandleTypes {
fn from(val: ash::vk::ExternalMemoryHandleTypeFlags) -> Self {
ExternalMemoryHandleTypes {
opaque_fd: !(val & ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD).is_empty(),
opaque_win32: !(val & ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32).is_empty(),
opaque_win32_kmt: !(val & ash::vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KMT)
.is_empty(),
d3d11_texture: !(val & ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE)
.is_empty(),
d3d11_texture_kmt: !(val & ash::vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT)
.is_empty(),
d3d12_heap: !(val & ash::vk::ExternalMemoryHandleTypeFlags::D3D12_HEAP).is_empty(),
d3d12_resource: !(val & ash::vk::ExternalMemoryHandleTypeFlags::D3D12_RESOURCE)
.is_empty(),
dma_buf: !(val & ash::vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT).is_empty(),
android_hardware_buffer: !(val
& ash::vk::ExternalMemoryHandleTypeFlags::ANDROID_HARDWARE_BUFFER_ANDROID)
.is_empty(),
host_allocation: !(val & ash::vk::ExternalMemoryHandleTypeFlags::HOST_ALLOCATION_EXT)
.is_empty(),
host_mapped_foreign_memory: !(val
& ash::vk::ExternalMemoryHandleTypeFlags::HOST_MAPPED_FOREIGN_MEMORY_EXT)
.is_empty(),
}
}
}
impl BitOr for ExternalMemoryHandleTypes {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
ExternalMemoryHandleTypes {
opaque_fd: self.opaque_fd || rhs.opaque_fd,
opaque_win32: self.opaque_win32 || rhs.opaque_win32,
opaque_win32_kmt: self.opaque_win32_kmt || rhs.opaque_win32_kmt,
d3d11_texture: self.d3d11_texture || rhs.d3d11_texture,
d3d11_texture_kmt: self.d3d11_texture_kmt || rhs.d3d11_texture_kmt,
d3d12_heap: self.d3d12_heap || rhs.d3d12_heap,
d3d12_resource: self.d3d12_resource || rhs.d3d12_resource,
dma_buf: self.dma_buf || rhs.dma_buf,
android_hardware_buffer: self.android_hardware_buffer || rhs.android_hardware_buffer,
host_allocation: self.host_allocation || rhs.host_allocation,
host_mapped_foreign_memory: self.host_mapped_foreign_memory
|| rhs.host_mapped_foreign_memory,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceMemoryExportError {
OomError(OomError),
TooManyObjects,
HandleTypeNotSupported {
handle_type: ExternalMemoryHandleType,
},
}
impl error::Error for DeviceMemoryExportError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for DeviceMemoryExportError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::TooManyObjects => {
write!(fmt, "the maximum number of allocations has been exceeded")
}
Self::HandleTypeNotSupported {
handle_type,
} => write!(
fmt,
"the requested export handle type ({:?}) is not supported for this operation, or was not provided in `export_handle_types` when allocating the memory",
handle_type,
),
}
}
}
impl From<Error> for DeviceMemoryExportError {
#[inline]
fn from(err: Error) -> Self {
match err {
e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => Self::OomError(e.into()),
Error::TooManyObjects => Self::TooManyObjects,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for DeviceMemoryExportError {
#[inline]
fn from(err: OomError) -> DeviceMemoryExportError {
Self::OomError(err)
}
}
#[derive(Debug)]
pub struct MappedDeviceMemory {
memory: DeviceMemory,
pointer: *mut c_void, range: Range<DeviceSize>,
atom_size: DeviceSize,
coherent: bool,
}
impl MappedDeviceMemory {
pub fn new(memory: DeviceMemory, range: Range<DeviceSize>) -> Result<Self, MemoryMapError> {
assert!(!range.is_empty());
if range.end > memory.allocation_size {
return Err(MemoryMapError::OutOfRange {
provided_range: range,
allowed_range: 0..memory.allocation_size,
});
}
if !memory.memory_type().is_host_visible() {
return Err(MemoryMapError::NotHostVisible);
}
let device = memory.device();
let coherent = memory.memory_type().is_host_coherent();
let atom_size = device.physical_device().properties().non_coherent_atom_size;
if !coherent
&& (range.start % atom_size != 0
|| (range.end % atom_size != 0 && range.end != memory.allocation_size))
{
return Err(MemoryMapError::RangeNotAlignedToAtomSize { range, atom_size });
}
let pointer = unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.map_memory(
device.internal_object(),
memory.handle,
range.start,
range.end - range.start,
ash::vk::MemoryMapFlags::empty(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(MappedDeviceMemory {
memory,
pointer,
range,
atom_size,
coherent,
})
}
pub fn unmap(self) -> DeviceMemory {
unsafe {
let device = self.memory.device();
let fns = device.fns();
fns.v1_0
.unmap_memory(device.internal_object(), self.memory.handle);
}
self.memory
}
pub unsafe fn invalidate_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
if self.coherent {
return Ok(());
}
self.check_range(range.clone())?;
let range = ash::vk::MappedMemoryRange {
memory: self.memory.internal_object(),
offset: range.start,
size: range.end - range.start,
..Default::default()
};
let fns = self.memory.device().fns();
check_errors(fns.v1_0.invalidate_mapped_memory_ranges(
self.memory.device().internal_object(),
1,
&range,
))?;
Ok(())
}
pub unsafe fn flush_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
self.check_range(range.clone())?;
if self.coherent {
return Ok(());
}
let range = ash::vk::MappedMemoryRange {
memory: self.memory.internal_object(),
offset: range.start,
size: range.end - range.start,
..Default::default()
};
let fns = self.device().fns();
check_errors(fns.v1_0.flush_mapped_memory_ranges(
self.memory.device().internal_object(),
1,
&range,
))?;
Ok(())
}
pub unsafe fn read(&self, range: Range<DeviceSize>) -> Result<&[u8], MemoryMapError> {
self.check_range(range.clone())?;
let bytes = slice::from_raw_parts(
self.pointer.add((range.start - self.range.start) as usize) as *const u8,
(range.end - range.start) as usize,
);
Ok(bytes)
}
pub unsafe fn write(&self, range: Range<DeviceSize>) -> Result<&mut [u8], MemoryMapError> {
self.check_range(range.clone())?;
let bytes = slice::from_raw_parts_mut(
self.pointer.add((range.start - self.range.start) as usize) as *mut u8,
(range.end - range.start) as usize,
);
Ok(bytes)
}
#[inline]
fn check_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
assert!(!range.is_empty());
if range.start < self.range.start || range.end > self.range.end {
return Err(MemoryMapError::OutOfRange {
provided_range: range,
allowed_range: self.range.clone(),
});
}
if !self.coherent {
if range.start % self.atom_size != 0
|| (range.end % self.atom_size != 0 && range.end != self.memory.allocation_size)
{
return Err(MemoryMapError::RangeNotAlignedToAtomSize {
range,
atom_size: self.atom_size,
});
}
}
Ok(())
}
}
impl AsRef<DeviceMemory> for MappedDeviceMemory {
#[inline]
fn as_ref(&self) -> &DeviceMemory {
&self.memory
}
}
impl AsMut<DeviceMemory> for MappedDeviceMemory {
#[inline]
fn as_mut(&mut self) -> &mut DeviceMemory {
&mut self.memory
}
}
unsafe impl DeviceOwned for MappedDeviceMemory {
#[inline]
fn device(&self) -> &Arc<Device> {
self.memory.device()
}
}
unsafe impl Send for MappedDeviceMemory {}
unsafe impl Sync for MappedDeviceMemory {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MemoryMapError {
OomError(OomError),
MemoryMapFailed,
NotHostVisible,
OutOfRange {
provided_range: Range<DeviceSize>,
allowed_range: Range<DeviceSize>,
},
RangeNotAlignedToAtomSize {
range: Range<DeviceSize>,
atom_size: DeviceSize,
},
}
impl error::Error for MemoryMapError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for MemoryMapError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::MemoryMapFailed => write!(fmt, "memory map failed"),
Self::NotHostVisible => write!(
fmt,
"tried to map memory whose type is not host-visible",
),
Self::OutOfRange { ref provided_range, ref allowed_range } => write!(
fmt,
"the specified `range` ({:?}) was not contained within the allocated or mapped memory range ({:?})",
provided_range, allowed_range,
),
Self::RangeNotAlignedToAtomSize { ref range, atom_size } => write!(
fmt,
"the memory is not host-coherent, and the specified `range` bounds ({:?}) are not a multiple of the `non_coherent_atom_size` device property ({})",
range, atom_size,
)
}
}
}
impl From<Error> for MemoryMapError {
#[inline]
fn from(err: Error) -> Self {
match err {
e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => Self::OomError(e.into()),
Error::MemoryMapFailed => Self::MemoryMapFailed,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for MemoryMapError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
#[cfg(test)]
mod tests {
use super::MemoryAllocateInfo;
use crate::memory::DeviceMemory;
use crate::memory::DeviceMemoryAllocationError;
use crate::OomError;
#[test]
fn create() {
let (device, _) = gfx_dev_and_queue!();
let memory_type = device.physical_device().memory_types().next().unwrap();
let _ = DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: 256,
memory_type_index: memory_type.id(),
..Default::default()
},
)
.unwrap();
}
#[test]
fn zero_size() {
let (device, _) = gfx_dev_and_queue!();
let memory_type = device.physical_device().memory_types().next().unwrap();
assert_should_panic!({
let _ = DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: 0,
memory_type_index: memory_type.id(),
..Default::default()
},
)
.unwrap();
});
}
#[test]
#[cfg(target_pointer_width = "64")]
fn oom_single() {
let (device, _) = gfx_dev_and_queue!();
let memory_type = device
.physical_device()
.memory_types()
.filter(|m| !m.is_lazily_allocated())
.next()
.unwrap();
match DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: 0xffffffffffffffff,
memory_type_index: memory_type.id(),
..Default::default()
},
) {
Err(DeviceMemoryAllocationError::MemoryTypeHeapSizeExceeded { .. }) => (),
_ => panic!(),
}
}
#[test]
#[ignore] fn oom_multi() {
let (device, _) = gfx_dev_and_queue!();
let memory_type = device
.physical_device()
.memory_types()
.filter(|m| !m.is_lazily_allocated())
.next()
.unwrap();
let heap_size = memory_type.heap().size();
let mut allocs = Vec::new();
for _ in 0..4 {
match DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: heap_size / 3,
memory_type_index: memory_type.id(),
..Default::default()
},
) {
Err(DeviceMemoryAllocationError::OomError(OomError::OutOfDeviceMemory)) => return, Ok(a) => allocs.push(a),
_ => (),
}
}
panic!()
}
#[test]
fn allocation_count() {
let (device, _) = gfx_dev_and_queue!();
let memory_type = device.physical_device().memory_types().next().unwrap();
assert_eq!(*device.allocation_count().lock().unwrap(), 0);
let mem1 = DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: 256,
memory_type_index: memory_type.id(),
..Default::default()
},
)
.unwrap();
assert_eq!(*device.allocation_count().lock().unwrap(), 1);
{
let mem2 = DeviceMemory::allocate(
device.clone(),
MemoryAllocateInfo {
allocation_size: 256,
memory_type_index: memory_type.id(),
..Default::default()
},
)
.unwrap();
assert_eq!(*device.allocation_count().lock().unwrap(), 2);
}
assert_eq!(*device.allocation_count().lock().unwrap(), 1);
}
}