plate/
command.rs

1use std::sync::Arc;
2
3use ash::vk;
4
5use crate::{Device, Error};
6
7pub use vk::CommandBufferLevel as CommandBufferLevel;
8pub use vk::CommandBufferUsageFlags as CommandBufferUsageFlags;
9
10/// Holds a [`vk::CommandPool`], used to allocate [`CommandBuffers`](CommandBuffer).
11pub struct CommandPool {
12    device: Arc<Device>,
13    cmd_pool: vk::CommandPool,
14}
15
16impl std::ops::Deref for CommandPool {
17    type Target = vk::CommandPool;
18
19    fn deref(&self) -> &Self::Target {
20        &self.cmd_pool
21    }
22}
23
24impl Drop for CommandPool {
25    fn drop(&mut self) {
26        unsafe {
27            self.device.destroy_command_pool(self.cmd_pool, None);
28        }
29    }
30}
31
32impl CommandPool {
33    /// Creates a CommandPool.
34    ///
35    /// # Examples
36    ///
37    /// ```no_run
38    /// # let event_loop = winit::event_loop::EventLoop::new();
39    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
40    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
41    /// let cmd_pool = plate::CommandPool::new(&device)?;
42    /// # Ok::<(), Box<dyn std::error::Error>>(())
43    /// ```
44    pub fn new(device: &Arc<Device>) -> Result<Self, Error> {
45        let pool_info = vk::CommandPoolCreateInfo::builder()
46            .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
47            .queue_family_index(device.queue.family);
48
49        let cmd_pool = unsafe { device.create_command_pool(&pool_info, None)? };
50
51        Ok(Self {
52            device: Arc::clone(&device),
53            cmd_pool,
54        })
55    }
56
57    /// Allocates CommandBuffers.
58    ///
59    /// # Examples
60    ///
61    /// ```no_run
62    /// # let event_loop = winit::event_loop::EventLoop::new();
63    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
64    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
65    /// # let cmd_pool = plate::CommandPool::new(&device)?;
66    /// let cmd_buffers = cmd_pool.alloc_cmd_buffers(plate::CommandBufferLevel::PRIMARY, 2)?;
67    /// # Ok::<(), Box<dyn std::error::Error>>(())
68    /// ```
69    pub fn alloc_cmd_buffers(&self, level: CommandBufferLevel, cmd_buffer_count: u32) -> Result<Vec<CommandBuffer>, Error> {
70        let alloc_info = vk::CommandBufferAllocateInfo::builder()
71            .command_pool(self.cmd_pool)
72            .level(level)
73            .command_buffer_count(cmd_buffer_count);
74
75        let cmd_buffers = unsafe { self.device.allocate_command_buffers(&alloc_info)? };
76        Ok(cmd_buffers.into_iter()
77            .map(|cmd_buffer| CommandBuffer { device: Arc::clone(&self.device), cmd_buffer })
78            .collect())
79    }
80
81    /// Allocates a single CommandBuffer.
82    ///
83    /// # Examples
84    ///
85    /// ```no_run
86    /// # let event_loop = winit::event_loop::EventLoop::new();
87    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
88    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
89    /// # let cmd_pool = plate::CommandPool::new(&device)?;
90    /// let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
91    /// # Ok::<(), Box<dyn std::error::Error>>(())
92    /// ```
93    pub fn alloc_cmd_buffer(&self, level: CommandBufferLevel) -> Result<CommandBuffer, Error> {
94        Ok(self.alloc_cmd_buffers(level, 1)?.swap_remove(0))
95    }
96}
97
98/// Used to send instructions to the GPU.
99pub struct CommandBuffer {
100    device: Arc<Device>,
101    cmd_buffer: vk::CommandBuffer,
102}
103
104impl std::ops::Deref for CommandBuffer {
105    type Target = vk::CommandBuffer;
106
107    fn deref(&self) -> &Self::Target {
108        &self.cmd_buffer
109    }
110}
111
112impl CommandBuffer {
113    /// Records instructions in the given closure to this command buffer.
114    ///
115    /// Runs [`begin()`](Self::begin()), the closure then [`end()`](Self::end()).
116    ///
117    /// # Examples
118    ///
119    /// ```no_run
120    /// # let event_loop = winit::event_loop::EventLoop::new();
121    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
122    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
123    /// # let cmd_pool = plate::CommandPool::new(&device)?;
124    /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
125    /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || {
126    ///     // cmd_buffer.draw(..);
127    /// })?;
128    /// # Ok::<(), Box<dyn std::error::Error>>(())
129    /// ```
130    pub fn record<F: FnOnce()>(&self, flags: CommandBufferUsageFlags, f: F) -> Result<(), Error> {
131        self.begin(flags)?;
132        f();
133        self.end()?;
134        Ok(())
135    }
136
137    /// Begin recording instructions to this command buffer.
138    ///
139    /// # Examples
140    ///
141    /// ```no_run
142    /// # let event_loop = winit::event_loop::EventLoop::new();
143    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
144    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
145    /// # let cmd_pool = plate::CommandPool::new(&device)?;
146    /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
147    /// cmd_buffer.begin(plate::CommandBufferUsageFlags::empty())?;
148    /// // cmd_buffer.draw(..);
149    /// # Ok::<(), Box<dyn std::error::Error>>(())
150    /// ```
151    pub fn begin(&self, flags: CommandBufferUsageFlags) -> Result<(), Error> {
152        let info = vk::CommandBufferBeginInfo::builder().flags(flags);
153        unsafe {
154            self.device.reset_command_buffer(self.cmd_buffer, vk::CommandBufferResetFlags::empty())?;
155            self.device.begin_command_buffer(self.cmd_buffer, &info)?;
156        }
157        Ok(())
158    }
159
160    /// Stop recording instructions to this command buffer.
161    ///
162    /// # Examples
163    ///
164    /// ```no_run
165    /// # let event_loop = winit::event_loop::EventLoop::new();
166    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
167    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
168    /// # let cmd_pool = plate::CommandPool::new(&device)?;
169    /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
170    /// // cmd_buffer.draw(..);
171    /// cmd_buffer.end()?;
172    /// # Ok::<(), Box<dyn std::error::Error>>(())
173    /// ```
174    pub fn end(&self) -> Result<(), Error> {
175        unsafe { self.device.end_command_buffer(self.cmd_buffer)? };
176        Ok(())
177    }
178
179    /// Calls a [`draw`](ash::Device::cmd_draw()) command.
180    ///
181    /// To be used when recording a CommandBuffer.
182    ///
183    /// # Examples
184    ///
185    /// ```no_run
186    /// # let event_loop = winit::event_loop::EventLoop::new();
187    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
188    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
189    /// # let cmd_pool = plate::CommandPool::new(&device)?;
190    /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
191    /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || {
192    ///     cmd_buffer.draw(3, 1, 0, 0);
193    /// })?;
194    /// # Ok::<(), Box<dyn std::error::Error>>(())
195    /// ```
196    pub fn draw(&self, vert_count: u32, instance_count: u32, first_vert: u32, first_instance: u32) {
197        unsafe { self.device.cmd_draw(self.cmd_buffer, vert_count, instance_count, first_vert, first_instance) }
198    }
199
200    /// Calls a [`draw_indexed`](ash::Device::cmd_draw_indexed()) command.
201    ///
202    /// To be used when recording a CommandBuffer.
203    ///
204    /// # Examples
205    ///
206    /// ```no_run
207    /// # let event_loop = winit::event_loop::EventLoop::new();
208    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
209    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
210    /// # let cmd_pool = plate::CommandPool::new(&device)?;
211    /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?;
212    /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || {
213    ///     cmd_buffer.draw_indexed(3, 1, 0, 0, 0);
214    /// })?;
215    /// # Ok::<(), Box<dyn std::error::Error>>(())
216    /// ```
217    pub fn draw_indexed(&self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) {
218        unsafe { self.device.cmd_draw_indexed(self.cmd_buffer, index_count, instance_count, first_index, vertex_offset, first_instance) }
219    }
220}