1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use {
    super::{Device, DriverError, QueueFamily},
    ash::vk,
    log::{trace, warn},
    std::{fmt::Debug, ops::Deref, sync::Arc, thread::panicking},
};

#[derive(Debug)]
pub struct CommandBuffer {
    cmd_buf: vk::CommandBuffer,
    pub device: Arc<Device>,
    droppables: Vec<Box<dyn Debug + Send + 'static>>,
    pub fence: vk::Fence, // Keeps state because everyone wants this
    pub pool: vk::CommandPool,
}

impl CommandBuffer {
    pub fn create(device: &Arc<Device>, queue_family: QueueFamily) -> Result<Self, DriverError> {
        let device = Arc::clone(device);
        let cmd_pool_info = vk::CommandPoolCreateInfo::builder()
            .flags(vk::CommandPoolCreateFlags::empty())
            .queue_family_index(queue_family.idx);
        let cmd_pool = unsafe {
            device
                .create_command_pool(&cmd_pool_info, None)
                .map_err(|err| {
                    warn!("{err}");

                    DriverError::Unsupported
                })?
        };
        let cmd_buf_info = vk::CommandBufferAllocateInfo::builder()
            .command_buffer_count(1)
            .command_pool(cmd_pool)
            .level(vk::CommandBufferLevel::PRIMARY);
        let cmd_buf = unsafe {
            device
                .allocate_command_buffers(&cmd_buf_info)
                .map_err(|err| {
                    warn!("{err}");

                    DriverError::Unsupported
                })?
        }[0];
        let fence = unsafe {
            device
                .create_fence(
                    &vk::FenceCreateInfo::builder()
                        .flags(vk::FenceCreateFlags::SIGNALED)
                        .build(),
                    None,
                )
                .map_err(|err| {
                    warn!("{err}");

                    DriverError::Unsupported
                })?
        };

        Ok(Self {
            cmd_buf,
            device,
            droppables: vec![],
            fence,
            pool: cmd_pool,
        })
    }

    /// Signals that execution has completed and it is time to drop anything we collected.
    pub(crate) fn drop_fenced(this: &mut Self) {
        if !this.droppables.is_empty() {
            trace!("dropping {} shared references", this.droppables.len());
        }

        this.droppables.clear();
    }

    /// Drops an item after execution has been completed
    pub(crate) fn push_fenced_drop(this: &mut Self, thing_to_drop: impl Debug + Send + 'static) {
        this.droppables.push(Box::new(thing_to_drop));
    }

    pub fn queue_family_index(this: &Self) -> u32 {
        this.device.queue.family.idx
    }
}

impl Deref for CommandBuffer {
    type Target = vk::CommandBuffer;

    fn deref(&self) -> &Self::Target {
        &self.cmd_buf
    }
}

impl Drop for CommandBuffer {
    fn drop(&mut self) {
        use std::slice::from_ref;

        if panicking() {
            return;
        }

        unsafe {
            if Device::wait_for_fence(&self.device, &self.fence).is_err() {
                return;
            }

            self.device
                .free_command_buffers(self.pool, from_ref(&self.cmd_buf));
            self.device.destroy_command_pool(self.pool, None);
            self.device.destroy_fence(self.fence, None);
        }
    }
}