1use super::*;
6use crate as vk;
7use crate::mem::{AllocationChain, chain::ChainData};
8
9pub(super) fn validate_types(
10 allocator: &Allocator<'_>,
11 types: vk::ExternalMemoryHandleTypeFlags,
12) -> Result<(), AllocError> {
13 crate::mem::resource::validate_external_types(allocator.device.as_device(), types)
14}
15
16pub(super) fn allocation_exports(
17 allocator: &Allocator<'_>,
18 index: u32,
19 chain: ChainData,
20) -> Result<vk::ExternalMemoryHandleTypeFlags, AllocError> {
21 validate_allocation_chain(allocator, chain)?;
22 let configured = allocator.external_types[index as usize];
23 if configured.as_raw() != 0 && chain.has_export {
24 return Err(AllocError::InvalidRequest(
25 "duplicate configured and chained export records",
26 ));
27 }
28 let types = configured | chain.exports;
29 validate_types(allocator, types)?;
30 Ok(types)
31}
32
33pub unsafe fn set_external_memory_types(
43 allocator: &mut Allocator<'_>,
44 types: &[vk::ExternalMemoryHandleTypeFlags],
45) -> Result<(), AllocError> {
46 allocator.ensure_global()?;
47 allocator.ensure_open()?;
48 if allocator.native_count != 0 || !allocator.live.is_empty() {
49 return Err(AllocError::LiveAllocations);
50 }
51 if !allocator.pools.is_empty() {
52 return Err(AllocError::LivePools);
53 }
54 if types.len() != allocator.selector.properties.memory_type_count as usize {
55 return Err(AllocError::InvalidRequest(
56 "external profile must cover exactly the native memory types",
57 ));
58 }
59 for &types in types {
60 validate_types(allocator, types)?;
61 }
62 allocator
63 .external_types
64 .fill(vk::ExternalMemoryHandleTypeFlags::empty());
65 allocator.external_types[..types.len()].copy_from_slice(types);
66 Ok(())
67}
68
69pub(super) fn exports(
70 allocator: &Allocator<'_>,
71 allocation: &Allocation,
72) -> Result<vk::ExternalMemoryHandleTypeFlags, AllocError> {
73 let live = allocator.validate(allocation)?;
74 if live.placement.is_some() {
75 Ok(
76 allocator
77 .blocks
78 .iter()
79 .find(|block| block.handle == live.handle)
80 .ok_or(AllocError::UnknownAllocation)?
81 .export_types,
82 )
83 } else {
84 Ok(live.export_types)
85 }
86}
87
88pub fn external_memory_types(
90 allocator: &Allocator<'_>,
91 allocation: &Allocation,
92) -> Result<vk::ExternalMemoryHandleTypeFlags, AllocError> {
93 exports(allocator, allocation)
94}
95
96#[cfg(unix)]
106pub unsafe fn get_memory_fd(
107 allocator: &Allocator<'_>,
108 allocation: &Allocation,
109 handle_type: vk::ExternalMemoryHandleTypeFlagBits,
110) -> Result<std::os::fd::RawFd, AllocError> {
111 use vk::ExternalMemoryHandleTypeFlagBits as F;
112 if handle_type != F::OPAQUE_FD_BIT && handle_type != F::DMA_BUF_BIT_EXT {
113 return Err(AllocError::InvalidRequest(
114 "file descriptor handle type required",
115 ));
116 }
117 validate_export(allocator, allocation, handle_type)?;
118 require(
119 allocator.device.fp().get_memory_fd_khr.is_some(),
120 "vkGetMemoryFdKHR",
121 )?;
122 let info = vk::MemoryGetFdInfoKHR {
123 memory: allocation.handle,
124 handle_type,
125 ..Default::default()
126 };
127 let mut fd = -1;
128 unsafe { allocator.device.get_memory_fd_khr(&info, &mut fd) }?;
130 if fd < 0 {
131 return Err(
132 vk::load::CallError::InvalidOutput("vkGetMemoryFdKHR returned an invalid descriptor").into(),
133 );
134 }
135 Ok(fd)
136}
137
138fn validate_export(
139 allocator: &Allocator<'_>,
140 allocation: &Allocation,
141 handle_type: vk::ExternalMemoryHandleTypeFlagBits,
142) -> Result<(), AllocError> {
143 let bits = handle_type.as_raw();
144 if bits == 0 || !bits.is_power_of_two() || exports(allocator, allocation)?.as_raw() & bits == 0 {
145 return Err(AllocError::InvalidRequest(
146 "handle type was not enabled for this native allocation",
147 ));
148 }
149 validate_types(allocator, handle_type)
150}
151
152pub(super) unsafe fn required_resource_types(
153 chain: *const std::ffi::c_void,
154) -> vk::ExternalMemoryHandleTypeFlags {
155 let mut types = vk::ExternalMemoryHandleTypeFlags::empty();
156 let mut cursor = chain.cast::<vk::BaseInStructure>();
157 while !cursor.is_null() {
159 let node = unsafe { &*cursor };
161 match node.s_type {
162 vk::StructureType::EXTERNAL_MEMORY_BUFFER_CREATE_INFO => {
163 types = unsafe { (*cursor.cast::<vk::ExternalMemoryBufferCreateInfo>()).handle_types };
165 }
166 vk::StructureType::EXTERNAL_MEMORY_IMAGE_CREATE_INFO => {
167 types = unsafe { (*cursor.cast::<vk::ExternalMemoryImageCreateInfo>()).handle_types };
169 }
170 _ => {}
171 }
172 cursor = node.p_next;
173 }
174 types
175}
176
177#[inline]
178pub(super) unsafe fn resource_options<'options, 'a>(
179 allocator: &Allocator<'_>,
180 options: &'options AllocationCreateInfo<'a>,
181 chain: *const std::ffi::c_void,
182) -> Result<std::borrow::Cow<'options, AllocationCreateInfo<'a>>, AllocError> {
183 if chain.is_null() {
184 return Ok(std::borrow::Cow::Borrowed(options));
185 }
186 unsafe { external_resource_options(allocator, options, chain) }
188}
189
190#[inline(never)]
191unsafe fn external_resource_options<'options, 'a>(
192 allocator: &Allocator<'_>,
193 options: &'options AllocationCreateInfo<'a>,
194 chain: *const std::ffi::c_void,
195) -> Result<std::borrow::Cow<'options, AllocationCreateInfo<'a>>, AllocError> {
196 let required = unsafe { required_resource_types(chain) };
198 if required.as_raw() == 0 {
199 return Ok(std::borrow::Cow::Borrowed(options));
200 }
201 let tail = options.memory_allocate_next.map_or_else(
202 || {
203 options
204 .pool
205 .and_then(|pool| allocator.pools.get(&pool.serial))
206 .map_or(Default::default(), |pool| pool.allocation_chain)
207 },
208 |chain| chain.data,
209 );
210 if tail.imports {
212 return Ok(std::borrow::Cow::Borrowed(options));
213 }
214 let mut bits = 0;
215 for index in 0..allocator.selector.properties.memory_type_count {
216 if options.memory_type_bits & (1 << index) == 0
217 || options
218 .pool
219 .is_some_and(|pool| allocator.pools[&pool.serial].info.memory_type_index != index)
220 {
221 continue;
222 }
223 if tail.has_export && allocator.external_types[index as usize].as_raw() != 0 {
226 continue;
227 }
228 let types = allocation_exports(allocator, index, tail)?;
229 if types.as_raw() & required.as_raw() == required.as_raw() {
230 bits |= 1 << index;
231 }
232 }
233 if bits & options.memory_type_bits == 0 {
234 return Err(AllocError::NoCompatibleMemoryType);
235 }
236 Ok(std::borrow::Cow::Owned(AllocationCreateInfo {
237 memory_type_bits: options.memory_type_bits & bits,
238 ..*options
239 }))
240}
241
242impl<'device> Allocator<'device> {
243 pub unsafe fn set_external_memory_types(
249 &mut self,
250 types: &[vk::ExternalMemoryHandleTypeFlags],
251 ) -> Result<(), AllocError> {
252 unsafe { set_external_memory_types(self, types) }
254 }
255
256 pub fn external_memory_types(
257 &self,
258 allocation: &Allocation,
259 ) -> Result<vk::ExternalMemoryHandleTypeFlags, AllocError> {
260 external_memory_types(self, allocation)
261 }
262
263 #[cfg(unix)]
269 pub unsafe fn get_memory_fd(
270 &self,
271 allocation: &Allocation,
272 handle_type: vk::ExternalMemoryHandleTypeFlagBits,
273 ) -> Result<std::os::fd::RawFd, AllocError> {
274 unsafe { get_memory_fd(self, allocation, handle_type) }
276 }
277
278 pub unsafe fn create_pool_with_chain(
284 &mut self,
285 info: &PoolCreateInfo,
286 chain: AllocationChain<'device>,
287 ) -> Result<Pool, AllocError> {
288 unsafe { create_pool_with_chain(self, info, chain) }
290 }
291}
292
293#[cfg(all(windows, feature = "platform-win32"))]
294use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
295
296#[cfg(all(windows, feature = "platform-win32"))]
301pub(super) struct CachedHandle {
302 pub memory: vk::DeviceMemory,
303 kind: vk::ExternalMemoryHandleTypeFlagBits,
304 handle: *mut std::ffi::c_void,
305 _root: Option<OwnedHandle>,
306}
307
308#[cfg(all(windows, feature = "platform-win32"))]
309#[link(name = "kernel32")]
310unsafe extern "system" {
311 fn GetCurrentProcess() -> *mut std::ffi::c_void;
312 fn DuplicateHandle(
313 source_process: *mut std::ffi::c_void,
314 source: *mut std::ffi::c_void,
315 target_process: *mut std::ffi::c_void,
316 target: *mut *mut std::ffi::c_void,
317 access: u32,
318 inherit: i32,
319 options: u32,
320 ) -> i32;
321}
322
323#[cfg(all(windows, feature = "platform-win32"))]
337pub unsafe fn get_memory_win32_handle(
338 allocator: &mut Allocator<'_>,
339 allocation: &Allocation,
340 handle_type: vk::ExternalMemoryHandleTypeFlagBits,
341 target_process: *mut std::ffi::c_void,
342) -> Result<vk::HANDLE, AllocError> {
343 use vk::ExternalMemoryHandleTypeFlagBits as F;
344 let nt = match handle_type {
345 F::OPAQUE_WIN32_BIT | F::D3D11_TEXTURE_BIT | F::D3D12_HEAP_BIT | F::D3D12_RESOURCE_BIT => true,
346 F::OPAQUE_WIN32_KMT_BIT | F::D3D11_TEXTURE_KMT_BIT => false,
347 _ => return Err(AllocError::InvalidRequest("Win32 handle type required")),
348 };
349 validate_export(allocator, allocation, handle_type)?;
350 if !nt && !target_process.is_null() {
351 return Err(AllocError::InvalidRequest(
352 "KMT handles cannot be duplicated into a target process",
353 ));
354 }
355 require(
356 allocator.device.fp().get_memory_win32_handle_khr.is_some(),
357 "vkGetMemoryWin32HandleKHR",
358 )?;
359 let cached = allocator
360 .external_handles
361 .iter()
362 .position(|entry| entry.memory == allocation.handle && entry.kind == handle_type);
363 let index = if let Some(index) = cached {
364 index
365 } else {
366 if let Some(shared) = allocator.parallel_state() {
367 shared.reserve_external()?;
368 }
369 allocator
370 .external_handles
371 .try_reserve(1)
372 .map_err(|_| AllocError::HostAllocationFailed)?;
373 let info = vk::MemoryGetWin32HandleInfoKHR {
374 memory: allocation.handle,
375 handle_type,
376 ..Default::default()
377 };
378 let mut handle = std::ptr::null_mut();
379 unsafe {
381 allocator
382 .device
383 .as_device()
384 .get_memory_win32_handle_khr(&info, &mut handle)
385 }?;
386 if handle.is_null() || handle == (-1_isize) as *mut std::ffi::c_void {
387 return Err(
388 vk::load::CallError::InvalidOutput("vkGetMemoryWin32HandleKHR returned an invalid handle")
389 .into(),
390 );
391 }
392 let root = if nt {
393 Some(unsafe { OwnedHandle::from_raw_handle(handle) })
395 } else {
396 None
397 };
398 let index = allocator.external_handles.len();
399 allocator.external_handles.push_reserved(CachedHandle {
400 memory: allocation.handle,
401 kind: handle_type,
402 handle,
403 _root: root,
404 });
405 if let Some(shared) = allocator.parallel_state() {
406 shared.external_added();
407 }
408 index
409 };
410 let entry = &allocator.external_handles[index];
411 if !nt {
412 return Ok(entry.handle);
413 }
414 let root = entry
415 ._root
416 .as_ref()
417 .expect("NT export root owns its handle");
418 let current = unsafe { GetCurrentProcess() };
420 let target = if target_process.is_null() {
421 current
422 } else {
423 target_process
424 };
425 let mut duplicate = std::ptr::null_mut();
426 if unsafe {
429 DuplicateHandle(
430 current,
431 root.as_raw_handle(),
432 target,
433 &mut duplicate,
434 0,
435 0,
436 2,
437 )
438 } == 0
439 {
440 let error = std::io::Error::last_os_error();
441 return Err(AllocError::Output {
442 kind: error.kind(),
443 code: error.raw_os_error(),
444 });
445 }
446 Ok(duplicate)
447}
448
449#[cfg(all(windows, feature = "platform-win32"))]
450impl Allocator<'_> {
451 pub unsafe fn get_memory_win32_handle(
457 &mut self,
458 allocation: &Allocation,
459 handle_type: vk::ExternalMemoryHandleTypeFlagBits,
460 target_process: *mut std::ffi::c_void,
461 ) -> Result<vk::HANDLE, AllocError> {
462 unsafe { get_memory_win32_handle(self, allocation, handle_type, target_process) }
464 }
465}
466
467fn validate_allocation_chain(
468 allocator: &Allocator<'_>,
469 chain: ChainData,
470) -> Result<(), AllocError> {
471 if let Some(name) = chain.import_extension
472 && !crate::mem::resource::extension(allocator.device.as_device(), name)
473 {
474 return Err(AllocError::UnsupportedCapability(
475 "memory import extension is not enabled",
476 ));
477 }
478 if let Some(flags) = chain.flags {
479 if !allocator
480 .device
481 .as_device()
482 .capabilities()
483 .is_some_and(|caps| caps.api_version() >= vk::API_VERSION_1_1)
484 && !crate::mem::resource::extension(allocator.device.as_device(), c"VK_KHR_device_group")
485 {
486 return Err(AllocError::UnsupportedCapability(
487 "allocation flags require core 1.1 or enabled device_group",
488 ));
489 }
490 if flags.as_raw() & vk::MemoryAllocateFlagBits::DEVICE_ADDRESS_BIT.as_raw() != 0
491 && !allocator.buffer_device_address
492 {
493 return Err(AllocError::UnsupportedCapability(
494 "allocation address flag requires enabled feature",
495 ));
496 }
497 }
498 Ok(())
499}