Skip to main content

vk/mem/
request.rs

1//! Resource-aware type queries and unclassified native requests, ported from
2//! pinned VMA 3.4.0 `FindMemoryTypeIndexForBufferInfo`,
3//! `FindMemoryTypeIndexForImageInfo` and `vmaAllocateMemory` (license/vma/MIT.txt).
4//! Adaptation: cached exact PFNs, checked identities, conservative native pages
5//! and the same fallible placement, mapping and accounting as managed resources.
6use super::*;
7
8pub(super) unsafe fn validate_buffer(
9	allocator: &Allocator<'_>,
10	info: &vk::BufferCreateInfo,
11) -> Result<bool, AllocError> {
12	allocator.ensure_open()?;
13	// SAFETY: caller supplies a readable native create chain for this call.
14	unsafe {
15		super::super::resource::validate_chain(allocator.device.as_device(), info.p_next, false)
16	}?;
17	let protected = info.flags.as_raw() & vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
18	if protected && !allocator.protected_memory {
19		return Err(AllocError::UnsupportedCapability(
20			"protected buffer requires enabled feature metadata",
21		));
22	}
23	if info.flags.as_raw() & !vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0
24		|| info.size == 0
25		|| info.usage.as_raw() == 0
26		|| info.usage.as_raw() & vk::BufferUsageFlagBits::TILE_MEMORY_BIT_QCOM.as_raw() != 0
27	{
28		return Err(AllocError::InvalidRequest("ordinary nonempty buffers only"));
29	}
30	let device_address =
31		info.usage.as_raw() & vk::BufferUsageFlagBits::SHADER_DEVICE_ADDRESS_BIT.as_raw() != 0;
32	if device_address && !allocator.buffer_device_address {
33		return Err(AllocError::UnsupportedCapability(
34			"buffer device address requires enabled core/KHR feature metadata",
35		));
36	}
37	Ok(device_address)
38}
39
40pub(super) unsafe fn validate_image(
41	allocator: &Allocator<'_>,
42	info: &vk::ImageCreateInfo,
43) -> Result<ResourceClass, AllocError> {
44	allocator.ensure_open()?;
45	use crate::ImageCreateFlagBits as Flag;
46	let allowed = Flag::MUTABLE_FORMAT_BIT
47		| Flag::CUBE_COMPATIBLE_BIT
48		| Flag::_2D_ARRAY_COMPATIBLE_BIT
49		| Flag::BLOCK_TEXEL_VIEW_COMPATIBLE_BIT
50		| Flag::EXTENDED_USAGE_BIT
51		| Flag::ALIAS_BIT
52		| Flag::PROTECTED_BIT;
53	if info.flags.as_raw() & Flag::PROTECTED_BIT.as_raw() != 0 && !allocator.protected_memory {
54		return Err(AllocError::UnsupportedCapability(
55			"protected image requires enabled feature metadata",
56		));
57	}
58	if info.flags.as_raw() & !allowed.as_raw() != 0
59		|| info.extent.width == 0
60		|| info.extent.height == 0
61		|| info.extent.depth == 0
62		|| info.mip_levels == 0
63		|| info.array_layers == 0
64		|| info.usage.as_raw() == 0
65	{
66		return Err(AllocError::InvalidRequest(
67			"ordinary nonempty non-disjoint images only",
68		));
69	}
70	let class = match info.tiling {
71		vk::ImageTiling::LINEAR => ResourceClass::LinearImage,
72		vk::ImageTiling::OPTIMAL => ResourceClass::OptimalImage,
73		_ => return Err(AllocError::UnsupportedCapability("image tiling")),
74	};
75	// SAFETY: caller supplies readable native create records and pointed-to storage.
76	unsafe {
77		super::super::resource::validate_chain(allocator.device.as_device(), info.p_next, true)
78	}?;
79	Ok(class)
80}
81
82pub(super) fn resource_properties(
83	allocator: &Allocator<'_>,
84	protected: bool,
85	required: vk::MemoryPropertyFlags,
86) -> Result<vk::MemoryPropertyFlags, AllocError> {
87	let flag = vk::MemoryPropertyFlagBits::PROTECTED_BIT;
88	if protected {
89		if !allocator.protected_memory {
90			return Err(AllocError::UnsupportedCapability(
91				"protected memory feature is not enabled",
92			));
93		}
94		Ok(required | flag)
95	} else if required.as_raw() & flag.as_raw() != 0 {
96		Err(AllocError::InvalidRequest(
97			"non-protected resource cannot use protected memory",
98		))
99	} else {
100		Ok(required)
101	}
102}
103
104pub(super) fn validate_options(
105	allocator: &Allocator<'_>,
106	options: &AllocationCreateInfo,
107	required: vk::MemoryPropertyFlags,
108) -> Result<(), AllocError> {
109	allocator.ensure_allocation()?;
110	if matches!(allocator.partition, parallel::Domain::Pool(_)) && options.pool.is_none() {
111		return Err(AllocError::InvalidRequest(
112			"pool scope requires its selected pool",
113		));
114	}
115	let forced = options.dedicated_memory || options.memory_allocate_next.is_some();
116	if forced && (options.never_allocate || options.upper_address) {
117		return Err(AllocError::InvalidRequest(
118			"separate storage conflicts with reuse/upper placement",
119		));
120	}
121	if let Some(pool) = options.pool {
122		let state = pool::validate(allocator, pool)?;
123		if forced && state.info.block_size != 0 {
124			return Err(AllocError::UnsupportedCapability(
125				"fixed pool cannot dedicate",
126			));
127		}
128		if options.memory_allocate_next.is_some() && !state.allocation_chain.next.is_null() {
129			return Err(AllocError::InvalidRequest(
130				"allocation and pool chains cannot be combined",
131			));
132		}
133	}
134	if options.within_budget {
135		budget::validate_profile(allocator)?;
136	}
137	let priority = if let Some(pool) = options.pool {
138		pool::validate_options(allocator, pool, options)?
139			.info
140			.priority
141	} else {
142		options.priority
143	};
144	if priority != MemoryPriority::default() && !allocator.memory_priority {
145		return Err(AllocError::UnsupportedCapability(
146			"memory priority requires enabled EXT feature metadata",
147		));
148	}
149	if !allocator.protected_memory
150		&& required.as_raw() & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0
151	{
152		return Err(AllocError::UnsupportedCapability("protected allocation"));
153	}
154	// A selected pool's options were checked while deriving its priority above.
155	if options.pool.is_none() {
156		if options.upper_address {
157			return Err(AllocError::UnsupportedCapability(
158				"upper address requires a single-block linear pool",
159			));
160		}
161		validate_policy(options.policy)?;
162	}
163	if options.min_allocation_alignment != 0 && !options.min_allocation_alignment.is_power_of_two() {
164		return Err(AllocError::InvalidRequest(
165			"minimum allocation alignment must be zero or a power of two",
166		));
167	}
168	Ok(())
169}
170
171fn explicit(options: &AllocationCreateInfo) -> Result<(), AllocError> {
172	if options.usage != super::super::MemoryUsage::Explicit {
173		return Err(AllocError::UnsupportedCapability(
174			"raw requirements have no creation usage; use explicit memory flags",
175		));
176	}
177	Ok(())
178}
179
180fn select(
181	allocator: &Allocator<'_>,
182	bits: u32,
183	options: &AllocationCreateInfo,
184	device_access: bool,
185	protected: Option<bool>,
186) -> Result<u32, AllocError> {
187	allocator.ensure_open()?;
188	let (mut required, preferred, avoided) =
189		super::super::policy::preferences(options, device_access, allocator.integrated)?;
190	if let Some(protected) = protected {
191		required = resource_properties(allocator, protected, required)?;
192	}
193	validate_options(allocator, options, required)?;
194	let mut bits = bits & options.memory_type_bits;
195	if let Some(pool) = options.pool {
196		bits &= 1 << pool::validate(allocator, pool)?.info.memory_type_index;
197	}
198	// Selection is independent of allocation pressure and requested storage size.
199	// Preserve the selector's required/preferred/avoided cost and native exclusions.
200	allocator.selector.find_memory_type_with_preferences(
201		&AllocRequest {
202			size: 1,
203			alignment: 1,
204			memory_type_bits: bits,
205			required_flags: required,
206			preferred_flags: preferred,
207		},
208		avoided,
209	)
210}
211
212/// Select from explicit native memory-type bits without allocating or querying a budget.
213///
214/// Pool identity/type, masks and mandatory properties remain checked. This does
215/// not reserve storage or promise allocation success. Automatic usage requires
216/// a creation-info query instead. Zero mask means no compatible type.
217/// # Errors
218/// Returns closed/pool identity, unsupported policy or incompatible-type errors.
219pub fn find_memory_type(
220	allocator: &Allocator<'_>,
221	memory_type_bits: u32,
222	options: &AllocationCreateInfo,
223) -> Result<u32, AllocError> {
224	allocator.ensure_open()?;
225	explicit(options)?;
226	select(allocator, memory_type_bits, options, false, None)
227}
228
229enum QueryRoute {
230	Core,
231	Khr,
232}
233
234fn direct_route(allocator: &Allocator<'_>) -> Result<Option<QueryRoute>, AllocError> {
235	let capabilities =
236		allocator
237			.device
238			.as_device()
239			.capabilities()
240			.ok_or(AllocError::UnsupportedCapability(
241				"resource queries require device creation metadata",
242			))?;
243	Ok(if capabilities.api_version() >= vk::API_VERSION_1_3 {
244		Some(QueryRoute::Core)
245	} else if super::super::resource::extension(allocator.device.as_device(), c"VK_KHR_maintenance4")
246	{
247		Some(QueryRoute::Khr)
248	} else {
249		None
250	})
251}
252
253fn valid_requirements(memory: vk::MemoryRequirements) -> Result<u32, AllocError> {
254	if memory.size == 0 || !memory.alignment.is_power_of_two() {
255		return Err(AllocError::InvalidRequest(
256			"invalid native resource requirements",
257		));
258	}
259	Ok(memory.memory_type_bits)
260}
261
262/// Select memory for an ordinary buffer using its actual creation usage and requirements.
263///
264/// Core 1.3 or enabled KHR maintenance4 queries without creating a resource.
265/// Older versions create/query/destroy one temporary unbound buffer. No memory
266/// is allocated, mapped or bound; allocator accounting and live tokens stay unchanged.
267/// This is type selection, not an allocation/budget guarantee.
268/// # Errors
269/// Reports invalid/unsupported creation policy, missing exact PFNs, native create
270/// failure or incompatible requirements/properties. Temporary resources are destroyed
271/// after successful creation even if requirement validation or selection fails.
272/// # Safety
273/// Creation records/chains must satisfy enabled native valid usage and remain
274/// readable for this call; device metadata, callbacks and host synchronization must be valid.
275pub unsafe fn find_memory_type_for_buffer_info(
276	allocator: &Allocator<'_>,
277	info: &vk::BufferCreateInfo,
278	options: &AllocationCreateInfo,
279) -> Result<u32, AllocError> {
280	// SAFETY: identical create-record lifetime/valid-usage contract.
281	unsafe { validate_buffer(allocator, info) }?;
282	// Validate policy and pool identity before any temporary native creation.
283	let (mut required, _, _) = super::super::policy::preferences(
284		options,
285		info.usage.as_raw() & !3 != 0,
286		allocator.integrated,
287	)?;
288	let protected = info.flags.as_raw() & vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
289	required = resource_properties(allocator, protected, required)?;
290	validate_options(allocator, options, required)?;
291	let mut output = vk::MemoryRequirements2::new();
292	if let Some(route) = direct_route(allocator)? {
293		let query = vk::DeviceBufferMemoryRequirements {
294			p_create_info: info,
295			..Default::default()
296		};
297		// SAFETY: admitted exact core/KHR command and live initialized records.
298		unsafe {
299			match route {
300				QueryRoute::Core => allocator
301					.device
302					.as_device()
303					.get_device_buffer_memory_requirements(&query, &mut output)?,
304				QueryRoute::Khr => allocator
305					.device
306					.as_device()
307					.get_device_buffer_memory_requirements_khr(&query, &mut output)?,
308			}
309		}
310	} else {
311		let fp = allocator.device.fp();
312		require(fp.create_buffer.is_some(), "vkCreateBuffer")?;
313		require(fp.destroy_buffer.is_some(), "vkDestroyBuffer")?;
314		require(
315			fp.get_buffer_memory_requirements.is_some(),
316			"vkGetBufferMemoryRequirements",
317		)?;
318		// SAFETY: caller establishes native creation validity; inverse PFN preflighted.
319		let buffer = unsafe { allocator.device.create_buffer(info, allocator.callbacks) }?;
320		if buffer.is_null() || binding::associated(allocator, Resource::Buffer(buffer)) {
321			return Err(CallError::InvalidOutput("temporary buffer is null or already live").into());
322		}
323		// SAFETY: new unbound temporary; no GPU work or allocation exists.
324		let queried = unsafe { allocator.device.get_buffer_memory_requirements(buffer) };
325		// SAFETY: new temporary uses the same retained callbacks; immutable PFN preflighted.
326		unsafe { allocator.device.destroy_buffer(buffer, allocator.callbacks) }
327			.expect("preflighted temporary cleanup");
328		output.memory_requirements = queried?;
329	}
330	select(
331		allocator,
332		valid_requirements(output.memory_requirements)?,
333		options,
334		info.usage.as_raw() & !3 != 0,
335		Some(protected),
336	)
337}
338
339/// Select memory for an ordinary non-disjoint image from creation usage and native requirements.
340///
341/// Uses exact core/KHR maintenance4 when admitted, otherwise a temporary unbound
342/// image with preflighted cleanup. No allocation/binding/mapping or budget query occurs.
343/// # Errors
344/// Same policy, identity, missing-command, native creation and compatibility errors
345/// as `find_memory_type_for_buffer_info`; all created temporaries are cleaned up.
346/// # Safety
347/// Image/format/feature, chain, callback and host synchronization valid usage must
348/// hold for the complete call. Sparse/disjoint/DRM/external paths are excluded; protected queries require captured enablement.
349pub unsafe fn find_memory_type_for_image_info(
350	allocator: &Allocator<'_>,
351	info: &vk::ImageCreateInfo,
352	options: &AllocationCreateInfo,
353) -> Result<u32, AllocError> {
354	// SAFETY: identical creation-record and enabled-feature contract.
355	unsafe { validate_image(allocator, info) }?;
356	let (mut required, _, _) = super::super::policy::preferences(
357		options,
358		info.usage.as_raw() & !3 != 0,
359		allocator.integrated,
360	)?;
361	let protected = info.flags.as_raw() & vk::ImageCreateFlagBits::PROTECTED_BIT.as_raw() != 0;
362	required = resource_properties(allocator, protected, required)?;
363	validate_options(allocator, options, required)?;
364	let mut output = vk::MemoryRequirements2::new();
365	if let Some(route) = direct_route(allocator)? {
366		let query = vk::DeviceImageMemoryRequirements {
367			p_create_info: info,
368			..Default::default()
369		};
370		// SAFETY: ordinary non-disjoint/non-DRM image; initialized output and admitted command.
371		unsafe {
372			match route {
373				QueryRoute::Core => allocator
374					.device
375					.as_device()
376					.get_device_image_memory_requirements(&query, &mut output)?,
377				QueryRoute::Khr => allocator
378					.device
379					.as_device()
380					.get_device_image_memory_requirements_khr(&query, &mut output)?,
381			}
382		}
383	} else {
384		let fp = allocator.device.fp();
385		require(fp.create_image.is_some(), "vkCreateImage")?;
386		require(fp.destroy_image.is_some(), "vkDestroyImage")?;
387		require(
388			fp.get_image_memory_requirements.is_some(),
389			"vkGetImageMemoryRequirements",
390		)?;
391		// SAFETY: valid ordinary image and preflighted matching inverse.
392		let image = unsafe { allocator.device.create_image(info, allocator.callbacks) }?;
393		if image.is_null() || binding::associated(allocator, Resource::Image(image)) {
394			return Err(CallError::InvalidOutput("temporary image is null or already live").into());
395		}
396		// SAFETY: new unbound temporary with initialized synchronous output.
397		let queried = unsafe {
398			allocator
399				.device
400				.as_device()
401				.get_image_memory_requirements(image, &mut output.memory_requirements)
402		};
403		// SAFETY: same retained callbacks and no native uses; immutable PFN preflighted.
404		unsafe { allocator.device.destroy_image(image, allocator.callbacks) }
405			.expect("preflighted temporary cleanup");
406		queried?;
407	}
408	select(
409		allocator,
410		valid_requirements(output.memory_requirements)?,
411		options,
412		info.usage.as_raw() & !3 != 0,
413		Some(protected),
414	)
415}
416
417/// Allocate unbound ordinary storage from native requirements with explicit policy.
418///
419/// Supports separate, adaptive and default/custom TLSF/linear pools. Unclassified
420/// pooled regions conflict with every native class (including themselves) on a
421/// granularity page. Actual offset satisfies native, minimum, pool and atom alignment.
422/// Persistent mapping, BDA flags, limits and rollback share the managed path.
423/// # Errors
424/// Reports invalid requirements/policy, incompatible type, pool identity, limits,
425/// placement, bookkeeping or native errors. Mapping failure releases only new storage.
426/// # Safety
427/// Requirements/physical metadata must be valid for this device. Imports use a
428/// validated AllocationChain; size/type-overriding payloads additionally require
429/// truthful ImportMemoryInfo. Its resource supplies any required dedication;
430/// without it, required native resource dedication remains excluded. Sparse,
431/// disjoint, capture/replay and tile semantics remain excluded. Protected memory requires captured enablement; aliases
432/// require can_alias and explicit native lifetime management. Complete every access and destroy bound resources before
433/// release. Bind once through checked helpers; raw pointer and GPU lifetimes remain caller-owned.
434pub unsafe fn allocate_with_options(
435	allocator: &mut Allocator<'_>,
436	requirements: &vk::MemoryRequirements,
437	options: &AllocationCreateInfo,
438) -> Result<Allocation, AllocError> {
439	allocator.ensure_open()?;
440	explicit(options)?;
441	valid_requirements(*requirements)?;
442	let (required, preferred, avoided) =
443		super::super::policy::preferences(options, false, allocator.integrated)?;
444	validate_options(allocator, options, required)?;
445	if options.persistent_map {
446		require(allocator.device.fp().map_memory.is_some(), "vkMapMemory")?;
447		require(
448			allocator.device.fp().unmap_memory.is_some(),
449			"vkUnmapMemory",
450		)?;
451	}
452	let request = AllocRequest {
453		size: requirements.size,
454		alignment: requirements.alignment.max(options.min_allocation_alignment),
455		memory_type_bits: requirements.memory_type_bits & options.memory_type_bits,
456		required_flags: required,
457		preferred_flags: preferred,
458	};
459	allocator.reserve()?;
460	// SAFETY: validated ordinary requirements; no resource-specific dedication exists.
461	let serial = unsafe {
462		allocate_managed(
463			allocator,
464			&request,
465			StorageOptions {
466				chain: &super::super::chain::ChainData::EMPTY,
467				dedicated: None,
468				device_address: allocator.buffer_device_address,
469				priority: options.priority,
470				within_budget: options.within_budget,
471			},
472			Dedication::None,
473			options,
474			ResourceClass::Unknown,
475			avoided,
476		)
477	}?;
478	allocator
479		.live
480		.get_mut(&serial)
481		.expect("new raw allocation")
482		.can_alias = options.can_alias;
483	let mut allocation = allocator.published(serial);
484	// SAFETY: new unbound host-visible storage; inverse PFNs preflighted.
485	if let Err(error) = unsafe { binding::persistent(allocator, &allocation, options) } {
486		// SAFETY: only unpublished new storage is released, with no resource or GPU use.
487		unsafe { rollback_allocation(allocator, &mut allocation) }
488			.expect("preflighted allocation rollback");
489		return Err(error);
490	}
491	Ok(allocation)
492}
493
494impl Allocator<'_> {
495	/// Select from explicit requirements bits without allocating native storage.
496	/// # Errors
497	/// Same checked policy/type/identity errors as `mem::find_memory_type`.
498	pub fn find_memory_type(
499		&self,
500		bits: u32,
501		options: &AllocationCreateInfo,
502	) -> Result<u32, AllocError> {
503		find_memory_type(self, bits, options)
504	}
505
506	/// Select using ordinary buffer creation usage and native requirements.
507	/// # Safety
508	/// Same native valid-usage/lifetime contract as `mem::find_memory_type_for_buffer_info`.
509	pub unsafe fn find_memory_type_for_buffer_info(
510		&self,
511		info: &vk::BufferCreateInfo,
512		options: &AllocationCreateInfo,
513	) -> Result<u32, AllocError> {
514		// SAFETY: caller supplies the canonical creation-record guarantees.
515		unsafe { find_memory_type_for_buffer_info(self, info, options) }
516	}
517
518	/// Select using ordinary image creation usage and native requirements.
519	/// # Safety
520	/// Same native valid-usage/lifetime contract as `mem::find_memory_type_for_image_info`.
521	pub unsafe fn find_memory_type_for_image_info(
522		&self,
523		info: &vk::ImageCreateInfo,
524		options: &AllocationCreateInfo,
525	) -> Result<u32, AllocError> {
526		// SAFETY: caller supplies the canonical creation-record guarantees.
527		unsafe { find_memory_type_for_image_info(self, info, options) }
528	}
529
530	/// Allocate raw separate/pooled storage using the canonical managed placement path.
531	/// # Safety
532	/// Same ordinary requirements, completion and release contract as `mem::allocate_with_options`.
533	pub unsafe fn allocate_with_options(
534		&mut self,
535		requirements: &vk::MemoryRequirements,
536		options: &AllocationCreateInfo,
537	) -> Result<Allocation, AllocError> {
538		// SAFETY: identical native requirements and lifetime contract.
539		unsafe { allocate_with_options(self, requirements, options) }
540	}
541}
542
543fn dedicated_options<'a>(
544	options: &AllocationCreateInfo<'a>,
545	chain: Option<super::super::AllocationChain<'a>>,
546) -> Result<AllocationCreateInfo<'a>, AllocError> {
547	if chain.is_some() && options.memory_allocate_next.is_some() {
548		return Err(AllocError::InvalidRequest(
549			"two per-allocation chains supplied",
550		));
551	}
552	Ok(AllocationCreateInfo {
553		dedicated_memory: true,
554		memory_allocate_next: chain.or(options.memory_allocate_next),
555		..*options
556	})
557}
558
559/// Allocate separate native storage with an optional borrowed allocation chain.
560/// Equivalent to `allocate_with_options` with `dedicated_memory` enabled.
561/// # Errors
562/// Preserves ordinary policy/type/native failures and rejects conflicting chains.
563/// # Safety
564/// Requirements/type/size and every chain payload must meet native valid usage.
565/// Successful native imports transfer handles according to their Vulkan contract,
566/// even if later setup fails; imported contents are not debug-filled or poisoned.
567pub unsafe fn allocate_dedicated<'a>(
568	allocator: &mut Allocator<'_>,
569	requirements: &vk::MemoryRequirements,
570	options: &AllocationCreateInfo<'a>,
571	chain: Option<super::super::AllocationChain<'a>>,
572) -> Result<Allocation, AllocError> {
573	let options = dedicated_options(options, chain)?;
574	// SAFETY: same native requirements/chain and imported-handle obligations.
575	unsafe { allocate_with_options(allocator, requirements, &options) }
576}
577
578/// Create, separately allocate and bind a buffer, optionally importing/exporting memory.
579/// # Errors
580/// Same transaction/rollback errors as `create_buffer_with_options` and chain conflicts.
581/// # Safety
582/// All resource and allocation chain payloads, external compatibility, callback
583/// and completion requirements must hold. Successful import handle transfer
584/// survives later setup failure; resource/native storage rollback does not undo it.
585pub unsafe fn create_dedicated_buffer<'a>(
586	allocator: &mut Allocator<'_>,
587	info: &vk::BufferCreateInfo,
588	options: &AllocationCreateInfo<'a>,
589	chain: Option<super::super::AllocationChain<'a>>,
590) -> Result<(vk::Buffer, Allocation), AllocError> {
591	let options = dedicated_options(options, chain)?;
592	// SAFETY: same complete resource/allocation native transaction obligations.
593	unsafe { create_buffer_with_options(allocator, info, &options) }
594}
595
596/// Create, separately allocate and bind an ordinary non-disjoint image with an optional chain.
597/// # Errors
598/// Same transaction/rollback errors as `create_image_with_options` and chain conflicts.
599/// # Safety
600/// Same native/external valid usage and successful-import transfer contract as
601/// `create_dedicated_buffer`, applied to the image and its queried requirements.
602pub unsafe fn create_dedicated_image<'a>(
603	allocator: &mut Allocator<'_>,
604	info: &vk::ImageCreateInfo,
605	options: &AllocationCreateInfo<'a>,
606	chain: Option<super::super::AllocationChain<'a>>,
607) -> Result<(vk::Image, Allocation), AllocError> {
608	let options = dedicated_options(options, chain)?;
609	// SAFETY: same complete resource/allocation native transaction obligations.
610	unsafe { create_image_with_options(allocator, info, &options) }
611}
612
613impl Allocator<'_> {
614	/// Allocate separate native storage with a borrowed chain.
615	/// # Errors
616	/// Same checked/native/chain errors as `mem::allocate_dedicated`.
617	/// # Safety
618	/// Same requirements, chain, import ownership and completion obligations.
619	pub unsafe fn allocate_dedicated<'a>(
620		&mut self,
621		requirements: &vk::MemoryRequirements,
622		options: &AllocationCreateInfo<'a>,
623		chain: Option<super::super::AllocationChain<'a>>,
624	) -> Result<Allocation, AllocError> {
625		// SAFETY: identical canonical operation contract.
626		unsafe { allocate_dedicated(self, requirements, options, chain) }
627	}
628
629	/// Create a separately allocated/bound buffer with an optional native chain.
630	/// # Errors
631	/// Same checked/native/rollback errors as `mem::create_dedicated_buffer`.
632	/// # Safety
633	/// Same complete resource/allocation external-memory and completion obligations.
634	pub unsafe fn create_dedicated_buffer<'a>(
635		&mut self,
636		info: &vk::BufferCreateInfo,
637		options: &AllocationCreateInfo<'a>,
638		chain: Option<super::super::AllocationChain<'a>>,
639	) -> Result<(vk::Buffer, Allocation), AllocError> {
640		// SAFETY: identical canonical operation contract.
641		unsafe { create_dedicated_buffer(self, info, options, chain) }
642	}
643
644	/// Create a separately allocated/bound image with an optional native chain.
645	/// # Errors
646	/// Same checked/native/rollback errors as `mem::create_dedicated_image`.
647	/// # Safety
648	/// Same complete resource/allocation external-memory and completion obligations.
649	pub unsafe fn create_dedicated_image<'a>(
650		&mut self,
651		info: &vk::ImageCreateInfo,
652		options: &AllocationCreateInfo<'a>,
653		chain: Option<super::super::AllocationChain<'a>>,
654	) -> Result<(vk::Image, Allocation), AllocError> {
655		// SAFETY: identical canonical operation contract.
656		unsafe { create_dedicated_image(self, info, options, chain) }
657	}
658}