use super::*;
pub(super) unsafe fn validate_buffer(
allocator: &Allocator<'_>,
info: &vk::BufferCreateInfo,
) -> Result<bool, AllocError> {
allocator.ensure_open()?;
unsafe {
super::super::resource::validate_chain(allocator.device.as_device(), info.p_next, false)
}?;
let protected = info.flags.as_raw() & vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
if protected && !allocator.protected_memory {
return Err(AllocError::UnsupportedCapability(
"protected buffer requires enabled feature metadata",
));
}
if info.flags.as_raw() & !vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0
|| info.size == 0
|| info.usage.as_raw() == 0
|| info.usage.as_raw() & vk::BufferUsageFlagBits::TILE_MEMORY_BIT_QCOM.as_raw() != 0
{
return Err(AllocError::InvalidRequest("ordinary nonempty buffers only"));
}
let device_address =
info.usage.as_raw() & vk::BufferUsageFlagBits::SHADER_DEVICE_ADDRESS_BIT.as_raw() != 0;
if device_address && !allocator.buffer_device_address {
return Err(AllocError::UnsupportedCapability(
"buffer device address requires enabled core/KHR feature metadata",
));
}
Ok(device_address)
}
pub(super) unsafe fn validate_image(
allocator: &Allocator<'_>,
info: &vk::ImageCreateInfo,
) -> Result<ResourceClass, AllocError> {
allocator.ensure_open()?;
use crate::ImageCreateFlagBits as Flag;
let allowed = Flag::MUTABLE_FORMAT_BIT
| Flag::CUBE_COMPATIBLE_BIT
| Flag::_2D_ARRAY_COMPATIBLE_BIT
| Flag::BLOCK_TEXEL_VIEW_COMPATIBLE_BIT
| Flag::EXTENDED_USAGE_BIT
| Flag::ALIAS_BIT
| Flag::PROTECTED_BIT;
if info.flags.as_raw() & Flag::PROTECTED_BIT.as_raw() != 0 && !allocator.protected_memory {
return Err(AllocError::UnsupportedCapability(
"protected image requires enabled feature metadata",
));
}
if info.flags.as_raw() & !allowed.as_raw() != 0
|| info.extent.width == 0
|| info.extent.height == 0
|| info.extent.depth == 0
|| info.mip_levels == 0
|| info.array_layers == 0
|| info.usage.as_raw() == 0
{
return Err(AllocError::InvalidRequest(
"ordinary nonempty non-disjoint images only",
));
}
let class = match info.tiling {
vk::ImageTiling::LINEAR => ResourceClass::LinearImage,
vk::ImageTiling::OPTIMAL => ResourceClass::OptimalImage,
_ => return Err(AllocError::UnsupportedCapability("image tiling")),
};
unsafe {
super::super::resource::validate_chain(allocator.device.as_device(), info.p_next, true)
}?;
Ok(class)
}
pub(super) fn resource_properties(
allocator: &Allocator<'_>,
protected: bool,
required: vk::MemoryPropertyFlags,
) -> Result<vk::MemoryPropertyFlags, AllocError> {
let flag = vk::MemoryPropertyFlagBits::PROTECTED_BIT;
if protected {
if !allocator.protected_memory {
return Err(AllocError::UnsupportedCapability(
"protected memory feature is not enabled",
));
}
Ok(required | flag)
} else if required.as_raw() & flag.as_raw() != 0 {
Err(AllocError::InvalidRequest(
"non-protected resource cannot use protected memory",
))
} else {
Ok(required)
}
}
pub(super) fn validate_options(
allocator: &Allocator<'_>,
options: &AllocationCreateInfo,
required: vk::MemoryPropertyFlags,
) -> Result<(), AllocError> {
allocator.ensure_allocation()?;
if matches!(allocator.partition, parallel::Domain::Pool(_)) && options.pool.is_none() {
return Err(AllocError::InvalidRequest(
"pool scope requires its selected pool",
));
}
let forced = options.dedicated_memory || options.memory_allocate_next.is_some();
if forced && (options.never_allocate || options.upper_address) {
return Err(AllocError::InvalidRequest(
"separate storage conflicts with reuse/upper placement",
));
}
if let Some(pool) = options.pool {
let state = pool::validate(allocator, pool)?;
if forced && state.info.block_size != 0 {
return Err(AllocError::UnsupportedCapability(
"fixed pool cannot dedicate",
));
}
if options.memory_allocate_next.is_some() && !state.allocation_chain.next.is_null() {
return Err(AllocError::InvalidRequest(
"allocation and pool chains cannot be combined",
));
}
}
if options.within_budget {
budget::validate_profile(allocator)?;
}
let priority = if let Some(pool) = options.pool {
pool::validate_options(allocator, pool, options)?
.info
.priority
} else {
options.priority
};
if priority != MemoryPriority::default() && !allocator.memory_priority {
return Err(AllocError::UnsupportedCapability(
"memory priority requires enabled EXT feature metadata",
));
}
if !allocator.protected_memory
&& required.as_raw() & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0
{
return Err(AllocError::UnsupportedCapability("protected allocation"));
}
if options.pool.is_none() {
if options.upper_address {
return Err(AllocError::UnsupportedCapability(
"upper address requires a single-block linear pool",
));
}
validate_policy(options.policy)?;
}
if options.min_allocation_alignment != 0 && !options.min_allocation_alignment.is_power_of_two() {
return Err(AllocError::InvalidRequest(
"minimum allocation alignment must be zero or a power of two",
));
}
Ok(())
}
fn explicit(options: &AllocationCreateInfo) -> Result<(), AllocError> {
if options.usage != super::super::MemoryUsage::Explicit {
return Err(AllocError::UnsupportedCapability(
"raw requirements have no creation usage; use explicit memory flags",
));
}
Ok(())
}
fn select(
allocator: &Allocator<'_>,
bits: u32,
options: &AllocationCreateInfo,
device_access: bool,
protected: Option<bool>,
) -> Result<u32, AllocError> {
allocator.ensure_open()?;
let (mut required, preferred, avoided) =
super::super::policy::preferences(options, device_access, allocator.integrated)?;
if let Some(protected) = protected {
required = resource_properties(allocator, protected, required)?;
}
validate_options(allocator, options, required)?;
let mut bits = bits & options.memory_type_bits;
if let Some(pool) = options.pool {
bits &= 1 << pool::validate(allocator, pool)?.info.memory_type_index;
}
allocator.selector.find_memory_type_with_preferences(
&AllocRequest {
size: 1,
alignment: 1,
memory_type_bits: bits,
required_flags: required,
preferred_flags: preferred,
},
avoided,
)
}
pub fn find_memory_type(
allocator: &Allocator<'_>,
memory_type_bits: u32,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
allocator.ensure_open()?;
explicit(options)?;
select(allocator, memory_type_bits, options, false, None)
}
enum QueryRoute {
Core,
Khr,
}
fn direct_route(allocator: &Allocator<'_>) -> Result<Option<QueryRoute>, AllocError> {
let capabilities =
allocator
.device
.as_device()
.capabilities()
.ok_or(AllocError::UnsupportedCapability(
"resource queries require device creation metadata",
))?;
Ok(if capabilities.api_version() >= vk::API_VERSION_1_3 {
Some(QueryRoute::Core)
} else if super::super::resource::extension(allocator.device.as_device(), c"VK_KHR_maintenance4")
{
Some(QueryRoute::Khr)
} else {
None
})
}
fn valid_requirements(memory: vk::MemoryRequirements) -> Result<u32, AllocError> {
if memory.size == 0 || !memory.alignment.is_power_of_two() {
return Err(AllocError::InvalidRequest(
"invalid native resource requirements",
));
}
Ok(memory.memory_type_bits)
}
pub unsafe fn find_memory_type_for_buffer_info(
allocator: &Allocator<'_>,
info: &vk::BufferCreateInfo,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
unsafe { validate_buffer(allocator, info) }?;
let (mut required, _, _) = super::super::policy::preferences(
options,
info.usage.as_raw() & !3 != 0,
allocator.integrated,
)?;
let protected = info.flags.as_raw() & vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
required = resource_properties(allocator, protected, required)?;
validate_options(allocator, options, required)?;
let mut output = vk::MemoryRequirements2::new();
if let Some(route) = direct_route(allocator)? {
let query = vk::DeviceBufferMemoryRequirements {
p_create_info: info,
..Default::default()
};
unsafe {
match route {
QueryRoute::Core => allocator
.device
.as_device()
.get_device_buffer_memory_requirements(&query, &mut output)?,
QueryRoute::Khr => allocator
.device
.as_device()
.get_device_buffer_memory_requirements_khr(&query, &mut output)?,
}
}
} else {
let fp = allocator.device.fp();
require(fp.create_buffer.is_some(), "vkCreateBuffer")?;
require(fp.destroy_buffer.is_some(), "vkDestroyBuffer")?;
require(
fp.get_buffer_memory_requirements.is_some(),
"vkGetBufferMemoryRequirements",
)?;
let buffer = unsafe { allocator.device.create_buffer(info, allocator.callbacks) }?;
if buffer.is_null() || binding::associated(allocator, Resource::Buffer(buffer)) {
return Err(CallError::InvalidOutput("temporary buffer is null or already live").into());
}
let queried = unsafe { allocator.device.get_buffer_memory_requirements(buffer) };
unsafe { allocator.device.destroy_buffer(buffer, allocator.callbacks) }
.expect("preflighted temporary cleanup");
output.memory_requirements = queried?;
}
select(
allocator,
valid_requirements(output.memory_requirements)?,
options,
info.usage.as_raw() & !3 != 0,
Some(protected),
)
}
pub unsafe fn find_memory_type_for_image_info(
allocator: &Allocator<'_>,
info: &vk::ImageCreateInfo,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
unsafe { validate_image(allocator, info) }?;
let (mut required, _, _) = super::super::policy::preferences(
options,
info.usage.as_raw() & !3 != 0,
allocator.integrated,
)?;
let protected = info.flags.as_raw() & vk::ImageCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
required = resource_properties(allocator, protected, required)?;
validate_options(allocator, options, required)?;
let mut output = vk::MemoryRequirements2::new();
if let Some(route) = direct_route(allocator)? {
let query = vk::DeviceImageMemoryRequirements {
p_create_info: info,
..Default::default()
};
unsafe {
match route {
QueryRoute::Core => allocator
.device
.as_device()
.get_device_image_memory_requirements(&query, &mut output)?,
QueryRoute::Khr => allocator
.device
.as_device()
.get_device_image_memory_requirements_khr(&query, &mut output)?,
}
}
} else {
let fp = allocator.device.fp();
require(fp.create_image.is_some(), "vkCreateImage")?;
require(fp.destroy_image.is_some(), "vkDestroyImage")?;
require(
fp.get_image_memory_requirements.is_some(),
"vkGetImageMemoryRequirements",
)?;
let image = unsafe { allocator.device.create_image(info, allocator.callbacks) }?;
if image.is_null() || binding::associated(allocator, Resource::Image(image)) {
return Err(CallError::InvalidOutput("temporary image is null or already live").into());
}
let queried = unsafe {
allocator
.device
.as_device()
.get_image_memory_requirements(image, &mut output.memory_requirements)
};
unsafe { allocator.device.destroy_image(image, allocator.callbacks) }
.expect("preflighted temporary cleanup");
queried?;
}
select(
allocator,
valid_requirements(output.memory_requirements)?,
options,
info.usage.as_raw() & !3 != 0,
Some(protected),
)
}
pub unsafe fn allocate_with_options(
allocator: &mut Allocator<'_>,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo,
) -> Result<Allocation, AllocError> {
allocator.ensure_open()?;
explicit(options)?;
valid_requirements(*requirements)?;
let (required, preferred, avoided) =
super::super::policy::preferences(options, false, allocator.integrated)?;
validate_options(allocator, options, required)?;
if options.persistent_map {
require(allocator.device.fp().map_memory.is_some(), "vkMapMemory")?;
require(
allocator.device.fp().unmap_memory.is_some(),
"vkUnmapMemory",
)?;
}
let request = AllocRequest {
size: requirements.size,
alignment: requirements.alignment.max(options.min_allocation_alignment),
memory_type_bits: requirements.memory_type_bits & options.memory_type_bits,
required_flags: required,
preferred_flags: preferred,
};
allocator.reserve()?;
let serial = unsafe {
allocate_managed(
allocator,
&request,
StorageOptions {
chain: &super::super::chain::ChainData::EMPTY,
dedicated: None,
device_address: allocator.buffer_device_address,
priority: options.priority,
within_budget: options.within_budget,
},
Dedication::None,
options,
ResourceClass::Unknown,
avoided,
)
}?;
allocator
.live
.get_mut(&serial)
.expect("new raw allocation")
.can_alias = options.can_alias;
let mut allocation = allocator.published(serial);
if let Err(error) = unsafe { binding::persistent(allocator, &allocation, options) } {
unsafe { rollback_allocation(allocator, &mut allocation) }
.expect("preflighted allocation rollback");
return Err(error);
}
Ok(allocation)
}
impl Allocator<'_> {
pub fn find_memory_type(
&self,
bits: u32,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
find_memory_type(self, bits, options)
}
pub unsafe fn find_memory_type_for_buffer_info(
&self,
info: &vk::BufferCreateInfo,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
unsafe { find_memory_type_for_buffer_info(self, info, options) }
}
pub unsafe fn find_memory_type_for_image_info(
&self,
info: &vk::ImageCreateInfo,
options: &AllocationCreateInfo,
) -> Result<u32, AllocError> {
unsafe { find_memory_type_for_image_info(self, info, options) }
}
pub unsafe fn allocate_with_options(
&mut self,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo,
) -> Result<Allocation, AllocError> {
unsafe { allocate_with_options(self, requirements, options) }
}
}
fn dedicated_options<'a>(
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<AllocationCreateInfo<'a>, AllocError> {
if chain.is_some() && options.memory_allocate_next.is_some() {
return Err(AllocError::InvalidRequest(
"two per-allocation chains supplied",
));
}
Ok(AllocationCreateInfo {
dedicated_memory: true,
memory_allocate_next: chain.or(options.memory_allocate_next),
..*options
})
}
pub unsafe fn allocate_dedicated<'a>(
allocator: &mut Allocator<'_>,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<Allocation, AllocError> {
let options = dedicated_options(options, chain)?;
unsafe { allocate_with_options(allocator, requirements, &options) }
}
pub unsafe fn create_dedicated_buffer<'a>(
allocator: &mut Allocator<'_>,
info: &vk::BufferCreateInfo,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<(vk::Buffer, Allocation), AllocError> {
let options = dedicated_options(options, chain)?;
unsafe { create_buffer_with_options(allocator, info, &options) }
}
pub unsafe fn create_dedicated_image<'a>(
allocator: &mut Allocator<'_>,
info: &vk::ImageCreateInfo,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<(vk::Image, Allocation), AllocError> {
let options = dedicated_options(options, chain)?;
unsafe { create_image_with_options(allocator, info, &options) }
}
impl Allocator<'_> {
pub unsafe fn allocate_dedicated<'a>(
&mut self,
requirements: &vk::MemoryRequirements,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<Allocation, AllocError> {
unsafe { allocate_dedicated(self, requirements, options, chain) }
}
pub unsafe fn create_dedicated_buffer<'a>(
&mut self,
info: &vk::BufferCreateInfo,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<(vk::Buffer, Allocation), AllocError> {
unsafe { create_dedicated_buffer(self, info, options, chain) }
}
pub unsafe fn create_dedicated_image<'a>(
&mut self,
info: &vk::ImageCreateInfo,
options: &AllocationCreateInfo<'a>,
chain: Option<super::super::AllocationChain<'a>>,
) -> Result<(vk::Image, Allocation), AllocError> {
unsafe { create_dedicated_image(self, info, options, chain) }
}
}