1mod import;
5pub(super) use import::ImportKind;
6
7use super::{AllocError, ImportMemoryInfo};
8use crate as vk;
9use std::{ffi::c_void, marker::PhantomData, ptr};
10
11#[derive(Clone, Copy, Debug)]
18pub struct AllocationChain<'a> {
19 pub(super) data: ChainData,
20 borrow: PhantomData<&'a c_void>,
21}
22
23#[derive(Clone, Copy, Debug)]
24pub(super) struct ChainData {
25 pub next: *const c_void,
26 pub flags: Option<vk::MemoryAllocateFlags>,
27 pub exports: vk::ExternalMemoryHandleTypeFlags,
28 pub has_export: bool,
29 pub imports: bool,
30 pub import_extension: Option<&'static std::ffi::CStr>,
31 pub import_kind: ImportKind,
32 pub import_info: Option<ImportMemoryInfo>,
33}
34
35impl ChainData {
36 pub(super) const EMPTY: Self = Self {
39 next: ptr::null(),
40 flags: None,
41 exports: vk::ExternalMemoryHandleTypeFlags::empty(),
42 has_export: false,
43 imports: false,
44 import_extension: None,
45 import_kind: ImportKind::Ordinary,
46 import_info: None,
47 };
48}
49
50impl Default for ChainData {
51 fn default() -> Self {
52 Self::EMPTY
53 }
54}
55
56impl<'a> AllocationChain<'a> {
57 pub unsafe fn new_from_record<T: vk::Extends<vk::MemoryAllocateInfo<'a>>>(
68 record: &'a T,
69 ) -> Result<Self, AllocError> {
70 let data = unsafe { inspect(ptr::from_ref(record).cast(), None) }?;
73 Ok(Self {
74 data,
75 borrow: PhantomData,
76 })
77 }
78
79 pub unsafe fn new_from_record_with_import<T: vk::Extends<vk::MemoryAllocateInfo<'a>>>(
97 record: &'a T,
98 info: ImportMemoryInfo,
99 ) -> Result<Self, AllocError> {
100 let data = unsafe { inspect(ptr::from_ref(record).cast(), Some(info)) }?;
103 Ok(Self {
104 data,
105 borrow: PhantomData,
106 })
107 }
108
109 pub fn as_ptr(&self) -> *const c_void {
110 self.data.next
111 }
112}
113
114unsafe fn inspect(
115 next: *const c_void,
116 import_info: Option<ImportMemoryInfo>,
117) -> Result<ChainData, AllocError> {
118 let mut data = ChainData {
119 next,
120 import_info,
121 ..Default::default()
122 };
123 let mut seen = [ptr::null::<vk::BaseInStructure>(); 64];
124 let mut tags = [vk::StructureType::from_raw(0); 64];
125 let mut count = 0;
126 let mut cursor = next.cast::<vk::BaseInStructure>();
127 while !cursor.is_null() {
128 if count == seen.len() || seen[..count].contains(&cursor) {
129 return Err(AllocError::InvalidRequest(
130 "cyclic or excessive allocation chain",
131 ));
132 }
133 let node = unsafe { &*cursor };
135 if tags[..count].contains(&node.s_type) {
136 return Err(AllocError::InvalidRequest(
137 "duplicate allocation chain record",
138 ));
139 }
140 seen[count] = cursor;
141 tags[count] = node.s_type;
142 count += 1;
143 match node.s_type {
144 vk::StructureType::MEMORY_DEDICATED_ALLOCATE_INFO
145 | vk::StructureType::DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV
146 | vk::StructureType::MEMORY_PRIORITY_ALLOCATE_INFO_EXT
147 | vk::StructureType::MEMORY_DEDICATED_ALLOCATE_INFO_TENSOR_ARM => {
148 return Err(AllocError::InvalidRequest(
149 "dedication and priority are allocator-owned",
150 ));
151 }
152 vk::StructureType::MEMORY_ALLOCATE_FLAGS_INFO => {
153 let flags = unsafe { &*cursor.cast::<vk::MemoryAllocateFlagsInfo>() };
155 if flags.flags.as_raw() & vk::MemoryAllocateFlagBits::DEVICE_MASK_BIT.as_raw() != 0
156 && flags.device_mask == 0
157 {
158 return Err(AllocError::InvalidRequest(
159 "device-mask allocation requires a nonzero mask",
160 ));
161 }
162 if flags.flags.as_raw()
163 & vk::MemoryAllocateFlagBits::DEVICE_ADDRESS_CAPTURE_REPLAY_BIT.as_raw()
164 != 0
165 {
166 return Err(AllocError::UnsupportedCapability(
167 "allocation capture/replay",
168 ));
169 }
170 data.flags = Some(flags.flags);
171 }
172 vk::StructureType::EXPORT_MEMORY_ALLOCATE_INFO => {
173 data.exports = unsafe { (*cursor.cast::<vk::ExportMemoryAllocateInfo>()).handle_types };
175 data.has_export = true;
176 }
177 vk::StructureType::IMPORT_MEMORY_FD_INFO_KHR => {
178 if data.imports {
179 return Err(AllocError::InvalidRequest(
180 "multiple memory import operations",
181 ));
182 }
183 let import = unsafe { &*cursor.cast::<vk::ImportMemoryFdInfoKHR>() };
185 if import.fd < 0
186 || (import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::OPAQUE_FD_BIT
187 && import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::DMA_BUF_BIT_EXT)
188 {
189 return Err(AllocError::InvalidRequest("invalid memory fd import"));
190 }
191 data.imports = true;
192 data.import_extension = Some(c"VK_KHR_external_memory_fd");
193 }
194 vk::StructureType::IMPORT_MEMORY_HOST_POINTER_INFO_EXT => {
195 if data.imports {
196 return Err(AllocError::InvalidRequest(
197 "multiple memory import operations",
198 ));
199 }
200 let import = unsafe { &*cursor.cast::<vk::ImportMemoryHostPointerInfoEXT>() };
202 if import.p_host_pointer.is_null()
203 || (import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::HOST_ALLOCATION_BIT_EXT
204 && import.handle_type
205 != vk::ExternalMemoryHandleTypeFlagBits::HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT)
206 {
207 return Err(AllocError::InvalidRequest("invalid host pointer import"));
208 }
209 data.imports = true;
210 data.import_kind = ImportKind::Host;
211 data.import_extension = Some(c"VK_EXT_external_memory_host");
212 }
213 vk::StructureType::MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO => {
214 return Err(AllocError::UnsupportedCapability(
215 "allocation capture/replay",
216 ));
217 }
218 #[cfg(feature = "platform-win32")]
219 vk::StructureType::EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR => {}
220 _ => {
221 let (extension, kind) = unsafe { import::inspect(cursor) }?;
223 if data.imports {
224 return Err(AllocError::InvalidRequest(
225 "multiple memory import operations",
226 ));
227 }
228 data.imports = true;
229 data.import_extension = Some(extension);
230 data.import_kind = kind;
231 }
232 }
233 cursor = node.p_next;
234 }
235 import::validate(&data)?;
236 Ok(data)
237}
238
239#[cfg(test)]
240#[path = "../../../test/rs/mem/test_chain.rs"]
241mod tests;