use super::*;
#[derive(Clone, Copy)]
pub struct CacheRange<'a> {
pub allocation: &'a Allocation,
pub offset: u64,
pub size: u64,
}
impl<'a> CacheRange<'a> {
#[inline]
#[must_use]
pub fn new(allocation: &'a Allocation) -> Self {
Self {
allocation,
offset: 0,
size: vk::WHOLE_SIZE,
}
}
#[inline]
#[must_use]
pub fn set_allocation(mut self, value: &'a Allocation) -> Self {
self.allocation = value;
self
}
#[inline]
#[must_use]
pub fn set_offset(mut self, value: u64) -> Self {
self.offset = value;
self
}
#[inline]
#[must_use]
pub fn set_size(mut self, value: u64) -> Self {
self.size = value;
self
}
}
pub unsafe fn allocate_many(
allocator: &mut Allocator<'_>,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo,
count: usize,
) -> Result<Vec<Allocation>, AllocError> {
if count > 1
&& options
.memory_allocate_next
.is_some_and(|chain| chain.data.imports)
{
return Err(AllocError::InvalidRequest(
"one imported handle cannot back multiple allocations",
));
}
allocator.ensure_open()?;
allocator
.next_serial
.checked_add(u64::try_from(count).map_err(|_| AllocError::SerialExhausted)?)
.ok_or(AllocError::SerialExhausted)?;
let mut allocations = Vec::new();
allocations
.try_reserve_exact(count)
.map_err(|_| AllocError::HostAllocationFailed)?;
for _ in 0..count {
match unsafe { allocate_with_options(allocator, requirements, options) } {
Ok(allocation) => allocations.push(allocation),
Err(error) => {
for allocation in allocations.iter_mut().rev() {
unsafe { rollback_allocation(allocator, allocation) }.expect("unpublished page rollback");
}
return Err(error);
}
}
}
Ok(allocations)
}
pub unsafe fn free_many(
allocator: &mut Allocator<'_>,
allocations: &mut [Allocation],
) -> Result<(), AllocError> {
allocator.ensure_open()?;
for (index, allocation) in allocations.iter().enumerate() {
if allocator.validate(allocation)?.map_count != 0 {
return Err(AllocError::AlreadyMapped);
}
if allocations[..index]
.iter()
.any(|previous| previous.serial == allocation.serial)
{
return Err(AllocError::InvalidRequest(
"duplicate allocation in release batch",
));
}
}
for allocation in allocations {
unsafe { free(allocator, allocation) }?;
}
Ok(())
}
fn cache_ranges<'device>(
allocator: &Allocator<'device>,
ranges: &[CacheRange<'_>],
) -> Result<HostVec<'device, vk::MappedMemoryRange<'static>>, AllocError> {
allocator.ensure_open()?;
let mut native = HostVec::new(allocator.host);
native
.try_reserve_exact(ranges.len())
.map_err(|_| AllocError::HostAllocationFailed)?;
for range in ranges {
let live = allocator.validate(range.allocation)?;
if live.map_count == 0 && !live.persistent {
return Err(AllocError::NotMapped);
}
if let Some(range) = range.allocation.mapped_range(range.offset, range.size)? {
native.push_reserved(range);
}
}
u32::try_from(native.len()).map_err(|_| AllocError::InvalidRange)?;
Ok(native)
}
pub unsafe fn flush_many(
allocator: &Allocator<'_>,
ranges: &[CacheRange<'_>],
) -> Result<(), AllocError> {
let native = cache_ranges(allocator, ranges)?;
if !native.is_empty() {
unsafe { allocator.device.flush_mapped_memory_ranges(&native) }?;
}
Ok(())
}
pub unsafe fn invalidate_many(
allocator: &Allocator<'_>,
ranges: &[CacheRange<'_>],
) -> Result<(), AllocError> {
let native = cache_ranges(allocator, ranges)?;
if !native.is_empty() {
unsafe { allocator.device.invalidate_mapped_memory_ranges(&native) }?;
}
Ok(())
}
fn copy_range(
allocator: &Allocator<'_>,
allocation: &Allocation,
offset: u64,
length: usize,
write: bool,
) -> Result<u64, AllocError> {
allocator.validate(allocation)?;
let size = u64::try_from(length).map_err(|_| AllocError::InvalidRange)?;
if offset
.checked_add(size)
.is_none_or(|end| end > allocation.size)
{
return Err(AllocError::InvalidRange);
}
if size != 0 && allocation.mapped_range(offset, size)?.is_some() {
if write {
require(
allocator.device.fp().flush_mapped_memory_ranges.is_some(),
"vkFlushMappedMemoryRanges",
)?;
} else {
require(
allocator
.device
.as_device()
.fp()
.invalidate_mapped_memory_ranges
.is_some(),
"vkInvalidateMappedMemoryRanges",
)?;
}
}
Ok(size)
}
pub unsafe fn copy_to_allocation(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
offset: u64,
source: &[u8],
) -> Result<(), AllocError> {
let size = copy_range(allocator, allocation, offset, source.len(), true)?;
if size == 0 {
return Ok(());
}
let pointer_offset = usize::try_from(offset).map_err(|_| AllocError::InvalidRange)?;
let pointer = unsafe { map(allocator, allocation) }?;
unsafe {
std::ptr::copy(
source.as_ptr(),
pointer.cast::<u8>().add(pointer_offset),
source.len(),
)
};
let result = unsafe { flush(allocator, allocation, offset, size) };
let cleanup = unsafe { unmap(allocator, allocation) };
result.and(cleanup)
}
pub unsafe fn copy_from_allocation(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
offset: u64,
destination: &mut [u8],
) -> Result<(), AllocError> {
let size = copy_range(allocator, allocation, offset, destination.len(), false)?;
if size == 0 {
return Ok(());
}
let pointer_offset = usize::try_from(offset).map_err(|_| AllocError::InvalidRange)?;
let pointer = unsafe { map(allocator, allocation) }?;
let result = unsafe { invalidate(allocator, allocation, offset, size) };
if result.is_ok() {
unsafe {
std::ptr::copy(
pointer.cast::<u8>().add(pointer_offset),
destination.as_mut_ptr(),
destination.len(),
)
};
}
let cleanup = unsafe { unmap(allocator, allocation) };
result.and(cleanup)
}
fn name_copy<'callbacks>(
host: HostMemory<'callbacks>,
name: Option<&str>,
) -> Result<Option<HostString<'callbacks>>, AllocError> {
name
.map(|name| HostString::new_from_str(host, name))
.transpose()
}
pub fn allocation_name<'a>(
allocator: &'a Allocator<'_>,
allocation: &Allocation,
) -> Result<Option<&'a str>, AllocError> {
Ok(allocator.validate(allocation)?.name.as_deref())
}
pub fn set_allocation_name(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
name: Option<&str>,
) -> Result<(), AllocError> {
allocator.validate(allocation)?;
let name = name_copy(allocator.host, name)?;
allocator
.live
.get_mut(&allocation.serial)
.expect("validated allocation")
.name = name;
Ok(())
}
pub fn set_allocation_user_data(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
data: usize,
) -> Result<(), AllocError> {
allocator.validate(allocation)?;
allocator
.live
.get_mut(&allocation.serial)
.expect("validated allocation")
.user_data = data;
Ok(())
}
pub fn pool_name<'a>(
allocator: &'a Allocator<'_>,
pool: &Pool,
) -> Result<Option<&'a str>, AllocError> {
Ok(pool::validate(allocator, pool)?.name.as_deref())
}
pub fn set_pool_name(
allocator: &mut Allocator<'_>,
pool: &Pool,
name: Option<&str>,
) -> Result<(), AllocError> {
pool::validate(allocator, pool)?;
let name = name_copy(allocator.host, name)?;
allocator
.pools
.get_mut(&pool.serial)
.expect("validated pool")
.name = name;
Ok(())
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DetailedStatistics {
pub native_count: usize,
pub native_bytes: u64,
pub allocation_count: usize,
pub allocation_bytes: u64,
pub reserved_bytes: u64,
pub free_range_count: usize,
pub free_bytes: u64,
pub allocation_size_min: Option<u64>,
pub allocation_size_max: Option<u64>,
pub free_range_size_min: Option<u64>,
pub free_range_size_max: Option<u64>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct MemoryStatistics {
pub total: DetailedStatistics,
pub memory_types: [DetailedStatistics; 32],
pub heaps: [DetailedStatistics; 16],
}
impl MemoryStatistics {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn set_total(mut self, value: DetailedStatistics) -> Self {
self.total = value;
self
}
#[inline]
#[must_use]
pub fn set_memory_types(mut self, value: [DetailedStatistics; 32]) -> Self {
self.memory_types = value;
self
}
#[inline]
#[must_use]
pub fn set_heaps(mut self, value: [DetailedStatistics; 16]) -> Self {
self.heaps = value;
self
}
}
impl DetailedStatistics {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn set_native_count(mut self, value: usize) -> Self {
self.native_count = value;
self
}
#[inline]
#[must_use]
pub fn set_native_bytes(mut self, value: u64) -> Self {
self.native_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_count(mut self, value: usize) -> Self {
self.allocation_count = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_bytes(mut self, value: u64) -> Self {
self.allocation_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_reserved_bytes(mut self, value: u64) -> Self {
self.reserved_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_free_range_count(mut self, value: usize) -> Self {
self.free_range_count = value;
self
}
#[inline]
#[must_use]
pub fn set_free_bytes(mut self, value: u64) -> Self {
self.free_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_size_min(mut self, value: Option<u64>) -> Self {
self.allocation_size_min = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_size_max(mut self, value: Option<u64>) -> Self {
self.allocation_size_max = value;
self
}
#[inline]
#[must_use]
pub fn set_free_range_size_min(mut self, value: Option<u64>) -> Self {
self.free_range_size_min = value;
self
}
#[inline]
#[must_use]
pub fn set_free_range_size_max(mut self, value: Option<u64>) -> Self {
self.free_range_size_max = value;
self
}
fn include(&mut self, other: Self) {
self.native_count += other.native_count;
self.native_bytes += other.native_bytes;
self.allocation_count += other.allocation_count;
self.allocation_bytes += other.allocation_bytes;
self.reserved_bytes += other.reserved_bytes;
self.free_range_count += other.free_range_count;
self.free_bytes += other.free_bytes;
for (target, value, minimum) in [
(
&mut self.allocation_size_min,
other.allocation_size_min,
true,
),
(
&mut self.allocation_size_max,
other.allocation_size_max,
false,
),
(
&mut self.free_range_size_min,
other.free_range_size_min,
true,
),
(
&mut self.free_range_size_max,
other.free_range_size_max,
false,
),
] {
if let Some(value) = value {
*target = Some(target.map_or(value, |old| {
if minimum {
old.min(value)
} else {
old.max(value)
}
}));
}
}
}
}
pub fn detailed_statistics(allocator: &Allocator<'_>) -> Result<MemoryStatistics, AllocError> {
collect_statistics(allocator, None)
}
pub fn detailed_pool_statistics(
allocator: &Allocator<'_>,
pool: &Pool,
) -> Result<DetailedStatistics, AllocError> {
pool::validate(allocator, pool)?;
Ok(collect_statistics(allocator, Some(pool.serial))?.total)
}
fn collect_statistics(
allocator: &Allocator<'_>,
pool_filter: Option<u64>,
) -> Result<MemoryStatistics, AllocError> {
allocator.ensure_open()?;
let mut result = MemoryStatistics::new();
for block in &allocator.blocks {
if pool_filter.is_some() && block.pool != pool_filter {
continue;
}
let stats = &mut result.memory_types[block.memory_type_index as usize];
stats.native_count += 1;
stats.native_bytes += block.size;
block.metadata.for_each_free(|_, size| {
stats.free_range_count += 1;
stats.free_bytes += size;
stats.free_range_size_min = Some(stats.free_range_size_min.map_or(size, |old| old.min(size)));
stats.free_range_size_max = Some(stats.free_range_size_max.map_or(size, |old| old.max(size)));
});
}
for live in allocator.live.values() {
if pool_filter.is_some() && live.pool != pool_filter {
continue;
}
let stats = &mut result.memory_types[live.memory_type_index as usize];
if live.placement.is_none() {
stats.native_count += 1;
stats.native_bytes += live.size;
}
stats.allocation_count += 1;
stats.allocation_bytes += live.size;
stats.reserved_bytes += live.reserved_size;
stats.allocation_size_min = Some(
stats
.allocation_size_min
.map_or(live.size, |old| old.min(live.size)),
);
stats.allocation_size_max = Some(
stats
.allocation_size_max
.map_or(live.size, |old| old.max(live.size)),
);
}
for index in 0..allocator.selector.properties.memory_type_count as usize {
let stats = result.memory_types[index];
result.total.include(stats);
result.heaps[allocator.selector.properties.memory_types[index].heap_index as usize]
.include(stats);
}
Ok(result)
}
pub fn write_statistics_json(
allocator: &Allocator<'_>,
output: &mut impl std::io::Write,
detailed: bool,
) -> Result<(), AllocError> {
let stats = detailed_statistics(allocator)?;
let mut allocations = HostVec::new(allocator.host);
if detailed {
allocations
.try_reserve_exact(allocator.live.len())
.map_err(|_| AllocError::HostAllocationFailed)?;
for entry in allocator.live.iter() {
allocations.push_reserved(entry);
}
allocations.sort_unstable_by_key(|(serial, _)| **serial);
}
let mut pools = HostVec::new(allocator.host);
pools
.try_reserve_exact(allocator.pools.len())
.map_err(|_| AllocError::HostAllocationFailed)?;
for entry in allocator.pools.iter() {
pools.push_reserved(entry);
}
pools.sort_unstable_by_key(|(serial, _)| **serial);
fn quote(out: &mut impl std::io::Write, text: &str) -> std::io::Result<()> {
out.write_all(b"\"")?;
for value in text.chars() {
match value {
'"' => out.write_all(b"\\\"")?,
'\\' => out.write_all(b"\\\\")?,
value if value <= '\u{1f}' => write!(out, "\\u{:04x}", u32::from(value))?,
value => {
let mut bytes = [0; 4];
out.write_all(value.encode_utf8(&mut bytes).as_bytes())?;
}
}
}
out.write_all(b"\"")
}
fn number(out: &mut impl std::io::Write, value: Option<u64>) -> std::io::Result<()> {
if let Some(value) = value {
write!(out, "{value}")
} else {
out.write_all(b"null")
}
}
fn statistics(out: &mut impl std::io::Write, stats: &DetailedStatistics) -> std::io::Result<()> {
write!(
out,
"{{\"native_count\":{},\"native_bytes\":{},\"allocation_count\":{},\"allocation_bytes\":{},\"reserved_bytes\":{},\"free_range_count\":{},\"free_bytes\":{},\"allocation_size_min\":",
stats.native_count,
stats.native_bytes,
stats.allocation_count,
stats.allocation_bytes,
stats.reserved_bytes,
stats.free_range_count,
stats.free_bytes
)?;
number(out, stats.allocation_size_min)?;
out.write_all(b",\"allocation_size_max\":")?;
number(out, stats.allocation_size_max)?;
out.write_all(b",\"free_range_size_min\":")?;
number(out, stats.free_range_size_min)?;
out.write_all(b",\"free_range_size_max\":")?;
number(out, stats.free_range_size_max)?;
out.write_all(b"}")
}
let written = (|| -> std::io::Result<()> {
output.write_all(b"{\"schema\":\"vk.memory.statistics.v1\",\"total\":")?;
statistics(output, &stats.total)?;
output.write_all(b",\"memory_types\":[")?;
for index in 0..allocator.selector.properties.memory_type_count as usize {
if index != 0 {
output.write_all(b",")?;
}
statistics(output, &stats.memory_types[index])?;
}
output.write_all(b"],\"heaps\":[")?;
for index in 0..allocator.selector.properties.memory_heap_count as usize {
if index != 0 {
output.write_all(b",")?;
}
statistics(output, &stats.heaps[index])?;
}
output.write_all(b"],\"pools\":[")?;
for (index, (serial, pool)) in pools.iter().enumerate() {
if index != 0 {
output.write_all(b",")?;
}
write!(
output,
"{{\"id\":{serial},\"memory_type\":{},\"name\":",
pool.info.memory_type_index
)?;
if let Some(name) = &pool.name {
quote(output, name)?;
} else {
output.write_all(b"null")?;
}
output.write_all(b"}")?;
}
output.write_all(b"]")?;
if detailed {
output.write_all(b",\"allocations\":[")?;
for (index, (serial, live)) in allocations.iter().enumerate() {
if index != 0 {
output.write_all(b",")?;
}
write!(
output,
"{{\"id\":{serial},\"offset\":{},\"size\":{},\"reserved_size\":{},\"memory_type\":{},\"user_data\":{},\"map_count\":{},\"persistent\":{},\"pinned\":{},\"name\":",
live.offset,
live.size,
live.reserved_size,
live.memory_type_index,
live.user_data,
live.map_count,
live.persistent,
live.pinned
)?;
if let Some(name) = &live.name {
quote(output, name)?;
} else {
output.write_all(b"null")?;
}
output.write_all(b"}")?;
}
output.write_all(b"],\"blocks\":[")?;
for (index, block) in allocator.blocks.iter().enumerate() {
if index != 0 {
output.write_all(b",")?;
}
write!(
output,
"{{\"memory_type\":{},\"size\":{},\"free_ranges\":[",
block.memory_type_index, block.size
)?;
let mut first = true;
let mut error = None;
block.metadata.for_each_free(|offset, size| {
if error.is_some() {
return;
}
let result = (|| {
if !first {
output.write_all(b",")?;
}
first = false;
write!(output, "[{offset},{size}]")
})();
if let Err(failure) = result {
error = Some(failure);
}
});
if let Some(error) = error {
return Err(error);
}
output.write_all(b"]}")?;
}
output.write_all(b"]")?;
}
output.write_all(b"}\n")
})();
written.map_err(|error| AllocError::Output {
kind: error.kind(),
code: error.raw_os_error(),
})
}
impl Allocator<'_> {
pub fn detailed_pool_statistics(&self, pool: &Pool) -> Result<DetailedStatistics, AllocError> {
detailed_pool_statistics(self, pool)
}
pub fn write_statistics_json(
&self,
output: &mut impl std::io::Write,
detailed: bool,
) -> Result<(), AllocError> {
write_statistics_json(self, output, detailed)
}
pub fn detailed_statistics(&self) -> Result<MemoryStatistics, AllocError> {
detailed_statistics(self)
}
pub unsafe fn allocate_many(
&mut self,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo,
count: usize,
) -> Result<Vec<Allocation>, AllocError> {
unsafe { allocate_many(self, requirements, options, count) }
}
pub unsafe fn free_many(&mut self, allocations: &mut [Allocation]) -> Result<(), AllocError> {
unsafe { free_many(self, allocations) }
}
pub unsafe fn flush_many(&self, ranges: &[CacheRange<'_>]) -> Result<(), AllocError> {
unsafe { flush_many(self, ranges) }
}
pub unsafe fn invalidate_many(&self, ranges: &[CacheRange<'_>]) -> Result<(), AllocError> {
unsafe { invalidate_many(self, ranges) }
}
pub unsafe fn copy_to_allocation(
&mut self,
allocation: &Allocation,
offset: u64,
source: &[u8],
) -> Result<(), AllocError> {
unsafe { copy_to_allocation(self, allocation, offset, source) }
}
pub unsafe fn copy_from_allocation(
&mut self,
allocation: &Allocation,
offset: u64,
destination: &mut [u8],
) -> Result<(), AllocError> {
unsafe { copy_from_allocation(self, allocation, offset, destination) }
}
pub fn allocation_name<'a>(
&'a self,
allocation: &Allocation,
) -> Result<Option<&'a str>, AllocError> {
allocation_name(self, allocation)
}
pub fn set_allocation_name(
&mut self,
allocation: &Allocation,
name: Option<&str>,
) -> Result<(), AllocError> {
set_allocation_name(self, allocation, name)
}
pub fn set_allocation_user_data(
&mut self,
allocation: &Allocation,
data: usize,
) -> Result<(), AllocError> {
set_allocation_user_data(self, allocation, data)
}
pub fn pool_name<'a>(&'a self, pool: &Pool) -> Result<Option<&'a str>, AllocError> {
pool_name(self, pool)
}
pub fn set_pool_name(&mut self, pool: &Pool, name: Option<&str>) -> Result<(), AllocError> {
set_pool_name(self, pool, name)
}
}
pub unsafe fn create_aliasing_buffer(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
local_offset: u64,
info: &vk::BufferCreateInfo,
) -> Result<vk::Buffer, AllocError> {
if !allocator.validate(allocation)?.can_alias {
return Err(AllocError::UnsupportedCapability(
"allocation was not created for aliasing",
));
}
unsafe { request::validate_buffer(allocator, info) }?;
let required_exports = unsafe { external::required_resource_types(info.p_next) };
if !allocator.validate(allocation)?.imported
&& external::exports(allocator, allocation)?.as_raw() & required_exports.as_raw()
!= required_exports.as_raw()
{
return Err(AllocError::InvalidRequest(
"alias resource export types do not match backing memory",
));
}
require(
allocator.device.fp().create_buffer.is_some(),
"vkCreateBuffer",
)?;
require(
allocator.device.fp().destroy_buffer.is_some(),
"vkDestroyBuffer",
)?;
let buffer = unsafe { allocator.device.create_buffer(info, allocator.callbacks) }?;
if buffer.is_null() || binding::associated(allocator, Resource::Buffer(buffer)) {
return Err(CallError::InvalidOutput("alias buffer is null or already live").into());
}
if let Err(error) = unsafe {
binding::bind_alias(
allocator,
Resource::Buffer(buffer),
allocation,
local_offset,
)
} {
unsafe { allocator.device.destroy_buffer(buffer, allocator.callbacks) }
.expect("preflighted alias cleanup");
return Err(error);
}
Ok(buffer)
}
pub unsafe fn create_aliasing_image(
allocator: &mut Allocator<'_>,
allocation: &Allocation,
local_offset: u64,
info: &vk::ImageCreateInfo,
) -> Result<vk::Image, AllocError> {
if !allocator.validate(allocation)?.can_alias {
return Err(AllocError::UnsupportedCapability(
"allocation was not created for aliasing",
));
}
if info.flags.as_raw() & vk::ImageCreateFlagBits::ALIAS_BIT.as_raw() == 0 {
return Err(AllocError::InvalidRequest("alias images require ALIAS_BIT"));
}
unsafe { request::validate_image(allocator, info) }?;
let required_exports = unsafe { external::required_resource_types(info.p_next) };
if !allocator.validate(allocation)?.imported
&& external::exports(allocator, allocation)?.as_raw() & required_exports.as_raw()
!= required_exports.as_raw()
{
return Err(AllocError::InvalidRequest(
"alias resource export types do not match backing memory",
));
}
require(
allocator.device.fp().create_image.is_some(),
"vkCreateImage",
)?;
require(
allocator.device.fp().destroy_image.is_some(),
"vkDestroyImage",
)?;
let image = unsafe { allocator.device.create_image(info, allocator.callbacks) }?;
if image.is_null() || binding::associated(allocator, Resource::Image(image)) {
return Err(CallError::InvalidOutput("alias image is null or already live").into());
}
if let Err(error) =
unsafe { binding::bind_alias(allocator, Resource::Image(image), allocation, local_offset) }
{
unsafe { allocator.device.destroy_image(image, allocator.callbacks) }
.expect("preflighted alias cleanup");
return Err(error);
}
Ok(image)
}
impl Allocator<'_> {
pub fn allocation_callbacks(&self) -> Option<&vk::AllocationCallbacks> {
self.callbacks
}
pub unsafe fn create_aliasing_buffer(
&mut self,
allocation: &Allocation,
local_offset: u64,
info: &vk::BufferCreateInfo,
) -> Result<vk::Buffer, AllocError> {
unsafe { create_aliasing_buffer(self, allocation, local_offset, info) }
}
pub unsafe fn create_aliasing_image(
&mut self,
allocation: &Allocation,
local_offset: u64,
info: &vk::ImageCreateInfo,
) -> Result<vk::Image, AllocError> {
unsafe { create_aliasing_image(self, allocation, local_offset, info) }
}
}
pub fn allocator_info(
allocator: &Allocator<'_>,
) -> Result<super::super::AllocatorInfo, AllocError> {
allocator.ensure_open()?;
Ok(super::super::AllocatorInfo {
device: allocator.device.handle(),
instance: allocator.instance_handle,
physical_device: allocator.physical_device,
api_version: allocator
.device
.as_device()
.capabilities()
.map(|caps| caps.api_version()),
})
}
pub fn memory_properties<'a>(
allocator: &'a Allocator<'_>,
) -> Result<&'a vk::PhysicalDeviceMemoryProperties, AllocError> {
allocator.ensure_open()?;
Ok(&allocator.selector.properties)
}
pub fn physical_device_properties<'a>(
allocator: &'a Allocator<'_>,
) -> Result<Option<&'a vk::PhysicalDeviceProperties>, AllocError> {
allocator.ensure_open()?;
Ok(allocator.physical_properties.as_ref())
}
pub fn memory_type_properties(
allocator: &Allocator<'_>,
index: u32,
) -> Result<vk::MemoryPropertyFlags, AllocError> {
allocator.ensure_open()?;
if index >= allocator.selector.properties.memory_type_count {
return Err(AllocError::InvalidRequest("absent memory type"));
}
Ok(allocator.selector.properties.memory_types[index as usize].property_flags)
}
impl<'device> Allocator<'device> {
pub unsafe fn set_device_memory_callbacks(
&mut self,
callbacks: Option<&'device super::super::DeviceMemoryCallbacks>,
) -> Result<(), AllocError> {
self.ensure_open()?;
self.ensure_global()?;
if self.native_count != 0 || !self.live.is_empty() {
return Err(AllocError::LiveAllocations);
}
if !self.pools.is_empty() {
return Err(AllocError::LivePools);
}
self.device_memory_callbacks = callbacks;
Ok(())
}
pub fn info(&self) -> Result<super::super::AllocatorInfo, AllocError> {
allocator_info(self)
}
pub fn memory_properties(&self) -> Result<&vk::PhysicalDeviceMemoryProperties, AllocError> {
memory_properties(self)
}
pub fn physical_device_properties(
&self,
) -> Result<Option<&vk::PhysicalDeviceProperties>, AllocError> {
physical_device_properties(self)
}
pub fn memory_type_properties(&self, index: u32) -> Result<vk::MemoryPropertyFlags, AllocError> {
memory_type_properties(self, index)
}
}