1use super::*;
7
8pub(super) unsafe fn validate_buffer(
9 allocator: &Allocator<'_>,
10 info: &vk::BufferCreateInfo,
11) -> Result<bool, AllocError> {
12 allocator.ensure_open()?;
13 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 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 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 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
212pub 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
262pub unsafe fn find_memory_type_for_buffer_info(
276 allocator: &Allocator<'_>,
277 info: &vk::BufferCreateInfo,
278 options: &AllocationCreateInfo,
279) -> Result<u32, AllocError> {
280 unsafe { validate_buffer(allocator, info) }?;
282 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 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 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 let queried = unsafe { allocator.device.get_buffer_memory_requirements(buffer) };
325 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
339pub unsafe fn find_memory_type_for_image_info(
350 allocator: &Allocator<'_>,
351 info: &vk::ImageCreateInfo,
352 options: &AllocationCreateInfo,
353) -> Result<u32, AllocError> {
354 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 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 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 let queried = unsafe {
398 allocator
399 .device
400 .as_device()
401 .get_image_memory_requirements(image, &mut output.memory_requirements)
402 };
403 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
417pub 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 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 if let Err(error) = unsafe { binding::persistent(allocator, &allocation, options) } {
486 unsafe { rollback_allocation(allocator, &mut allocation) }
488 .expect("preflighted allocation rollback");
489 return Err(error);
490 }
491 Ok(allocation)
492}
493
494impl Allocator<'_> {
495 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 pub unsafe fn find_memory_type_for_buffer_info(
510 &self,
511 info: &vk::BufferCreateInfo,
512 options: &AllocationCreateInfo,
513 ) -> Result<u32, AllocError> {
514 unsafe { find_memory_type_for_buffer_info(self, info, options) }
516 }
517
518 pub unsafe fn find_memory_type_for_image_info(
522 &self,
523 info: &vk::ImageCreateInfo,
524 options: &AllocationCreateInfo,
525 ) -> Result<u32, AllocError> {
526 unsafe { find_memory_type_for_image_info(self, info, options) }
528 }
529
530 pub unsafe fn allocate_with_options(
534 &mut self,
535 requirements: &vk::MemoryRequirements,
536 options: &AllocationCreateInfo,
537 ) -> Result<Allocation, AllocError> {
538 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
559pub 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 unsafe { allocate_with_options(allocator, requirements, &options) }
576}
577
578pub 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 unsafe { create_buffer_with_options(allocator, info, &options) }
594}
595
596pub 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 unsafe { create_image_with_options(allocator, info, &options) }
611}
612
613impl Allocator<'_> {
614 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 unsafe { allocate_dedicated(self, requirements, options, chain) }
627 }
628
629 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 unsafe { create_dedicated_buffer(self, info, options, chain) }
642 }
643
644 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 unsafe { create_dedicated_image(self, info, options, chain) }
657 }
658}