1use crate::{
10 command_buffer::CommandBufferLevel,
11 device::{Device, DeviceOwned},
12 instance::InstanceOwnedDebugWrapper,
13 macros::{impl_id_counter, vulkan_bitflags},
14 Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
15 VulkanObject,
16};
17use smallvec::SmallVec;
18use std::{cell::Cell, marker::PhantomData, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
19
20#[derive(Debug)]
28pub struct CommandPool {
29 handle: ash::vk::CommandPool,
30 device: InstanceOwnedDebugWrapper<Arc<Device>>,
31 id: NonZeroU64,
32
33 flags: CommandPoolCreateFlags,
34 queue_family_index: u32,
35
36 _marker: PhantomData<Cell<ash::vk::CommandPool>>,
38}
39
40impl CommandPool {
41 pub fn new(
43 device: Arc<Device>,
44 create_info: CommandPoolCreateInfo,
45 ) -> Result<CommandPool, Validated<VulkanError>> {
46 Self::validate_new(&device, &create_info)?;
47
48 Ok(unsafe { Self::new_unchecked(device, create_info) }?)
49 }
50
51 fn validate_new(
52 device: &Device,
53 create_info: &CommandPoolCreateInfo,
54 ) -> Result<(), Box<ValidationError>> {
55 create_info
56 .validate(device)
57 .map_err(|err| err.add_context("create_info"))?;
58
59 Ok(())
60 }
61
62 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
63 pub unsafe fn new_unchecked(
64 device: Arc<Device>,
65 create_info: CommandPoolCreateInfo,
66 ) -> Result<Self, VulkanError> {
67 let create_info_vk = create_info.to_vk();
68
69 let handle = {
70 let fns = device.fns();
71 let mut output = MaybeUninit::uninit();
72 unsafe {
73 (fns.v1_0.create_command_pool)(
74 device.handle(),
75 &create_info_vk,
76 ptr::null(),
77 output.as_mut_ptr(),
78 )
79 }
80 .result()
81 .map_err(VulkanError::from)?;
82 unsafe { output.assume_init() }
83 };
84
85 Ok(unsafe { Self::from_handle(device, handle, create_info) })
86 }
87
88 #[inline]
95 pub unsafe fn from_handle(
96 device: Arc<Device>,
97 handle: ash::vk::CommandPool,
98 create_info: CommandPoolCreateInfo,
99 ) -> CommandPool {
100 let CommandPoolCreateInfo {
101 flags,
102 queue_family_index,
103 _ne: _,
104 } = create_info;
105
106 CommandPool {
107 handle,
108 device: InstanceOwnedDebugWrapper(device),
109 id: Self::next_id(),
110
111 flags,
112 queue_family_index,
113
114 _marker: PhantomData,
115 }
116 }
117
118 #[inline]
120 pub fn flags(&self) -> CommandPoolCreateFlags {
121 self.flags
122 }
123
124 #[inline]
126 pub fn queue_family_index(&self) -> u32 {
127 self.queue_family_index
128 }
129
130 #[inline]
136 pub unsafe fn reset(&self, flags: CommandPoolResetFlags) -> Result<(), Validated<VulkanError>> {
137 self.validate_reset(flags)?;
138
139 Ok(unsafe { self.reset_unchecked(flags) }?)
140 }
141
142 fn validate_reset(&self, flags: CommandPoolResetFlags) -> Result<(), Box<ValidationError>> {
143 flags.validate_device(self.device()).map_err(|err| {
144 err.add_context("flags")
145 .set_vuids(&["VUID-vkResetCommandPool-flags-parameter"])
146 })?;
147
148 Ok(())
149 }
150
151 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
152 pub unsafe fn reset_unchecked(&self, flags: CommandPoolResetFlags) -> Result<(), VulkanError> {
153 let fns = self.device.fns();
154 unsafe { (fns.v1_0.reset_command_pool)(self.device.handle(), self.handle, flags.into()) }
155 .result()
156 .map_err(VulkanError::from)?;
157
158 Ok(())
159 }
160
161 #[inline]
163 pub fn allocate_command_buffers(
164 &self,
165 allocate_info: CommandBufferAllocateInfo,
166 ) -> Result<impl ExactSizeIterator<Item = CommandPoolAlloc>, VulkanError> {
167 let CommandBufferAllocateInfo {
168 level,
169 command_buffer_count,
170 _ne: _,
171 } = allocate_info;
172
173 let out = if command_buffer_count == 0 {
175 vec![]
176 } else {
177 let allocate_info_vk = allocate_info.to_vk(self.handle);
178 let command_buffer_count = command_buffer_count as usize;
179
180 let fns = self.device.fns();
181 let mut out = Vec::with_capacity(command_buffer_count);
182
183 unsafe {
184 (fns.v1_0.allocate_command_buffers)(
185 self.device.handle(),
186 &allocate_info_vk,
187 out.as_mut_ptr(),
188 )
189 }
190 .result()
191 .map_err(VulkanError::from)?;
192 unsafe { out.set_len(command_buffer_count) };
193
194 out
195 };
196
197 let device = self.device.clone();
198
199 Ok(out.into_iter().map(move |command_buffer| CommandPoolAlloc {
200 handle: command_buffer,
201 device: InstanceOwnedDebugWrapper(device.clone()),
202 id: CommandPoolAlloc::next_id(),
203 level,
204 }))
205 }
206
207 pub unsafe fn free_command_buffers(
214 &self,
215 command_buffers: impl IntoIterator<Item = CommandPoolAlloc>,
216 ) -> Result<(), Box<ValidationError>> {
217 let command_buffers: SmallVec<[_; 4]> = command_buffers.into_iter().collect();
218 self.validate_free_command_buffers(&command_buffers)?;
219
220 unsafe { self.free_command_buffers_unchecked(command_buffers) };
221 Ok(())
222 }
223
224 fn validate_free_command_buffers(
225 &self,
226 _command_buffers: &[CommandPoolAlloc],
227 ) -> Result<(), Box<ValidationError>> {
228 Ok(())
233 }
234
235 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
236 pub unsafe fn free_command_buffers_unchecked(
237 &self,
238 command_buffers: impl IntoIterator<Item = CommandPoolAlloc>,
239 ) {
240 let command_buffers_vk: SmallVec<[_; 4]> =
241 command_buffers.into_iter().map(|cb| cb.handle).collect();
242
243 let fns = self.device.fns();
244 unsafe {
245 (fns.v1_0.free_command_buffers)(
246 self.device.handle(),
247 self.handle,
248 command_buffers_vk.len() as u32,
249 command_buffers_vk.as_ptr(),
250 )
251 }
252 }
253
254 #[inline]
265 pub fn trim(&self) -> Result<(), Box<ValidationError>> {
266 self.validate_trim()?;
267
268 unsafe { self.trim_unchecked() }
269 Ok(())
270 }
271
272 fn validate_trim(&self) -> Result<(), Box<ValidationError>> {
273 if !(self.device.api_version() >= Version::V1_1
274 || self.device.enabled_extensions().khr_maintenance1)
275 {
276 return Err(Box::new(ValidationError {
277 requires_one_of: RequiresOneOf(&[
278 RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
279 RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance1")]),
280 ]),
281 ..Default::default()
282 }));
283 }
284
285 Ok(())
286 }
287
288 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
289 pub unsafe fn trim_unchecked(&self) {
290 let fns = self.device.fns();
291
292 if self.device.api_version() >= Version::V1_1 {
293 unsafe {
294 (fns.v1_1.trim_command_pool)(
295 self.device.handle(),
296 self.handle,
297 ash::vk::CommandPoolTrimFlags::empty(),
298 )
299 };
300 } else {
301 unsafe {
302 (fns.khr_maintenance1.trim_command_pool_khr)(
303 self.device.handle(),
304 self.handle,
305 ash::vk::CommandPoolTrimFlagsKHR::empty(),
306 )
307 };
308 }
309 }
310}
311
312impl Drop for CommandPool {
313 #[inline]
314 fn drop(&mut self) {
315 let fns = self.device.fns();
316 unsafe { (fns.v1_0.destroy_command_pool)(self.device.handle(), self.handle, ptr::null()) };
317 }
318}
319
320unsafe impl VulkanObject for CommandPool {
321 type Handle = ash::vk::CommandPool;
322
323 #[inline]
324 fn handle(&self) -> Self::Handle {
325 self.handle
326 }
327}
328
329unsafe impl DeviceOwned for CommandPool {
330 #[inline]
331 fn device(&self) -> &Arc<Device> {
332 &self.device
333 }
334}
335
336impl_id_counter!(CommandPool);
337
338#[derive(Clone, Debug)]
340pub struct CommandPoolCreateInfo {
341 pub flags: CommandPoolCreateFlags,
345
346 pub queue_family_index: u32,
351
352 pub _ne: crate::NonExhaustive,
353}
354
355impl Default for CommandPoolCreateInfo {
356 #[inline]
357 fn default() -> Self {
358 Self {
359 flags: CommandPoolCreateFlags::empty(),
360 queue_family_index: u32::MAX,
361 _ne: crate::NonExhaustive(()),
362 }
363 }
364}
365
366impl CommandPoolCreateInfo {
367 pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
368 let &Self {
369 flags,
370 queue_family_index,
371 _ne: _,
372 } = self;
373
374 flags.validate_device(device).map_err(|err| {
375 err.add_context("flags")
376 .set_vuids(&["VUID-VkCommandPoolCreateInfo-flags-parameter"])
377 })?;
378
379 if queue_family_index >= device.physical_device().queue_family_properties().len() as u32 {
380 return Err(Box::new(ValidationError {
381 context: "queue_family_index".into(),
382 problem: "is not less than the number of queue families in the physical device"
383 .into(),
384 vuids: &["VUID-vkCreateCommandPool-queueFamilyIndex-01937"],
385 ..Default::default()
386 }));
387 }
388
389 Ok(())
390 }
391
392 pub(crate) fn to_vk(&self) -> ash::vk::CommandPoolCreateInfo<'static> {
393 let &Self {
394 flags,
395 queue_family_index,
396 _ne: _,
397 } = self;
398
399 ash::vk::CommandPoolCreateInfo::default()
400 .flags(flags.into())
401 .queue_family_index(queue_family_index)
402 }
403}
404
405vulkan_bitflags! {
406 #[non_exhaustive]
407
408 CommandPoolCreateFlags = CommandPoolCreateFlags(u32);
410
411 TRANSIENT = TRANSIENT,
414
415 RESET_COMMAND_BUFFER = RESET_COMMAND_BUFFER,
417
418 }
425
426vulkan_bitflags! {
427 #[non_exhaustive]
428
429 CommandPoolResetFlags = CommandPoolResetFlags(u32);
431
432 RELEASE_RESOURCES = RELEASE_RESOURCES,
435}
436
437#[derive(Clone, Debug)]
439pub struct CommandBufferAllocateInfo {
440 pub level: CommandBufferLevel,
444
445 pub command_buffer_count: u32,
449
450 pub _ne: crate::NonExhaustive,
451}
452
453impl CommandBufferAllocateInfo {
454 pub(crate) fn to_vk(
455 &self,
456 command_pool_vk: ash::vk::CommandPool,
457 ) -> ash::vk::CommandBufferAllocateInfo<'static> {
458 let &Self {
459 level,
460 command_buffer_count,
461 _ne: _,
462 } = self;
463
464 ash::vk::CommandBufferAllocateInfo::default()
465 .command_pool(command_pool_vk)
466 .level(level.into())
467 .command_buffer_count(command_buffer_count)
468 }
469}
470
471impl Default for CommandBufferAllocateInfo {
472 #[inline]
473 fn default() -> Self {
474 Self {
475 level: CommandBufferLevel::Primary,
476 command_buffer_count: 1,
477 _ne: crate::NonExhaustive(()),
478 }
479 }
480}
481
482#[derive(Debug)]
484pub struct CommandPoolAlloc {
485 handle: ash::vk::CommandBuffer,
486 device: InstanceOwnedDebugWrapper<Arc<Device>>,
487 id: NonZeroU64,
488 level: CommandBufferLevel,
489}
490
491impl CommandPoolAlloc {
492 #[inline]
494 pub fn level(&self) -> CommandBufferLevel {
495 self.level
496 }
497}
498
499unsafe impl VulkanObject for CommandPoolAlloc {
500 type Handle = ash::vk::CommandBuffer;
501
502 #[inline]
503 fn handle(&self) -> Self::Handle {
504 self.handle
505 }
506}
507
508unsafe impl DeviceOwned for CommandPoolAlloc {
509 #[inline]
510 fn device(&self) -> &Arc<Device> {
511 &self.device
512 }
513}
514
515impl_id_counter!(CommandPoolAlloc);
516
517#[cfg(test)]
518mod tests {
519 use super::{CommandPool, CommandPoolCreateInfo};
520 use crate::{
521 command_buffer::{pool::CommandBufferAllocateInfo, CommandBufferLevel},
522 Validated,
523 };
524
525 #[test]
526 fn basic_create() {
527 let (device, queue) = gfx_dev_and_queue!();
528 let _ = CommandPool::new(
529 device,
530 CommandPoolCreateInfo {
531 queue_family_index: queue.queue_family_index(),
532 ..Default::default()
533 },
534 )
535 .unwrap();
536 }
537
538 #[test]
539 fn queue_family_getter() {
540 let (device, queue) = gfx_dev_and_queue!();
541 let pool = CommandPool::new(
542 device,
543 CommandPoolCreateInfo {
544 queue_family_index: queue.queue_family_index(),
545 ..Default::default()
546 },
547 )
548 .unwrap();
549 assert_eq!(pool.queue_family_index(), queue.queue_family_index());
550 }
551
552 #[test]
553 fn check_queue_family_too_high() {
554 let (device, _) = gfx_dev_and_queue!();
555
556 match CommandPool::new(
557 device,
558 CommandPoolCreateInfo {
559 ..Default::default()
560 },
561 ) {
562 Err(Validated::ValidationError(_)) => (),
563 _ => panic!(),
564 }
565 }
566
567 #[test]
571 fn basic_alloc() {
572 let (device, queue) = gfx_dev_and_queue!();
573 let pool = CommandPool::new(
574 device,
575 CommandPoolCreateInfo {
576 queue_family_index: queue.queue_family_index(),
577 ..Default::default()
578 },
579 )
580 .unwrap();
581 let iter = pool
582 .allocate_command_buffers(CommandBufferAllocateInfo {
583 level: CommandBufferLevel::Primary,
584 command_buffer_count: 12,
585 ..Default::default()
586 })
587 .unwrap();
588 assert_eq!(iter.count(), 12);
589 }
590}