bort_vk/
command_buffer.rs

1use crate::{
2    Buffer, CommandPool, DescriptorSet, Device, DeviceOwned, ImageAccess, PipelineAccess,
3    PipelineLayout,
4};
5use ash::{
6    prelude::VkResult,
7    vk::{self, Handle},
8};
9use std::{error::Error, sync::Arc};
10
11pub struct CommandBuffer {
12    handle: vk::CommandBuffer,
13    level: vk::CommandBufferLevel,
14
15    // dependencies
16    command_pool: Arc<CommandPool>,
17}
18
19impl CommandBuffer {
20    /// Allocates a single command buffer. To allocate multiple at a time, use `CommandPool::allocate_command_buffers`.
21    pub fn new(command_pool: Arc<CommandPool>, level: vk::CommandBufferLevel) -> VkResult<Self> {
22        let mut command_buffers = command_pool.allocate_command_buffers(level, 1)?;
23        Ok(command_buffers.remove(0))
24    }
25
26    /// Safety: make sure `handle` was allocated from `descriptor_pool` of type `level`.
27    pub(crate) unsafe fn from_handle(
28        handle: vk::CommandBuffer,
29        level: vk::CommandBufferLevel,
30        command_pool: Arc<CommandPool>,
31    ) -> Self {
32        Self {
33            handle,
34            level,
35            command_pool,
36        }
37    }
38
39    /// Note: this will fail if the command pool wasn't created with `vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER`
40    /// set.
41    ///
42    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkResetCommandBuffer.html>
43    pub fn reset(&self, reset_flags: vk::CommandBufferResetFlags) -> VkResult<()> {
44        unsafe {
45            self.device()
46                .inner()
47                .reset_command_buffer(self.handle, reset_flags)
48        }
49    }
50
51    // Getters
52
53    pub fn handle(&self) -> vk::CommandBuffer {
54        self.handle
55    }
56
57    pub fn level(&self) -> vk::CommandBufferLevel {
58        self.level
59    }
60
61    #[inline]
62    pub fn command_pool(&self) -> &Arc<CommandPool> {
63        &self.command_pool
64    }
65
66    // Commands
67
68    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkBeginCommandBuffer.html>
69    pub fn begin(&self, begin_info: &vk::CommandBufferBeginInfoBuilder) -> VkResult<()> {
70        unsafe {
71            self.device()
72                .inner()
73                .begin_command_buffer(self.handle, begin_info)
74        }
75    }
76
77    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEndCommandBuffer.html>
78    pub fn end(&self) -> VkResult<()> {
79        unsafe { self.device().inner().end_command_buffer(self.handle) }
80    }
81
82    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBeginRenderPass.html>
83    pub fn begin_render_pass(
84        &self,
85        begin_info: &vk::RenderPassBeginInfoBuilder,
86        subpass_contents: vk::SubpassContents,
87    ) {
88        unsafe {
89            self.device()
90                .inner()
91                .cmd_begin_render_pass(self.handle, &begin_info, subpass_contents)
92        }
93    }
94
95    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdNextSubpass.html>
96    pub fn next_subpass(&self, subpass_contents: vk::SubpassContents) {
97        unsafe {
98            self.device()
99                .inner()
100                .cmd_next_subpass(self.handle, subpass_contents)
101        }
102    }
103
104    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdEndRenderPass.html>
105    pub fn end_render_pass(&self) {
106        unsafe { self.device().inner().cmd_end_render_pass(self.handle) }
107    }
108
109    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBindPipeline.html>
110    pub fn bind_pipeline(&self, pipeline: &dyn PipelineAccess) {
111        unsafe {
112            self.device().inner().cmd_bind_pipeline(
113                self.handle,
114                pipeline.bind_point(),
115                pipeline.handle(),
116            )
117        }
118    }
119
120    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBindDescriptorSets.html>
121    pub fn bind_descriptor_sets<'a>(
122        &self,
123        pipeline_bind_point: vk::PipelineBindPoint,
124        pipeline_layout: &PipelineLayout,
125        first_set: u32,
126        descriptor_sets: impl IntoIterator<Item = &'a DescriptorSet>,
127        dynamic_offsets: &[u32],
128    ) {
129        let descriptor_set_handles: Vec<vk::DescriptorSet> = descriptor_sets
130            .into_iter()
131            .map(|descriptor_set| descriptor_set.handle())
132            .collect();
133        unsafe {
134            self.device().inner().cmd_bind_descriptor_sets(
135                self.handle,
136                pipeline_bind_point,
137                pipeline_layout.handle(),
138                first_set,
139                &descriptor_set_handles,
140                dynamic_offsets,
141            )
142        }
143    }
144
145    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBindVertexBuffers.html>
146    pub fn bind_vertex_buffers<'a>(
147        &self,
148        first_binding: u32,
149        buffers: impl IntoIterator<Item = &'a Buffer>,
150        offsets: &[vk::DeviceSize],
151    ) {
152        let buffer_handles: Vec<vk::Buffer> =
153            buffers.into_iter().map(|buffer| buffer.handle()).collect();
154        unsafe {
155            self.device().inner().cmd_bind_vertex_buffers(
156                self.handle,
157                first_binding,
158                &buffer_handles,
159                offsets,
160            )
161        }
162    }
163
164    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBindIndexBuffer.html>
165    pub fn bind_index_buffer(
166        &self,
167        buffer: &Buffer,
168        offset: vk::DeviceSize,
169        index_type: vk::IndexType,
170    ) {
171        unsafe {
172            self.device().inner().cmd_bind_index_buffer(
173                self.handle,
174                buffer.handle(),
175                offset,
176                index_type,
177            )
178        }
179    }
180
181    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdSetViewport.html>
182    pub fn set_viewport(&self, first_viewport: u32, viewports: &[vk::Viewport]) {
183        unsafe {
184            self.device()
185                .inner()
186                .cmd_set_viewport(self.handle, first_viewport, viewports)
187        }
188    }
189
190    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdSetScissor.html>
191    pub fn set_scissor(&self, first_scissor: u32, scissors: &[vk::Rect2D]) {
192        unsafe {
193            self.device()
194                .inner()
195                .cmd_set_scissor(self.handle, first_scissor, scissors)
196        }
197    }
198
199    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdDraw.html>
200    pub fn draw(
201        &self,
202        vertex_count: u32,
203        instance_count: u32,
204        first_vertex: u32,
205        first_instance: u32,
206    ) {
207        unsafe {
208            self.device().inner().cmd_draw(
209                self.handle,
210                vertex_count,
211                instance_count,
212                first_vertex,
213                first_instance,
214            )
215        }
216    }
217
218    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdDrawIndexed.html>
219    pub fn draw_indexed(
220        &self,
221        index_count: u32,
222        instance_count: u32,
223        first_index: u32,
224        vertex_offset: i32,
225        first_instance: u32,
226    ) {
227        unsafe {
228            self.device().inner().cmd_draw_indexed(
229                self.handle,
230                index_count,
231                instance_count,
232                first_index,
233                vertex_offset,
234                first_instance,
235            )
236        }
237    }
238
239    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdDrawIndexedIndirect.html>
240    pub fn draw_indexed_indirect(
241        &self,
242        buffer: &Buffer,
243        offset: vk::DeviceSize,
244        draw_count: u32,
245        stride: u32,
246    ) {
247        unsafe {
248            self.device().inner().cmd_draw_indexed_indirect(
249                self.handle,
250                buffer.handle(),
251                offset,
252                draw_count,
253                stride,
254            )
255        }
256    }
257
258    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdExecuteCommands.html>
259    pub fn execute_commands(
260        &self,
261        secondary_command_buffers: &[&CommandBuffer],
262    ) -> Result<(), CommandError> {
263        let any_primary_buffers = secondary_command_buffers
264            .iter()
265            .any(|command_buffer| command_buffer.level == vk::CommandBufferLevel::PRIMARY);
266        if any_primary_buffers {
267            return Err(CommandError::CantExecutePrimaryCommandBuffer);
268        }
269
270        let secondary_command_buffer_handles: Vec<vk::CommandBuffer> = secondary_command_buffers
271            .iter()
272            .map(|command_buffer| command_buffer.handle())
273            .collect();
274
275        unsafe {
276            self.device()
277                .inner()
278                .cmd_execute_commands(self.handle, &secondary_command_buffer_handles);
279        }
280
281        Ok(())
282    }
283
284    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdCopyBuffer.html>
285    pub fn copy_buffer(
286        &self,
287        src_buffer: &Buffer,
288        dst_buffer: &Buffer,
289        regions: &[vk::BufferCopy],
290    ) {
291        unsafe {
292            self.device().inner().cmd_copy_buffer(
293                self.handle,
294                src_buffer.handle(),
295                dst_buffer.handle(),
296                regions,
297            )
298        }
299    }
300
301    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdCopyBufferToImage.html>
302    pub fn copy_buffer_to_image(
303        &self,
304        src_buffer: &Buffer,
305        dst_image: &dyn ImageAccess,
306        dst_image_layout: vk::ImageLayout,
307        regions: &[vk::BufferImageCopy],
308    ) {
309        unsafe {
310            self.device().inner().cmd_copy_buffer_to_image(
311                self.handle,
312                src_buffer.handle(),
313                dst_image.handle(),
314                dst_image_layout,
315                regions,
316            )
317        }
318    }
319
320    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdPushConstants.html>
321    pub fn push_constants(
322        &self,
323        pipeline_layout: &PipelineLayout,
324        stage_flags: vk::ShaderStageFlags,
325        offset: u32,
326        constants: &[u8],
327    ) {
328        unsafe {
329            self.device().inner().cmd_push_constants(
330                self.handle,
331                pipeline_layout.handle(),
332                stage_flags,
333                offset,
334                constants,
335            )
336        }
337    }
338}
339
340impl Drop for CommandBuffer {
341    fn drop(&mut self) {
342        unsafe {
343            self.device()
344                .inner()
345                .free_command_buffers(self.command_pool.handle(), &[self.handle])
346        }
347    }
348}
349
350impl DeviceOwned for CommandBuffer {
351    #[inline]
352    fn device(&self) -> &Arc<Device> {
353        self.command_pool.device()
354    }
355
356    #[inline]
357    fn handle_raw(&self) -> u64 {
358        self.handle.as_raw()
359    }
360}
361
362// ~~ Errors ~~
363
364#[derive(Clone, Copy, Debug)]
365pub enum CommandError {
366    CantExecutePrimaryCommandBuffer,
367}
368
369impl std::fmt::Display for CommandError {
370    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
371        match self {
372            Self::CantExecutePrimaryCommandBuffer => write!(
373                f,
374                "attempted to call vkCmdExecuteCommands on a primary command buffer"
375            ),
376        }
377    }
378}
379
380impl Error for CommandError {}