Skip to main content

vk/mem/synchronized/
access.rs

1//! Direct `&self` operation access, delegating to the canonical allocator.
2//!
3//! Placement retains the owner mutex. Mapping/binding uses dynamic per-backing
4//! leases and releases the placement mutex through the native call. Exclusive
5//! services and borrowed access wait for those calls before observing/mutating
6//! their state. Native byte/GPU synchronization remains the caller's obligation.
7//!
8//! Results borrowing allocator metadata and relocation contexts remain scoped
9//! to `with_allocator`; no mutable allocator guard or borrowed result escapes.
10use super::SynchronizedAllocator;
11use crate::{
12	self as vk,
13	mem::{
14		AllocError, AllocRequest, Allocation, AllocationChain, AllocationCreateInfo, AllocationInfo,
15		Allocator, AllocatorInfo, BudgetSnapshot, CacheRange, DebugMemorySettings, DetailedStatistics,
16		DeviceMemoryCallbacks, MemoryStatistics, Pool, PoolCreateInfo, PoolStats, TrimStats,
17	},
18};
19
20impl<'device> SynchronizedAllocator<'device> {
21	fn locked<R>(
22		&self,
23		operation: impl FnOnce(&mut Allocator<'device>) -> Result<R, AllocError>,
24	) -> Result<R, AllocError> {
25		let mut guard = self.lock_idle()?;
26		operation(&mut guard.allocator)
27	}
28
29	fn placement<R>(
30		&self,
31		operation: impl FnOnce(&mut Allocator<'device>) -> Result<R, AllocError>,
32	) -> Result<R, AllocError> {
33		let mut guard = self.lock_owner()?;
34		operation(&mut guard.allocator)
35	}
36
37	/// Number of live allocation registrations under this allocator's lock.
38	/// # Errors
39	/// Returns SynchronizationPoisoned after a closure panic.
40	pub fn live_count(&self) -> Result<usize, AllocError> {
41		self.locked(|allocator| Ok(allocator.live_count()))
42	}
43
44	/// Observe explicit close state under this allocator's lock.
45	/// # Errors
46	/// Returns SynchronizationPoisoned after a closure panic.
47	pub fn is_closed(&self) -> Result<bool, AllocError> {
48		self.locked(|allocator| Ok(allocator.is_closed()))
49	}
50
51	/// Optional caller-configured size limit under this allocator's lock.
52	/// # Errors
53	/// Returns SynchronizationPoisoned after a closure panic.
54	pub fn max_allocation_size(&self) -> Result<Option<u64>, AllocError> {
55		self.locked(|allocator| Ok(allocator.max_allocation_size()))
56	}
57
58	/// Close the allocator under its lock; later access reports `Closed`.
59	/// Concurrent operations complete or fail before this runs.
60	/// # Errors
61	/// Same live-resource errors as `Allocator::close`, plus SynchronizationPoisoned.
62	pub fn close(&self) -> Result<(), AllocError> {
63		self.locked(Allocator::close)
64	}
65
66	/// Captured native identities under this allocator's lock.
67	/// # Errors
68	/// Same errors as `Allocator::allocator_info`, plus SynchronizationPoisoned.
69	pub fn allocator_info(&self) -> Result<AllocatorInfo, AllocError> {
70		self.locked(|allocator| crate::mem::allocator_info(allocator))
71	}
72
73	/// Queried properties of one native memory type under this allocator's lock.
74	/// # Errors
75	/// Same errors as `Allocator::memory_type_properties`, plus SynchronizationPoisoned.
76	pub fn memory_type_properties(&self, index: u32) -> Result<vk::MemoryPropertyFlags, AllocError> {
77		self.locked(|allocator| allocator.memory_type_properties(index))
78	}
79
80	/// Score memory types for one allocation request under this allocator's lock.
81	/// # Errors
82	/// Same errors as `Allocator::find_memory_type`, plus SynchronizationPoisoned.
83	pub fn find_memory_type(
84		&self,
85		bits: u32,
86		options: &AllocationCreateInfo,
87	) -> Result<u32, AllocError> {
88		self.locked(|allocator| allocator.find_memory_type(bits, options))
89	}
90
91	/// Score memory types for one buffer's requirements under this allocator's lock.
92	/// # Errors
93	/// Same errors as `Allocator::find_memory_type_for_buffer_info`, plus SynchronizationPoisoned.
94	/// # Safety
95	/// Same create-chain and requirement query contract as
96	/// `Allocator::find_memory_type_for_buffer_info`.
97	pub unsafe fn find_memory_type_for_buffer_info(
98		&self,
99		info: &vk::BufferCreateInfo,
100		options: &AllocationCreateInfo,
101	) -> Result<u32, AllocError> {
102		// SAFETY: identical canonical query contract.
103		unsafe { self.locked(|allocator| allocator.find_memory_type_for_buffer_info(info, options)) }
104	}
105
106	/// Score memory types for one image's requirements under this allocator's lock.
107	/// # Errors
108	/// Same errors as `Allocator::find_memory_type_for_image_info`, plus SynchronizationPoisoned.
109	/// # Safety
110	/// Same create-chain and requirement query contract as
111	/// `Allocator::find_memory_type_for_image_info`.
112	pub unsafe fn find_memory_type_for_image_info(
113		&self,
114		info: &vk::ImageCreateInfo,
115		options: &AllocationCreateInfo,
116	) -> Result<u32, AllocError> {
117		// SAFETY: identical canonical query contract.
118		unsafe { self.locked(|allocator| allocator.find_memory_type_for_image_info(info, options)) }
119	}
120
121	/// Run `Allocator::allocate` under this allocator's lock.
122	/// # Errors
123	/// Same checked/native errors as `Allocator::allocate`, plus SynchronizationPoisoned.
124	/// # Safety
125	/// Same request/chain/import and completion contract as `Allocator::allocate`.
126	pub unsafe fn allocate(&self, request: &AllocRequest) -> Result<Allocation, AllocError> {
127		// SAFETY: identical canonical operation contract.
128		unsafe { self.placement(|allocator| allocator.allocate(request)) }
129	}
130
131	/// Run `Allocator::allocate_with_options` under this allocator's lock.
132	/// # Errors
133	/// Same checked/native errors as `Allocator::allocate_with_options`, plus SynchronizationPoisoned.
134	/// # Safety
135	/// Same requirements/options/chain contract as `Allocator::allocate_with_options`.
136	pub unsafe fn allocate_with_options(
137		&self,
138		requirements: &vk::MemoryRequirements,
139		options: &AllocationCreateInfo,
140	) -> Result<Allocation, AllocError> {
141		// SAFETY: identical canonical operation contract.
142		unsafe { self.placement(|allocator| allocator.allocate_with_options(requirements, options)) }
143	}
144
145	/// Run `Allocator::allocate_dedicated` under this allocator's lock.
146	/// # Errors
147	/// Same checked/native errors as `Allocator::allocate_dedicated`, plus SynchronizationPoisoned.
148	/// # Safety
149	/// Same requirements/chain/import contract as `Allocator::allocate_dedicated`.
150	pub unsafe fn allocate_dedicated(
151		&self,
152		requirements: &vk::MemoryRequirements,
153		options: &AllocationCreateInfo,
154		chain: Option<AllocationChain>,
155	) -> Result<Allocation, AllocError> {
156		// SAFETY: identical canonical operation contract.
157		unsafe {
158			self.placement(|allocator| allocator.allocate_dedicated(requirements, options, chain))
159		}
160	}
161
162	/// Run `Allocator::allocate_many` under this allocator's lock.
163	/// # Errors
164	/// Same checked/native errors as `Allocator::allocate_many`, plus SynchronizationPoisoned.
165	/// # Safety
166	/// Same requirements/options contract as `Allocator::allocate_many`.
167	pub unsafe fn allocate_many(
168		&self,
169		requirements: &vk::MemoryRequirements,
170		options: &AllocationCreateInfo,
171		count: usize,
172	) -> Result<Vec<Allocation>, AllocError> {
173		// SAFETY: identical canonical operation contract.
174		unsafe { self.placement(|allocator| allocator.allocate_many(requirements, options, count)) }
175	}
176
177	/// Run `Allocator::free` under this allocator's lock.
178	/// # Errors
179	/// Same checked/native errors as `Allocator::free`, plus SynchronizationPoisoned.
180	/// # Safety
181	/// Same live-token, mapped-byte and completion contract as `Allocator::free`.
182	pub unsafe fn free(&self, allocation: &mut Allocation) -> Result<(), AllocError> {
183		// SAFETY: identical canonical operation contract.
184		unsafe {
185			crate::mem::allocator::native::with_allocation(self, allocation.serial, |allocator| {
186				allocator.free(allocation)
187			})
188		}
189	}
190
191	/// Run `Allocator::free_many` under this allocator's lock.
192	/// # Errors
193	/// Same checked/native errors as `Allocator::free_many`, plus SynchronizationPoisoned.
194	/// # Safety
195	/// Same live-token and completion contract as `Allocator::free_many`.
196	pub unsafe fn free_many(&self, allocations: &mut [Allocation]) -> Result<(), AllocError> {
197		// SAFETY: identical canonical operation contract.
198		unsafe { self.locked(|allocator| allocator.free_many(allocations)) }
199	}
200
201	/// Checked allocation snapshot under this allocator's lock.
202	/// # Errors
203	/// Same errors as `Allocator::allocation_info`, plus SynchronizationPoisoned.
204	pub fn allocation_info(&self, allocation: &Allocation) -> Result<AllocationInfo, AllocError> {
205		self.locked(|allocator| allocator.allocation_info(allocation))
206	}
207
208	/// Run `Allocator::set_allocation_pinned` under this allocator's lock.
209	/// # Errors
210	/// Same errors as `Allocator::set_allocation_pinned`, plus SynchronizationPoisoned.
211	pub fn set_allocation_pinned(
212		&self,
213		allocation: &Allocation,
214		pinned: bool,
215	) -> Result<(), AllocError> {
216		self.locked(|allocator| allocator.set_allocation_pinned(allocation, pinned))
217	}
218
219	/// Run `Allocator::set_allocation_name` under this allocator's lock.
220	/// # Errors
221	/// Same errors as `Allocator::set_allocation_name`, plus SynchronizationPoisoned.
222	pub fn set_allocation_name(
223		&self,
224		allocation: &Allocation,
225		name: Option<&str>,
226	) -> Result<(), AllocError> {
227		self.locked(|allocator| allocator.set_allocation_name(allocation, name))
228	}
229
230	/// Run `Allocator::set_allocation_user_data` under this allocator's lock.
231	/// # Errors
232	/// Same errors as `Allocator::set_allocation_user_data`, plus SynchronizationPoisoned.
233	pub fn set_allocation_user_data(
234		&self,
235		allocation: &Allocation,
236		data: usize,
237	) -> Result<(), AllocError> {
238		self.locked(|allocator| allocator.set_allocation_user_data(allocation, data))
239	}
240
241	/// Run `Allocator::create_buffer` under this allocator's lock.
242	/// # Errors
243	/// Same transaction errors as `Allocator::create_buffer`, plus SynchronizationPoisoned.
244	/// # Safety
245	/// Same resource/allocation contract as `Allocator::create_buffer`.
246	pub unsafe fn create_buffer(
247		&self,
248		info: &vk::BufferCreateInfo,
249		required: vk::MemoryPropertyFlags,
250		preferred: vk::MemoryPropertyFlags,
251	) -> Result<(vk::Buffer, Allocation), AllocError> {
252		// SAFETY: identical canonical operation contract.
253		unsafe { self.placement(|allocator| allocator.create_buffer(info, required, preferred)) }
254	}
255
256	/// Run `Allocator::create_buffer_with_options` under this allocator's lock.
257	/// # Errors
258	/// Same transaction errors as `Allocator::create_buffer_with_options`, plus SynchronizationPoisoned.
259	/// # Safety
260	/// Same resource/allocation contract as `Allocator::create_buffer_with_options`.
261	pub unsafe fn create_buffer_with_options(
262		&self,
263		info: &vk::BufferCreateInfo,
264		options: &AllocationCreateInfo,
265	) -> Result<(vk::Buffer, Allocation), AllocError> {
266		// SAFETY: identical canonical operation contract.
267		unsafe { self.placement(|allocator| allocator.create_buffer_with_options(info, options)) }
268	}
269
270	/// Run `Allocator::create_dedicated_buffer` under this allocator's lock.
271	/// # Errors
272	/// Same transaction errors as `Allocator::create_dedicated_buffer`, plus SynchronizationPoisoned.
273	/// # Safety
274	/// Same resource/chain/import contract as `Allocator::create_dedicated_buffer`.
275	pub unsafe fn create_dedicated_buffer(
276		&self,
277		info: &vk::BufferCreateInfo,
278		options: &AllocationCreateInfo,
279		chain: Option<AllocationChain>,
280	) -> Result<(vk::Buffer, Allocation), AllocError> {
281		// SAFETY: identical canonical operation contract.
282		unsafe { self.placement(|allocator| allocator.create_dedicated_buffer(info, options, chain)) }
283	}
284
285	/// Run `Allocator::destroy_buffer` under this allocator's lock.
286	/// # Errors
287	/// Same checked errors as `Allocator::destroy_buffer`, plus SynchronizationPoisoned.
288	/// # Safety
289	/// Same resource ownership and completion contract as `Allocator::destroy_buffer`.
290	pub unsafe fn destroy_buffer(
291		&self,
292		buffer: vk::Buffer,
293		allocation: &mut Allocation,
294	) -> Result<(), AllocError> {
295		// SAFETY: identical canonical operation contract.
296		unsafe {
297			crate::mem::allocator::native::with_allocation(self, allocation.serial, |allocator| {
298				allocator.destroy_buffer(buffer, allocation)
299			})
300		}
301	}
302
303	/// Run `Allocator::create_image` under this allocator's lock.
304	/// # Errors
305	/// Same transaction errors as `Allocator::create_image`, plus SynchronizationPoisoned.
306	/// # Safety
307	/// Same resource/allocation contract as `Allocator::create_image`.
308	pub unsafe fn create_image(
309		&self,
310		info: &vk::ImageCreateInfo,
311		required: vk::MemoryPropertyFlags,
312		preferred: vk::MemoryPropertyFlags,
313	) -> Result<(vk::Image, Allocation), AllocError> {
314		// SAFETY: identical canonical operation contract.
315		unsafe { self.placement(|allocator| allocator.create_image(info, required, preferred)) }
316	}
317
318	/// Run `Allocator::create_image_with_options` under this allocator's lock.
319	/// # Errors
320	/// Same transaction errors as `Allocator::create_image_with_options`, plus SynchronizationPoisoned.
321	/// # Safety
322	/// Same resource/allocation contract as `Allocator::create_image_with_options`.
323	pub unsafe fn create_image_with_options(
324		&self,
325		info: &vk::ImageCreateInfo,
326		options: &AllocationCreateInfo,
327	) -> Result<(vk::Image, Allocation), AllocError> {
328		// SAFETY: identical canonical operation contract.
329		unsafe { self.placement(|allocator| allocator.create_image_with_options(info, options)) }
330	}
331
332	/// Run `Allocator::create_dedicated_image` under this allocator's lock.
333	/// # Errors
334	/// Same transaction errors as `Allocator::create_dedicated_image`, plus SynchronizationPoisoned.
335	/// # Safety
336	/// Same resource/chain/import contract as `Allocator::create_dedicated_image`.
337	pub unsafe fn create_dedicated_image(
338		&self,
339		info: &vk::ImageCreateInfo,
340		options: &AllocationCreateInfo,
341		chain: Option<AllocationChain>,
342	) -> Result<(vk::Image, Allocation), AllocError> {
343		// SAFETY: identical canonical operation contract.
344		unsafe { self.placement(|allocator| allocator.create_dedicated_image(info, options, chain)) }
345	}
346
347	/// Run `Allocator::destroy_image` under this allocator's lock.
348	/// # Errors
349	/// Same checked errors as `Allocator::destroy_image`, plus SynchronizationPoisoned.
350	/// # Safety
351	/// Same resource ownership and completion contract as `Allocator::destroy_image`.
352	pub unsafe fn destroy_image(
353		&self,
354		image: vk::Image,
355		allocation: &mut Allocation,
356	) -> Result<(), AllocError> {
357		// SAFETY: identical canonical operation contract.
358		unsafe {
359			crate::mem::allocator::native::with_allocation(self, allocation.serial, |allocator| {
360				allocator.destroy_image(image, allocation)
361			})
362		}
363	}
364
365	/// Run `Allocator::allocate_for_buffer` under this allocator's lock.
366	/// # Errors
367	/// Same checked/native errors as `Allocator::allocate_for_buffer`, plus SynchronizationPoisoned.
368	/// # Safety
369	/// Same external-resource and allocation contract as `Allocator::allocate_for_buffer`.
370	pub unsafe fn allocate_for_buffer(
371		&self,
372		buffer: vk::Buffer,
373		options: &AllocationCreateInfo,
374	) -> Result<Allocation, AllocError> {
375		// SAFETY: identical canonical operation contract.
376		unsafe { self.placement(|allocator| allocator.allocate_for_buffer(buffer, options)) }
377	}
378
379	/// Run `Allocator::allocate_for_image` under this allocator's lock.
380	/// # Errors
381	/// Same checked/native errors as `Allocator::allocate_for_image`, plus SynchronizationPoisoned.
382	/// # Safety
383	/// Same external-resource and allocation contract as `Allocator::allocate_for_image`.
384	pub unsafe fn allocate_for_image(
385		&self,
386		image: vk::Image,
387		options: &AllocationCreateInfo,
388	) -> Result<Allocation, AllocError> {
389		// SAFETY: identical canonical operation contract.
390		unsafe { self.placement(|allocator| allocator.allocate_for_image(image, options)) }
391	}
392
393	/// Run `Allocator::create_aliasing_buffer` under this allocator's lock.
394	/// # Errors
395	/// Same checked errors as `Allocator::create_aliasing_buffer`, plus SynchronizationPoisoned.
396	/// # Safety
397	/// Same alias/storage contract as `Allocator::create_aliasing_buffer`.
398	pub unsafe fn create_aliasing_buffer(
399		&self,
400		allocation: &Allocation,
401		local_offset: u64,
402		info: &vk::BufferCreateInfo,
403	) -> Result<vk::Buffer, AllocError> {
404		// SAFETY: identical canonical operation contract.
405		unsafe {
406			self.locked(|allocator| allocator.create_aliasing_buffer(allocation, local_offset, info))
407		}
408	}
409
410	/// Run `Allocator::create_aliasing_image` under this allocator's lock.
411	/// # Errors
412	/// Same checked errors as `Allocator::create_aliasing_image`, plus SynchronizationPoisoned.
413	/// # Safety
414	/// Same alias/storage contract as `Allocator::create_aliasing_image`.
415	pub unsafe fn create_aliasing_image(
416		&self,
417		allocation: &Allocation,
418		local_offset: u64,
419		info: &vk::ImageCreateInfo,
420	) -> Result<vk::Image, AllocError> {
421		// SAFETY: identical canonical operation contract.
422		unsafe {
423			self.locked(|allocator| allocator.create_aliasing_image(allocation, local_offset, info))
424		}
425	}
426
427	/// Run `Allocator::copy_to_allocation` under this allocator's lock.
428	/// # Errors
429	/// Same checked errors as `Allocator::copy_to_allocation`, plus SynchronizationPoisoned.
430	/// # Safety
431	/// Same mapped-range and byte-validity contract as `Allocator::copy_to_allocation`.
432	pub unsafe fn copy_to_allocation(
433		&self,
434		allocation: &Allocation,
435		offset: u64,
436		source: &[u8],
437	) -> Result<(), AllocError> {
438		// SAFETY: identical canonical operation contract.
439		unsafe { self.locked(|allocator| allocator.copy_to_allocation(allocation, offset, source)) }
440	}
441
442	/// Run `Allocator::copy_from_allocation` under this allocator's lock.
443	/// # Errors
444	/// Same checked errors as `Allocator::copy_from_allocation`, plus SynchronizationPoisoned.
445	/// # Safety
446	/// Same mapped-range and byte-validity contract as `Allocator::copy_from_allocation`.
447	pub unsafe fn copy_from_allocation(
448		&self,
449		allocation: &Allocation,
450		offset: u64,
451		destination: &mut [u8],
452	) -> Result<(), AllocError> {
453		// SAFETY: identical canonical operation contract.
454		unsafe {
455			self.locked(|allocator| allocator.copy_from_allocation(allocation, offset, destination))
456		}
457	}
458
459	/// Run `Allocator::bind_buffer_memory` under a live lease and this backing's mutex.
460	/// # Errors
461	/// Same checked errors as `Allocator::bind_buffer_memory`, plus SynchronizationPoisoned.
462	/// # Safety
463	/// Same native binding contract as `Allocator::bind_buffer_memory`.
464	pub unsafe fn bind_buffer_memory(
465		&self,
466		buffer: vk::Buffer,
467		allocation: &Allocation,
468	) -> Result<(), AllocError> {
469		// SAFETY: identical canonical operation contract.
470		unsafe {
471			crate::mem::allocator::native::bind(
472				self,
473				buffer.into(),
474				allocation,
475				0,
476				std::ptr::null(),
477				false,
478			)
479		}
480	}
481
482	/// Run `Allocator::bind_image_memory` under a live lease and this backing's mutex.
483	/// # Errors
484	/// Same checked errors as `Allocator::bind_image_memory`, plus SynchronizationPoisoned.
485	/// # Safety
486	/// Same native binding contract as `Allocator::bind_image_memory`.
487	pub unsafe fn bind_image_memory(
488		&self,
489		image: vk::Image,
490		allocation: &Allocation,
491	) -> Result<(), AllocError> {
492		// SAFETY: identical canonical operation contract.
493		unsafe {
494			crate::mem::allocator::native::bind(
495				self,
496				image.into(),
497				allocation,
498				0,
499				std::ptr::null(),
500				false,
501			)
502		}
503	}
504
505	/// Run `Allocator::bind_buffer_memory2` under a live lease and this backing's mutex.
506	/// # Errors
507	/// Same checked errors as `Allocator::bind_buffer_memory2`, plus SynchronizationPoisoned.
508	/// # Safety
509	/// Same native chain/binding contract as `Allocator::bind_buffer_memory2`.
510	pub unsafe fn bind_buffer_memory2(
511		&self,
512		buffer: vk::Buffer,
513		allocation: &Allocation,
514		local_offset: u64,
515		next: *const std::ffi::c_void,
516	) -> Result<(), AllocError> {
517		// SAFETY: identical canonical operation contract.
518		unsafe {
519			crate::mem::allocator::native::bind(self, buffer.into(), allocation, local_offset, next, true)
520		}
521	}
522
523	/// Run `Allocator::bind_image_memory2` under a live lease and this backing's mutex.
524	/// # Errors
525	/// Same checked errors as `Allocator::bind_image_memory2`, plus SynchronizationPoisoned.
526	/// # Safety
527	/// Same native chain/binding contract as `Allocator::bind_image_memory2`.
528	pub unsafe fn bind_image_memory2(
529		&self,
530		image: vk::Image,
531		allocation: &Allocation,
532		local_offset: u64,
533		next: *const std::ffi::c_void,
534	) -> Result<(), AllocError> {
535		// SAFETY: identical canonical operation contract.
536		unsafe {
537			crate::mem::allocator::native::bind(self, image.into(), allocation, local_offset, next, true)
538		}
539	}
540
541	/// Run `Allocator::map` under a live lease and this backing's mutex.
542	/// # Errors
543	/// Same checked/native errors as `Allocator::map`, plus SynchronizationPoisoned.
544	/// # Safety
545	/// May fail to reserve the stable backing metadata before native effects.
546	/// Same mapping, mapped-byte synchronization and completion contract as
547	/// `Allocator::map`; escaping pointers retain `SynchronizedAllocator` rules.
548	pub unsafe fn map(&self, allocation: &Allocation) -> Result<*mut std::ffi::c_void, AllocError> {
549		// SAFETY: identical canonical operation contract.
550		unsafe { crate::mem::allocator::native::map(self, allocation, true) }
551	}
552
553	/// Run `Allocator::unmap` under a live lease and this backing's mutex.
554	/// # Errors
555	/// Same checked errors as `Allocator::unmap`, plus SynchronizationPoisoned.
556	/// # Safety
557	/// Same mapped-byte and completion contract as `Allocator::unmap`.
558	pub unsafe fn unmap(&self, allocation: &Allocation) -> Result<(), AllocError> {
559		// SAFETY: identical canonical operation contract.
560		unsafe { crate::mem::allocator::native::map(self, allocation, false) }.map(|_| ())
561	}
562
563	/// Current mapping pointer for one allocation under this allocator's lock.
564	/// # Errors
565	/// Same errors as `Allocator::mapped_pointer`, plus SynchronizationPoisoned.
566	pub fn mapped_pointer(
567		&self,
568		allocation: &Allocation,
569	) -> Result<Option<*mut std::ffi::c_void>, AllocError> {
570		self.locked(|allocator| allocator.mapped_pointer(allocation))
571	}
572
573	/// Run `Allocator::flush` under this allocator's lock.
574	/// # Errors
575	/// Same checked/native errors as `Allocator::flush`, plus SynchronizationPoisoned.
576	/// # Safety
577	/// Same cache-range and completion contract as `Allocator::flush`.
578	pub unsafe fn flush(
579		&self,
580		allocation: &Allocation,
581		offset: u64,
582		size: u64,
583	) -> Result<(), AllocError> {
584		// SAFETY: identical canonical operation contract.
585		unsafe { self.locked(|allocator| allocator.flush(allocation, offset, size)) }
586	}
587
588	/// Run `Allocator::invalidate` under this allocator's lock.
589	/// # Errors
590	/// Same checked/native errors as `Allocator::invalidate`, plus SynchronizationPoisoned.
591	/// # Safety
592	/// Same cache-range and completion contract as `Allocator::invalidate`.
593	pub unsafe fn invalidate(
594		&self,
595		allocation: &Allocation,
596		offset: u64,
597		size: u64,
598	) -> Result<(), AllocError> {
599		// SAFETY: identical canonical operation contract.
600		unsafe { self.locked(|allocator| allocator.invalidate(allocation, offset, size)) }
601	}
602
603	/// Run `Allocator::flush_many` under this allocator's lock.
604	/// # Errors
605	/// Same checked/native errors as `Allocator::flush_many`, plus SynchronizationPoisoned.
606	/// # Safety
607	/// Same cache-range contract as `Allocator::flush_many`.
608	pub unsafe fn flush_many(&self, ranges: &[CacheRange<'_>]) -> Result<(), AllocError> {
609		// SAFETY: identical canonical operation contract.
610		unsafe { self.locked(|allocator| allocator.flush_many(ranges)) }
611	}
612
613	/// Run `Allocator::invalidate_many` under this allocator's lock.
614	/// # Errors
615	/// Same checked/native errors as `Allocator::invalidate_many`, plus SynchronizationPoisoned.
616	/// # Safety
617	/// Same cache-range contract as `Allocator::invalidate_many`.
618	pub unsafe fn invalidate_many(&self, ranges: &[CacheRange<'_>]) -> Result<(), AllocError> {
619		// SAFETY: identical canonical operation contract.
620		unsafe { self.locked(|allocator| allocator.invalidate_many(ranges)) }
621	}
622
623	/// Run `Allocator::set_heap_limit` under this allocator's lock.
624	/// # Errors
625	/// Same errors as `Allocator::set_heap_limit`, plus SynchronizationPoisoned.
626	pub fn set_heap_limit(&self, heap: u32, limit: u64) -> Result<(), AllocError> {
627		self.locked(|allocator| allocator.set_heap_limit(heap, limit))
628	}
629
630	/// Cached budget snapshot under this allocator's lock.
631	/// # Errors
632	/// Returns SynchronizationPoisoned after a closure panic.
633	pub fn budget_snapshot(&self) -> Result<BudgetSnapshot, AllocError> {
634		self.locked(|allocator| Ok(allocator.budget_snapshot()))
635	}
636
637	/// Run `Allocator::query_budget` under this allocator's lock.
638	/// # Errors
639	/// Same errors as `Allocator::query_budget`, plus SynchronizationPoisoned.
640	/// # Safety
641	/// Same instance/physical-device and PFN contract as `Allocator::query_budget`.
642	pub unsafe fn query_budget(
643		&self,
644		instance: &vk::Instance,
645		physical: vk::PhysicalDevice,
646	) -> Result<BudgetSnapshot, AllocError> {
647		// SAFETY: identical canonical operation contract.
648		unsafe { self.locked(|allocator| allocator.query_budget(instance, physical)) }
649	}
650
651	/// Run `Allocator::refresh_budget` under this allocator's lock.
652	/// # Errors
653	/// Same errors as `Allocator::refresh_budget`, plus SynchronizationPoisoned.
654	pub fn refresh_budget(&self) -> Result<BudgetSnapshot, AllocError> {
655		self.locked(Allocator::refresh_budget)
656	}
657
658	/// Run `Allocator::refresh_budget_with_context` under this allocator's lock.
659	/// # Errors
660	/// Same errors as `Allocator::refresh_budget_with_context`, plus SynchronizationPoisoned.
661	/// # Safety
662	/// Same context/PFN contract as `Allocator::refresh_budget_with_context`.
663	pub unsafe fn refresh_budget_with_context(
664		&self,
665		instance: &vk::Instance,
666		physical: vk::PhysicalDevice,
667	) -> Result<BudgetSnapshot, AllocError> {
668		// SAFETY: identical canonical operation contract.
669		unsafe { self.locked(|allocator| allocator.refresh_budget_with_context(instance, physical)) }
670	}
671
672	/// Run `Allocator::heap_budgets` under this allocator's lock.
673	/// # Errors
674	/// Same errors as `Allocator::heap_budgets`, plus SynchronizationPoisoned.
675	pub fn heap_budgets(&self) -> Result<BudgetSnapshot, AllocError> {
676		self.locked(Allocator::heap_budgets)
677	}
678
679	/// Run `Allocator::set_current_frame_index` under this allocator's lock.
680	/// # Errors
681	/// Same errors as `Allocator::set_current_frame_index`, plus SynchronizationPoisoned.
682	pub fn set_current_frame_index(&self, index: u32) -> Result<(), AllocError> {
683		self.locked(|allocator| allocator.set_current_frame_index(index))
684	}
685
686	/// Current frame index under this allocator's lock.
687	/// # Errors
688	/// Returns SynchronizationPoisoned after a closure panic.
689	pub fn current_frame_index(&self) -> Result<u32, AllocError> {
690		self.locked(|allocator| Ok(allocator.current_frame_index()))
691	}
692
693	/// Run `Allocator::trim` under this allocator's lock.
694	/// # Errors
695	/// Same errors as `Allocator::trim`, plus SynchronizationPoisoned.
696	pub fn trim(&self) -> Result<TrimStats, AllocError> {
697		self.locked(Allocator::trim)
698	}
699
700	/// Run `Allocator::trim_with_budget` under this allocator's lock.
701	/// # Errors
702	/// Same errors as `Allocator::trim_with_budget`, plus SynchronizationPoisoned.
703	/// # Safety
704	/// Same instance/physical-device and PFN contract as `Allocator::trim_with_budget`.
705	pub unsafe fn trim_with_budget(
706		&self,
707		instance: &vk::Instance,
708		physical: vk::PhysicalDevice,
709	) -> Result<TrimStats, AllocError> {
710		// SAFETY: identical canonical operation contract.
711		unsafe { self.locked(|allocator| allocator.trim_with_budget(instance, physical)) }
712	}
713
714	/// Run `Allocator::create_pool` under this allocator's lock.
715	/// # Errors
716	/// Same checked/native errors as `Allocator::create_pool`, plus SynchronizationPoisoned.
717	/// # Safety
718	/// Same policy/storage contract as `Allocator::create_pool`.
719	pub unsafe fn create_pool(&self, info: &PoolCreateInfo) -> Result<Pool, AllocError> {
720		// SAFETY: identical canonical operation contract.
721		unsafe { self.locked(|allocator| allocator.create_pool(info)) }
722	}
723
724	/// Run `Allocator::create_pool_with_chain` under this allocator's lock.
725	/// # Errors
726	/// Same checked/native errors as `Allocator::create_pool_with_chain`, plus SynchronizationPoisoned.
727	/// # Safety
728	/// Same retained-chain contract as `Allocator::create_pool_with_chain`;
729	/// the chain must also satisfy `SynchronizedAllocator::new`'s any-thread rules.
730	pub unsafe fn create_pool_with_chain(
731		&self,
732		info: &PoolCreateInfo,
733		chain: AllocationChain<'device>,
734	) -> Result<Pool, AllocError> {
735		// SAFETY: identical canonical operation contract.
736		unsafe { self.locked(|allocator| allocator.create_pool_with_chain(info, chain)) }
737	}
738
739	/// Run `Allocator::destroy_pool` under this allocator's lock.
740	/// # Errors
741	/// Same live-resource errors as `Allocator::destroy_pool`, plus SynchronizationPoisoned.
742	pub fn destroy_pool(&self, pool: &mut Pool) -> Result<(), AllocError> {
743		self.locked(|allocator| allocator.destroy_pool(pool))
744	}
745
746	/// One pool's statistics under this allocator's lock.
747	/// # Errors
748	/// Same errors as `Allocator::pool_statistics`, plus SynchronizationPoisoned.
749	pub fn pool_statistics(&self, pool: &Pool) -> Result<PoolStats, AllocError> {
750		self.locked(|allocator| allocator.pool_statistics(pool))
751	}
752
753	/// Run `Allocator::set_pool_name` under this allocator's lock.
754	/// # Errors
755	/// Same errors as `Allocator::set_pool_name`, plus SynchronizationPoisoned.
756	pub fn set_pool_name(&self, pool: &Pool, name: Option<&str>) -> Result<(), AllocError> {
757		self.locked(|allocator| allocator.set_pool_name(pool, name))
758	}
759
760	/// Run `Allocator::set_preferred_block_size` under this allocator's lock.
761	/// # Errors
762	/// Same errors as `Allocator::set_preferred_block_size`, plus SynchronizationPoisoned.
763	pub fn set_preferred_block_size(&self, size: u64) -> Result<(), AllocError> {
764		self.locked(|allocator| allocator.set_preferred_block_size(size))
765	}
766
767	/// Run `Allocator::set_mapping_hysteresis` under this allocator's lock.
768	/// # Errors
769	/// Same errors as `Allocator::set_mapping_hysteresis`, plus SynchronizationPoisoned.
770	pub fn set_mapping_hysteresis(&self, enabled: bool) -> Result<(), AllocError> {
771		self.locked(|allocator| allocator.set_mapping_hysteresis(enabled))
772	}
773
774	/// Aggregate memory statistics under this allocator's lock.
775	/// # Errors
776	/// Same errors as `Allocator::detailed_statistics`, plus SynchronizationPoisoned.
777	pub fn detailed_statistics(&self) -> Result<MemoryStatistics, AllocError> {
778		self.locked(|allocator| allocator.detailed_statistics())
779	}
780
781	/// One pool's detailed statistics under this allocator's lock.
782	/// # Errors
783	/// Same errors as `Allocator::detailed_pool_statistics`, plus SynchronizationPoisoned.
784	pub fn detailed_pool_statistics(&self, pool: &Pool) -> Result<DetailedStatistics, AllocError> {
785		self.locked(|allocator| allocator.detailed_pool_statistics(pool))
786	}
787
788	/// Run `Allocator::write_statistics_json` under this allocator's lock.
789	/// # Errors
790	/// Same errors as `Allocator::write_statistics_json`, plus SynchronizationPoisoned.
791	pub fn write_statistics_json(
792		&self,
793		output: &mut impl std::io::Write,
794		detailed: bool,
795	) -> Result<(), AllocError> {
796		self.locked(|allocator| allocator.write_statistics_json(output, detailed))
797	}
798
799	/// Run `Allocator::set_debug_memory_settings` under this allocator's lock.
800	/// # Errors
801	/// Same errors as `Allocator::set_debug_memory_settings`, plus SynchronizationPoisoned.
802	pub fn set_debug_memory_settings(&self, settings: DebugMemorySettings) -> Result<(), AllocError> {
803		self.locked(|allocator| allocator.set_debug_memory_settings(settings))
804	}
805
806	/// Run `Allocator::check_corruption` under this allocator's lock.
807	/// # Errors
808	/// Same checked errors as `Allocator::check_corruption`, plus SynchronizationPoisoned.
809	/// # Safety
810	/// Same guard-pattern byte-validity contract as `Allocator::check_corruption`.
811	pub unsafe fn check_corruption(&self, bits: u32) -> Result<(), AllocError> {
812		// SAFETY: identical canonical operation contract.
813		unsafe { self.locked(|allocator| allocator.check_corruption(bits)) }
814	}
815
816	/// Run `Allocator::check_pool_corruption` under this allocator's lock.
817	/// # Errors
818	/// Same checked errors as `Allocator::check_pool_corruption`, plus SynchronizationPoisoned.
819	/// # Safety
820	/// Same guard-pattern byte-validity contract as `Allocator::check_pool_corruption`.
821	pub unsafe fn check_pool_corruption(&self, pool: &Pool) -> Result<(), AllocError> {
822		// SAFETY: identical canonical operation contract.
823		unsafe { self.locked(|allocator| allocator.check_pool_corruption(pool)) }
824	}
825
826	/// Run `Allocator::set_external_memory_types` under this allocator's lock.
827	/// # Errors
828	/// Same errors as `Allocator::set_external_memory_types`, plus SynchronizationPoisoned.
829	/// # Safety
830	/// Same capability/creation contract as `Allocator::set_external_memory_types`.
831	pub unsafe fn set_external_memory_types(
832		&self,
833		types: &[vk::ExternalMemoryHandleTypeFlags],
834	) -> Result<(), AllocError> {
835		// SAFETY: identical canonical operation contract.
836		unsafe { self.locked(|allocator| allocator.set_external_memory_types(types)) }
837	}
838
839	/// One allocation's external handle types under this allocator's lock.
840	/// # Errors
841	/// Same errors as `Allocator::external_memory_types`, plus SynchronizationPoisoned.
842	pub fn external_memory_types(
843		&self,
844		allocation: &Allocation,
845	) -> Result<vk::ExternalMemoryHandleTypeFlags, AllocError> {
846		self.locked(|allocator| allocator.external_memory_types(allocation))
847	}
848
849	/// Run `Allocator::get_memory_fd` under this allocator's lock.
850	/// # Errors
851	/// Same checked/native errors as `Allocator::get_memory_fd`, plus SynchronizationPoisoned.
852	/// # Safety
853	/// Same external-memory and descriptor-ownership contract as `Allocator::get_memory_fd`.
854	#[cfg(unix)]
855	pub unsafe fn get_memory_fd(
856		&self,
857		allocation: &Allocation,
858		handle_type: vk::ExternalMemoryHandleTypeFlagBits,
859	) -> Result<std::os::fd::RawFd, AllocError> {
860		// SAFETY: identical canonical operation contract.
861		unsafe { self.locked(|allocator| allocator.get_memory_fd(allocation, handle_type)) }
862	}
863
864	/// Run `Allocator::get_memory_win32_handle` under this allocator's lock.
865	/// # Errors
866	/// Same checked/native/OS errors as `Allocator::get_memory_win32_handle`, plus SynchronizationPoisoned.
867	/// # Safety
868	/// Same sharing, target-process and handle-closing contract as
869	/// `Allocator::get_memory_win32_handle`.
870	#[cfg(all(windows, feature = "platform-win32"))]
871	pub unsafe fn get_memory_win32_handle(
872		&self,
873		allocation: &Allocation,
874		handle_type: vk::ExternalMemoryHandleTypeFlagBits,
875		target_process: *mut std::ffi::c_void,
876	) -> Result<vk::HANDLE, AllocError> {
877		// SAFETY: identical canonical operation contract.
878		unsafe {
879			self.locked(|allocator| {
880				allocator.get_memory_win32_handle(allocation, handle_type, target_process)
881			})
882		}
883	}
884
885	/// Run `Allocator::set_device_memory_callbacks` under this allocator's lock.
886	/// # Errors
887	/// Same errors as `Allocator::set_device_memory_callbacks`, plus SynchronizationPoisoned.
888	/// # Safety
889	/// Same retained-callback contract as `Allocator::set_device_memory_callbacks`;
890	/// callbacks must also satisfy `SynchronizedAllocator::new`'s any-thread rules.
891	pub unsafe fn set_device_memory_callbacks(
892		&self,
893		callbacks: Option<&'device DeviceMemoryCallbacks>,
894	) -> Result<(), AllocError> {
895		// SAFETY: identical canonical operation contract.
896		unsafe { self.locked(|allocator| allocator.set_device_memory_callbacks(callbacks)) }
897	}
898}