Skip to main content

maia/
command_buffer.rs

1// Copyright 2022 Google LLC
2
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::fmt::Debug;
10use std::mem::MaybeUninit;
11use std::sync::Weak;
12
13use crate::descriptor_set::DescriptorSetLayout;
14use crate::device::Device;
15use crate::enums::*;
16use crate::error::{Error, ErrorAndSelf, Result, ResultAndSelf};
17use crate::exclusive::Exclusive;
18use crate::framebuffer::Framebuffer;
19use crate::pipeline::Pipeline;
20use crate::render_pass::RenderPass;
21use crate::subobject::{Owner, Subobject};
22use crate::types::*;
23
24mod transfer;
25pub mod barrier;
26mod bind;
27mod draw;
28
29/// A command pool.
30///
31/// Any resources that are recorded into command buffers allocated from the pool
32/// have their reference count incremented and held by the pool. To decrement
33/// the count and allow the resources to be freed, either call
34/// [`reset`](CommandPool::reset()) or drop the pool and all buffers allocated
35/// from it.
36///
37/// `reset`, in turn, returns [`Error::SynchronizationError`] if any command
38/// buffers allocated from the pool are still pending.
39/// See the documentation of [`Queue`](crate::vk::Queue) for more details
40/// on how to wait for them.
41pub struct CommandPool {
42    recording: Option<Arc<RecordedCommands>>,
43    res: Owner<CommandPoolLifetime>,
44    scratch: Exclusive<bumpalo::Bump>,
45}
46
47/// A primary command buffer.
48///
49/// **Note:** Dropping the CommandBuffer and not dropping the [`CommandPool`] it
50/// was allocated from will leak the pool's resources. Instead, pass it to
51/// [`CommandPool::free`]
52///
53/// Create with [`CommandPool::allocate`]
54#[must_use = "Leaks pool resources if not freed"]
55#[derive(Debug)]
56// Theoretically the arc could be on the outside, but this would encourage users
57// to store copies of it, causing confusing errors when they try to record only
58// to find it locked.
59pub struct CommandBuffer(Arc<CommandBufferLifetime>);
60
61/// A secondary command buffer.
62///
63/// **Note:** Dropping the CommandBuffer and not dropping the [`CommandPool]` it
64/// was allocated from will leak the pool's resources. Instead, pass it to
65/// [`CommandPool::free_secondary`]
66///
67/// Create with [`CommandPool::allocate_secondary`]
68#[must_use = "Leaks pool resources if not freed"]
69#[derive(Debug)]
70pub struct SecondaryCommandBuffer {
71    buf: Arc<CommandBufferLifetime>,
72    pass: Option<Arc<RenderPass>>,
73    subpass: u32,
74}
75
76struct Bindings<'a> {
77    layout: bumpalo::collections::Vec<'a, Arc<DescriptorSetLayout>>,
78    inited: bumpalo::collections::Vec<'a, bool>,
79    pipeline: Option<Arc<Pipeline>>,
80}
81
82/// An in-progress command buffer recording, outside of a render pass.
83pub struct CommandRecording<'a> {
84    pool: &'a mut Owner<CommandPoolLifetime>,
85    recording: &'a Arc<RecordedCommands>,
86    scratch: &'a bumpalo::Bump,
87    graphics: Bindings<'a>,
88    compute: Bindings<'a>,
89    buffer: Owner<CommandBufferLifetime>,
90}
91
92/// An in-progress command buffer recording, inside a render pass.
93#[must_use = "Record render pass commands on this object"]
94pub struct RenderPassRecording<'a> {
95    rec: CommandRecording<'a>,
96    pass: Arc<RenderPass>,
97    subpass: u32,
98}
99
100/// An in-progress command buffer recording, inside a render pass whose contents
101/// is provided with secondary command buffers.
102#[must_use = "Record secondary command buffers on this object"]
103pub struct ExternalRenderPassRecording<'a> {
104    rec: CommandRecording<'a>,
105    pass: Arc<RenderPass>,
106    subpass: u32,
107}
108
109/// An in-progress secondary command buffer recording, inside a render pass.
110pub struct SecondaryCommandRecording<'a> {
111    rec: CommandRecording<'a>,
112    pass: Arc<RenderPass>,
113    subpass: u32,
114}
115
116#[derive(Debug)]
117struct CommandBufferLifetime {
118    handle: Handle<VkCommandBuffer>,
119    pool: Subobject<CommandPoolLifetime>,
120    /// For buffers in the executable state, it will give an Arc. Otherwise the
121    /// buffer is in the initial state.
122    recording: Weak<RecordedCommands>,
123}
124
125#[derive(Debug)]
126struct CommandPoolLifetime {
127    handle: Handle<VkCommandPool>,
128    resources: Vec<Arc<dyn Send + Sync + Debug>>,
129    device: Arc<Device>,
130}
131
132#[derive(Debug)]
133struct RecordedCommands(Subobject<CommandPoolLifetime>);
134
135impl std::fmt::Debug for CommandPool {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        self.res.handle.fmt(f)
138    }
139}
140
141// We never put anything in here that uses interior mutability in 'Debug'
142impl std::panic::UnwindSafe for CommandPoolLifetime {}
143impl std::panic::RefUnwindSafe for CommandPoolLifetime {}
144
145impl CommandPool {
146    /// Create a command pool. The pool is not transient, not protected, and its
147    /// buffers cannot be individually reset.
148    #[doc = crate::man_link!(vkCreateCommandPool)]
149    pub fn new(device: &Arc<Device>, queue_family_index: u32) -> Result<Self> {
150        if !device.has_queue(queue_family_index, 1) {
151            return Err(Error::OutOfBounds);
152        }
153        let mut handle = None;
154        unsafe {
155            (device.fun.create_command_pool)(
156                device.handle(),
157                &CommandPoolCreateInfo {
158                    queue_family_index,
159                    ..Default::default()
160                },
161                None,
162                &mut handle,
163            )?;
164        }
165        let handle = handle.unwrap();
166
167        let res = Owner::new(CommandPoolLifetime {
168            handle,
169            resources: vec![],
170            device: device.clone(),
171        });
172        let _res = Subobject::new(&res);
173        Ok(CommandPool {
174            res,
175            recording: Some(Arc::new(RecordedCommands(_res))),
176            scratch: Exclusive::new(bumpalo::Bump::new()),
177        })
178    }
179}
180
181impl Drop for CommandPoolLifetime {
182    fn drop(&mut self) {
183        unsafe {
184            (self.device.fun.destroy_command_pool)(
185                self.device.handle(),
186                self.handle.borrow_mut(),
187                None,
188            )
189        }
190    }
191}
192
193impl CommandPool {
194    /// Borrows the inner Vulkan handle.
195    pub fn mut_handle(&mut self) -> Mut<VkCommandPool> {
196        self.res.handle.borrow_mut()
197    }
198
199    /// Return [`Error::SynchronizationError`] if any command buffers are
200    /// pending.
201    pub fn reset(&mut self, flags: CommandPoolResetFlags) -> Result<()> {
202        match Arc::try_unwrap(self.recording.take().unwrap()) {
203            // Buffer in pending state
204            Err(arc) => {
205                self.recording = Some(arc);
206                Err(Error::SynchronizationError)
207            }
208            Ok(_) => {
209                let res = &mut *self.res;
210                unsafe {
211                    (res.device.fun.reset_command_pool)(
212                        res.device.handle(),
213                        res.handle.borrow_mut(),
214                        flags,
215                    )?;
216                }
217                self.recording =
218                    Some(Arc::new(RecordedCommands(Subobject::new(&self.res))));
219                self.res.resources.clear();
220                Ok(())
221            }
222        }
223    }
224
225    /// Allocate a new primary command buffer from the pool.
226    #[doc = crate::man_link!(vkAllocateCommandBuffers)]
227    pub fn allocate(&mut self) -> Result<CommandBuffer> {
228        Ok(CommandBuffer(self.allocate_impl(CommandBufferLevel::PRIMARY)?))
229    }
230
231    /// Allocate a new secondary command buffer from the pool.
232    #[doc = crate::man_link!(vkAllocateCommandBuffers)]
233    pub fn allocate_secondary(&mut self) -> Result<SecondaryCommandBuffer> {
234        Ok(SecondaryCommandBuffer {
235            buf: self.allocate_impl(CommandBufferLevel::SECONDARY)?,
236            pass: None,
237            subpass: 0,
238        })
239    }
240
241    fn allocate_impl(
242        &mut self, level: CommandBufferLevel,
243    ) -> Result<Arc<CommandBufferLifetime>> {
244        let mut handle = MaybeUninit::uninit();
245        let res = &mut *self.res;
246        let handle = unsafe {
247            (res.device.fun.allocate_command_buffers)(
248                res.device.handle(),
249                &CommandBufferAllocateInfo {
250                    stype: Default::default(),
251                    next: Default::default(),
252                    pool: res.handle.borrow_mut(),
253                    level,
254                    count: 1,
255                },
256                std::array::from_mut(&mut handle).into(),
257            )?;
258            handle.assume_init()
259        };
260        Ok(Arc::new(CommandBufferLifetime {
261            handle,
262            pool: Subobject::new(&self.res),
263            recording: Weak::new(),
264        }))
265    }
266
267    /// Return [`Error::SynchronizationError`] if `buffer` is pending.
268    #[doc = crate::man_link!(vkFreeCommandBuffers)]
269    pub fn free(&mut self, mut buffer: CommandBuffer) -> Result<()> {
270        if !Owner::ptr_eq(&self.res, &buffer.0.pool) {
271            return Err(Error::InvalidArgument);
272        }
273        self.free_impl(buffer.mut_handle()?);
274        Ok(())
275    }
276
277    /// Return [`Error::SynchronizationError`] if `buffer` is pending.
278    #[doc = crate::man_link!(vkFreeCommandBuffers)]
279    pub fn free_secondary(
280        &mut self, mut buffer: SecondaryCommandBuffer,
281    ) -> Result<()> {
282        if !Owner::ptr_eq(&self.res, &buffer.buf.pool) {
283            return Err(Error::InvalidArgument);
284        }
285        self.free_impl(buffer.mut_handle()?);
286        Ok(())
287    }
288
289    fn free_impl(&mut self, buffer: Mut<VkCommandBuffer>) {
290        let res = &mut *self.res;
291        unsafe {
292            (res.device.fun.free_command_buffers)(
293                res.device.handle(),
294                res.handle.borrow_mut(),
295                1,
296                &buffer,
297            );
298        }
299    }
300
301    /// Returns [`Error::InvalidArgument`] if the buffer does not belong to this
302    /// pool or is in the executable state. Returns
303    /// [`Error::SynchronizationError`] if the buffer is in the pending state.
304    #[doc = crate::man_link!(vkBeginCommandBuffer)]
305    pub fn begin(
306        &mut self, buffer: CommandBuffer,
307    ) -> ResultAndSelf<CommandRecording<'_>, CommandBuffer> {
308        if !Owner::ptr_eq(&self.res, &buffer.0.pool)
309            // In executable state
310            || buffer.lock_resources().is_some()
311        {
312            return Err(ErrorAndSelf(Error::InvalidArgument, buffer));
313        }
314        // In pending state
315        let mut inner = Owner::from_arc(buffer.0).map_err(|arc| {
316            ErrorAndSelf(Error::SynchronizationError, CommandBuffer(arc))
317        })?;
318        unsafe {
319            if let Err(err) = (self.res.device.fun.begin_command_buffer)(
320                inner.handle.borrow_mut(),
321                &Default::default(),
322            ) {
323                return Err(ErrorAndSelf(
324                    err.into(),
325                    CommandBuffer(Owner::into_arc(inner)),
326                ));
327            };
328        }
329        let scratch = self.scratch.get_mut();
330        scratch.reset();
331        Ok(CommandRecording {
332            pool: &mut self.res,
333            recording: self.recording.as_ref().unwrap(),
334            graphics: Bindings::new(scratch),
335            compute: Bindings::new(scratch),
336            scratch,
337            buffer: inner,
338        })
339    }
340
341    /// Returns [`Error::InvalidArgument`] if the buffer does not belong to this
342    /// pool or is in the executable state. Returns
343    /// [`Error::SynchronizationError`] if the buffer is in the pending state.
344    #[doc = crate::man_link!(vkBeginCommandBuffer)]
345    pub fn begin_secondary<'a>(
346        &'a mut self, buffer: SecondaryCommandBuffer,
347        render_pass: &Arc<RenderPass>, subpass: u32,
348    ) -> ResultAndSelf<SecondaryCommandRecording<'a>, SecondaryCommandBuffer>
349    {
350        if subpass >= render_pass.num_subpasses() {
351            return Err(ErrorAndSelf(Error::InvalidArgument, buffer));
352        }
353        if !Owner::ptr_eq(&self.res, &buffer.buf.pool)
354            // In executable state
355            || buffer.lock_resources().is_some()
356        {
357            return Err(ErrorAndSelf(Error::InvalidArgument, buffer));
358        }
359        // In pending state
360        let mut inner = Owner::from_arc(buffer.buf).map_err(|arc| {
361            ErrorAndSelf(
362                Error::SynchronizationError,
363                SecondaryCommandBuffer { buf: arc, pass: None, subpass: 0 },
364            )
365        })?;
366        unsafe {
367            if let Err(err) = (self.res.device.fun.begin_command_buffer)(
368                inner.handle.borrow_mut(),
369                &CommandBufferBeginInfo {
370                    flags: CommandBufferUsageFlags::RENDER_PASS_CONTINUE,
371                    inheritance_info: Some(&CommandBufferInheritanceInfo {
372                        stype: Default::default(),
373                        next: Default::default(),
374                        render_pass: render_pass.handle(),
375                        subpass,
376                        framebuffer: Default::default(),
377                        occlusion_query_enable: Default::default(),
378                        query_flags: Default::default(),
379                        pipeline_statistics: Default::default(),
380                    }),
381                    ..Default::default()
382                },
383            ) {
384                return Err(ErrorAndSelf(
385                    err.into(),
386                    SecondaryCommandBuffer {
387                        buf: Owner::into_arc(inner),
388                        pass: None,
389                        subpass: 0,
390                    },
391                ));
392            };
393        }
394        let scratch = self.scratch.get_mut();
395        scratch.reset();
396        Ok(SecondaryCommandRecording {
397            rec: CommandRecording {
398                pool: &mut self.res,
399                recording: self.recording.as_ref().unwrap(),
400                graphics: Bindings::new(scratch),
401                compute: Bindings::new(scratch),
402                scratch,
403                buffer: inner,
404            },
405            pass: render_pass.clone(),
406            subpass,
407        })
408    }
409}
410
411impl CommandBuffer {
412    /// Attempts to borrow the inner Vulkan handle. Returns
413    /// [`Error::SynchronizationError`] if the buffer is in the pending state.
414    pub fn mut_handle(&mut self) -> Result<Mut<VkCommandBuffer>> {
415        match Arc::get_mut(&mut self.0) {
416            Some(inner) => Ok(inner.handle.borrow_mut()),
417            None => Err(Error::SynchronizationError),
418        }
419    }
420    /// Prevent the command buffer from being freed or submitted to a queue
421    /// until the the value is dropped
422    pub(crate) fn lock_self(&self) -> Arc<impl Send + Sync + Debug> {
423        self.0.clone()
424    }
425    /// Prevent the command pool from being cleared or destroyed until the value
426    /// is dropped.
427    pub(crate) fn lock_resources(
428        &self,
429    ) -> Option<Arc<impl Send + Sync + Debug>> {
430        self.0.recording.upgrade()
431    }
432}
433
434impl SecondaryCommandBuffer {
435    /// Attempts to borrow the inner Vulkan handle. Returns
436    /// [`Error::SynchronizationError`] if the buffer is in the pending state.
437    pub fn mut_handle(&mut self) -> Result<Mut<VkCommandBuffer>> {
438        match Arc::get_mut(&mut self.buf) {
439            Some(inner) => Ok(inner.handle.borrow_mut()),
440            None => Err(Error::SynchronizationError),
441        }
442    }
443    /// Prevent the command buffer from being freed or submitted to a queue
444    /// until the the value is dropped
445    pub(crate) fn lock_self(&self) -> Arc<impl Send + Sync + Debug> {
446        self.buf.clone()
447    }
448    /// Prevent the command pool from being cleared or destroyed until the value
449    /// is dropped.
450    pub(crate) fn lock_resources(
451        &self,
452    ) -> Option<Arc<impl Send + Sync + Debug>> {
453        self.buf.recording.upgrade()
454    }
455}
456
457impl<'a> Bindings<'a> {
458    fn new(scratch: &'a bumpalo::Bump) -> Self {
459        Self {
460            layout: bumpalo::vec![in scratch],
461            inited: bumpalo::vec![in scratch],
462            pipeline: None,
463        }
464    }
465}
466
467impl<'a> CommandRecording<'a> {
468    fn add_resource(&mut self, value: Arc<dyn Send + Sync + Debug>) {
469        self.pool.resources.push(value);
470    }
471    /// A failed call to vkEndCommandBuffer leaves the buffer in the invalid
472    /// state, so it is dropped in that case.
473    #[doc = crate::man_link!(vkEndCommandBuffer)]
474    pub fn end(mut self) -> Result<CommandBuffer> {
475        unsafe {
476            (self.pool.device.fun.end_command_buffer)(
477                self.buffer.handle.borrow_mut(),
478            )?;
479        }
480        self.buffer.recording = Arc::downgrade(self.recording);
481        Ok(CommandBuffer(Owner::into_arc(self.buffer)))
482    }
483}
484
485impl<'a> SecondaryCommandRecording<'a> {
486    /// A failed call to vkEndCommandBuffer leaves the buffer in the invalid
487    /// state, so it is dropped in that case.
488    #[doc = crate::man_link!(vkEndCommandBuffer)]
489    pub fn end(mut self) -> Result<SecondaryCommandBuffer> {
490        unsafe {
491            (self.rec.pool.device.fun.end_command_buffer)(
492                self.rec.buffer.handle.borrow_mut(),
493            )?;
494        }
495        self.rec.buffer.recording = Arc::downgrade(self.rec.recording);
496        Ok(SecondaryCommandBuffer {
497            buf: Owner::into_arc(self.rec.buffer),
498            pass: Some(self.pass),
499            subpass: self.subpass,
500        })
501    }
502}
503
504impl<'a> CommandRecording<'a> {
505    /// Begins a render pass recorded inline. Returns [`Error::InvalidArgument`]
506    /// if `framebuffer` and `render_pass` are not compatible.
507    #[doc = crate::man_link!(vkCmdBeginRenderPass)]
508    pub fn begin_render_pass(
509        mut self, render_pass: &Arc<RenderPass>,
510        framebuffer: &Arc<Framebuffer>, render_area: &Rect2D,
511        clear_values: &[ClearValue],
512    ) -> Result<RenderPassRecording<'a>> {
513        self.begin_render_pass_impl(
514            render_pass,
515            framebuffer,
516            render_area,
517            clear_values,
518            SubpassContents::INLINE,
519        )?;
520        Ok(RenderPassRecording {
521            rec: self,
522            pass: render_pass.clone(),
523            subpass: 0,
524        })
525    }
526    /// Begins a render pass recorded in secondary command buffers. Returns
527    /// [`Error::InvalidArgument`] if `framebuffer` and `render_pass` are not
528    /// compatible.
529    #[doc = crate::man_link!(vkCmdBeginRenderPass)]
530    pub fn begin_render_pass_secondary(
531        mut self, render_pass: &Arc<RenderPass>,
532        framebuffer: &Arc<Framebuffer>, render_area: &Rect2D,
533        clear_values: &[ClearValue],
534    ) -> Result<ExternalRenderPassRecording<'a>> {
535        if !framebuffer.is_compatible_with(render_pass) {
536            return Err(Error::InvalidArgument);
537        }
538        self.begin_render_pass_impl(
539            render_pass,
540            framebuffer,
541            render_area,
542            clear_values,
543            SubpassContents::SECONDARY_COMMAND_BUFFERS,
544        )?;
545        Ok(ExternalRenderPassRecording {
546            rec: self,
547            pass: render_pass.clone(),
548            subpass: 0,
549        })
550    }
551    fn begin_render_pass_impl(
552        &mut self, render_pass: &Arc<RenderPass>,
553        framebuffer: &Arc<Framebuffer>, render_area: &Rect2D,
554        clear_values: &[ClearValue], subpass_contents: SubpassContents,
555    ) -> Result<()> {
556        if !framebuffer.is_compatible_with(render_pass) {
557            return Err(Error::InvalidArgument);
558        }
559        self.add_resource(render_pass.clone());
560        self.add_resource(framebuffer.clone());
561        let info = RenderPassBeginInfo {
562            stype: Default::default(),
563            next: Default::default(),
564            render_pass: render_pass.handle(),
565            framebuffer: framebuffer.handle(),
566            render_area: *render_area,
567            clear_values: clear_values.into(),
568        };
569        unsafe {
570            (self.pool.device.fun.cmd_begin_render_pass)(
571                self.buffer.handle.borrow_mut(),
572                &info,
573                subpass_contents,
574            );
575        }
576        Ok(())
577    }
578}
579
580impl<'a> RenderPassRecording<'a> {
581    /// Advance to the next subpass, recorded inline. Returns
582    /// [`Error::OutOfBounds`] if this is the last subpass.
583    #[doc = crate::man_link!(vkCmdNextSubpass)]
584    pub fn next_subpass(&mut self) -> Result<()> {
585        if self.subpass >= self.pass.num_subpasses() - 1 {
586            return Err(Error::OutOfBounds);
587        }
588        self.subpass += 1;
589        unsafe {
590            (self.rec.pool.device.fun.cmd_next_subpass)(
591                self.rec.buffer.handle.borrow_mut(),
592                SubpassContents::INLINE,
593            )
594        }
595        Ok(())
596    }
597    /// Advance to the next subpass, recorded in secondary command buffers.
598    /// Returns [`Error::OutOfBounds`] if this is the last subpass.
599    #[doc = crate::man_link!(vkCmdNextSubpass)]
600    pub fn next_subpass_secondary(
601        mut self,
602    ) -> Result<ExternalRenderPassRecording<'a>> {
603        if self.subpass >= self.pass.num_subpasses() - 1 {
604            return Err(Error::OutOfBounds);
605        }
606        unsafe {
607            (self.rec.pool.device.fun.cmd_next_subpass)(
608                self.rec.buffer.handle.borrow_mut(),
609                SubpassContents::SECONDARY_COMMAND_BUFFERS,
610            );
611        }
612        Ok(ExternalRenderPassRecording {
613            rec: self.rec,
614            pass: self.pass,
615            subpass: self.subpass + 1,
616        })
617    }
618    /// Ends the render pass. Returns [`Error::InvalidState`] if this is not the
619    /// last subpass.
620    #[doc = crate::man_link!(vkCmdEndRenderPass)]
621    pub fn end(mut self) -> Result<CommandRecording<'a>> {
622        if self.subpass != self.pass.num_subpasses() - 1 {
623            return Err(Error::InvalidState);
624        }
625        unsafe {
626            (self.rec.pool.device.fun.cmd_end_render_pass)(
627                self.rec.buffer.handle.borrow_mut(),
628            );
629        }
630        Ok(self.rec)
631    }
632}
633
634impl<'a> ExternalRenderPassRecording<'a> {
635    /// Advance to the next subpass, recorded in secondary command buffers.
636    /// Returns [`Error::OutOfBounds`] if this is the last subpass.
637    #[doc = crate::man_link!(vkCmdNextSubpass)]
638    pub fn next_subpass_secondary(&mut self) -> Result<()> {
639        if self.subpass >= self.pass.num_subpasses() - 1 {
640            return Err(Error::OutOfBounds);
641        }
642        self.subpass += 1;
643        unsafe {
644            (self.rec.pool.device.fun.cmd_next_subpass)(
645                self.rec.buffer.handle.borrow_mut(),
646                SubpassContents::SECONDARY_COMMAND_BUFFERS,
647            )
648        }
649        Ok(())
650    }
651    /// Advance to the next subpass, recorded inline. Returns
652    /// [`Error::OutOfBounds`] if this is the last subpass.
653    #[doc = crate::man_link!(vkCmdNextSubpass)]
654    pub fn next_subpass(mut self) -> Result<RenderPassRecording<'a>> {
655        if self.subpass >= self.pass.num_subpasses() - 1 {
656            return Err(Error::OutOfBounds);
657        }
658        unsafe {
659            (self.rec.pool.device.fun.cmd_next_subpass)(
660                self.rec.buffer.handle.borrow_mut(),
661                SubpassContents::INLINE,
662            );
663        }
664        Ok(RenderPassRecording {
665            rec: self.rec,
666            pass: self.pass,
667            subpass: self.subpass + 1,
668        })
669    }
670    /// Ends the render pass. Returns [`Error::InvalidState`] if this is not the
671    /// last subpass.
672    #[doc = crate::man_link!(vkCmdEndRenderPass)]
673    pub fn end(mut self) -> Result<CommandRecording<'a>> {
674        if self.subpass != self.pass.num_subpasses() - 1 {
675            return Err(Error::InvalidState);
676        }
677        unsafe {
678            (self.rec.pool.device.fun.cmd_end_render_pass)(
679                self.rec.buffer.handle.borrow_mut(),
680            );
681        }
682        Ok(self.rec)
683    }
684}
685
686#[cfg(test)]
687mod test {
688    use crate::vk;
689
690    #[test]
691    fn secondary_reset() -> vk::Result<()> {
692        let (dev, _) = crate::test_device()?;
693        let pass = vk::RenderPass::new(
694            &dev,
695            &vk::RenderPassCreateInfo {
696                subpasses: vk::slice(&[Default::default()]),
697                ..Default::default()
698            },
699        )?;
700        let fb = vk::Framebuffer::new(
701            &pass,
702            Default::default(),
703            vec![],
704            Default::default(),
705        )?;
706        let mut pool1 = vk::CommandPool::new(&dev, 0)?;
707        let mut pool2 = vk::CommandPool::new(&dev, 0)?;
708
709        let sec = pool2.allocate_secondary()?;
710        let mut sec = pool2.begin_secondary(sec, &pass, 0)?.end()?;
711        let prim = pool1.allocate()?;
712        let rec = pool1.begin(prim)?;
713        let mut rec = rec.begin_render_pass_secondary(
714            &pass,
715            &fb,
716            &Default::default(),
717            Default::default(),
718        )?;
719        rec.execute_commands(&mut [&mut sec])?;
720        let prim = rec.end()?.end()?;
721
722        assert!(pool2.reset(Default::default()).is_err());
723        assert!(pool1.reset(Default::default()).is_ok());
724        assert!(pool2.reset(Default::default()).is_ok());
725
726        assert!(pool1.free_secondary(sec).is_err());
727        assert!(pool2.free(prim).is_err());
728
729        let sec = pool1.allocate_secondary()?;
730        let mut sec = pool1.begin_secondary(sec, &pass, 0)?.end()?;
731        let prim = pool1.allocate()?;
732        let rec = pool1.begin(prim)?;
733        let mut rec = rec.begin_render_pass_secondary(
734            &pass,
735            &fb,
736            &Default::default(),
737            Default::default(),
738        )?;
739        rec.execute_commands(&mut [&mut sec])?;
740        let _ = rec.end()?.end()?;
741
742        assert!(pool1.reset(Default::default()).is_ok());
743
744        Ok(())
745    }
746
747    #[test]
748    fn subpass() -> vk::Result<()> {
749        let (dev, _) = crate::test_device()?;
750        let pass = vk::RenderPass::new(
751            &dev,
752            &vk::RenderPassCreateInfo {
753                subpasses: vk::slice(&[Default::default(), Default::default()]),
754                ..Default::default()
755            },
756        )?;
757        let fb = vk::Framebuffer::new(
758            &pass,
759            Default::default(),
760            vec![],
761            Default::default(),
762        )?;
763
764        let mut pool = vk::CommandPool::new(&dev, 0)?;
765
766        let buf = pool.allocate()?;
767        let rec = pool.begin(buf)?;
768        let rec = rec.begin_render_pass(
769            &pass,
770            &fb,
771            &Default::default(),
772            Default::default(),
773        )?;
774        assert!(rec.end().is_err());
775
776        let buf = pool.allocate()?;
777        let rec = pool.begin(buf)?;
778        let mut rec = rec.begin_render_pass(
779            &pass,
780            &fb,
781            &Default::default(),
782            Default::default(),
783        )?;
784        assert!(rec.next_subpass().is_ok());
785        assert!(rec.next_subpass().is_err());
786        assert!(rec.next_subpass_secondary().is_err());
787
788        pool.reset(Default::default())?;
789
790        let buf = pool.allocate()?;
791        let rec = pool.begin(buf)?;
792        let mut rec = rec.begin_render_pass_secondary(
793            &pass,
794            &fb,
795            &Default::default(),
796            Default::default(),
797        )?;
798        assert!(rec.next_subpass_secondary().is_ok());
799        assert!(rec.next_subpass_secondary().is_err());
800        assert!(rec.next_subpass().is_err());
801
802        Ok(())
803    }
804}