screen_13/driver/
cmd_buf.rs

1use {
2    super::{DriverError, device::Device},
3    ash::vk,
4    log::{error, trace, warn},
5    std::{fmt::Debug, ops::Deref, sync::Arc, thread::panicking},
6};
7
8// TODO: Expose command functions so the fence, device, waiting flags do not
9// need to be public
10
11/// Represents a Vulkan command buffer to which some work has been submitted.
12#[derive(Debug)]
13pub struct CommandBuffer {
14    cmd_buf: vk::CommandBuffer,
15    pub(crate) device: Arc<Device>,
16    droppables: Vec<Box<dyn Debug + Send + 'static>>,
17    pub(crate) fence: vk::Fence, // Keeps state because everyone wants this
18
19    /// Information used to create this object.
20    pub info: CommandBufferInfo,
21
22    pub(crate) pool: vk::CommandPool,
23    pub(crate) waiting: bool,
24}
25
26impl CommandBuffer {
27    #[profiling::function]
28    pub(crate) fn create(
29        device: &Arc<Device>,
30        info: CommandBufferInfo,
31    ) -> Result<Self, DriverError> {
32        let device = Arc::clone(device);
33        let cmd_pool_info = vk::CommandPoolCreateInfo::default()
34            .flags(
35                vk::CommandPoolCreateFlags::TRANSIENT
36                    | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
37            )
38            .queue_family_index(info.queue_family_index);
39        let pool = unsafe {
40            device
41                .create_command_pool(&cmd_pool_info, None)
42                .map_err(|err| {
43                    warn!("{err}");
44
45                    DriverError::Unsupported
46                })?
47        };
48        let cmd_buf_info = vk::CommandBufferAllocateInfo::default()
49            .command_buffer_count(1)
50            .command_pool(pool)
51            .level(vk::CommandBufferLevel::PRIMARY);
52        let cmd_buf = unsafe {
53            device
54                .allocate_command_buffers(&cmd_buf_info)
55                .map_err(|err| {
56                    warn!("{err}");
57
58                    DriverError::Unsupported
59                })?
60        }[0];
61        let fence = Device::create_fence(&device, false)?;
62
63        Ok(Self {
64            cmd_buf,
65            device,
66            droppables: vec![],
67            fence,
68            info,
69            pool,
70            waiting: false,
71        })
72    }
73
74    /// Signals that execution has completed and it is time to drop anything we collected.
75    #[profiling::function]
76    pub(crate) fn drop_fenced(this: &mut Self) {
77        if !this.droppables.is_empty() {
78            trace!("dropping {} shared references", this.droppables.len());
79        }
80
81        this.droppables.clear();
82    }
83
84    /// Returns `true` after the GPU has executed the previous submission to this command buffer.
85    ///
86    /// See [`Self::wait_until_executed`] to block while checking.
87    #[profiling::function]
88    pub fn has_executed(&self) -> Result<bool, DriverError> {
89        let res = unsafe { self.device.get_fence_status(self.fence) };
90
91        match res {
92            Ok(status) => Ok(status),
93            Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
94                error!("Device lost");
95
96                Err(DriverError::InvalidData)
97            }
98            Err(err) => {
99                // VK_SUCCESS and VK_NOT_READY handled by get_fence_status in ash
100                // VK_ERROR_DEVICE_LOST already handled above, so no idea what happened
101                error!("{}", err);
102
103                Err(DriverError::InvalidData)
104            }
105        }
106    }
107
108    /// Drops an item after execution has been completed
109    pub(crate) fn push_fenced_drop(this: &mut Self, thing_to_drop: impl Debug + Send + 'static) {
110        this.droppables.push(Box::new(thing_to_drop));
111    }
112
113    /// Stalls by blocking the current thread until the GPU has executed the previous submission to
114    /// this command buffer.
115    ///
116    /// See [`Self::has_executed`] to check without blocking.
117    #[profiling::function]
118    pub fn wait_until_executed(&mut self) -> Result<(), DriverError> {
119        if !self.waiting {
120            return Ok(());
121        }
122
123        Device::wait_for_fence(&self.device, &self.fence)?;
124        self.waiting = false;
125
126        Ok(())
127    }
128}
129
130impl Deref for CommandBuffer {
131    type Target = vk::CommandBuffer;
132
133    fn deref(&self) -> &Self::Target {
134        &self.cmd_buf
135    }
136}
137
138impl Drop for CommandBuffer {
139    #[profiling::function]
140    fn drop(&mut self) {
141        use std::slice::from_ref;
142
143        if panicking() {
144            return;
145        }
146
147        unsafe {
148            if self.waiting && Device::wait_for_fence(&self.device, &self.fence).is_err() {
149                return;
150            }
151
152            Self::drop_fenced(self);
153
154            self.device
155                .free_command_buffers(self.pool, from_ref(&self.cmd_buf));
156            self.device.destroy_command_pool(self.pool, None);
157            self.device.destroy_fence(self.fence, None);
158        }
159    }
160}
161
162#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
163pub struct CommandBufferInfo {
164    pub queue_family_index: u32,
165}
166
167impl CommandBufferInfo {
168    pub fn new(queue_family_index: u32) -> Self {
169        Self { queue_family_index }
170    }
171}