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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use {
    crate::graphics::{
        vulkan_api::{raii, RenderDevice},
        GraphicsError,
    },
    anyhow::Context,
    ash::vk,
    std::sync::Arc,
};

/// All of the per-frame synchronization resources.
#[derive(Debug)]
pub(super) struct FrameSync {
    pub(super) index: usize,
    pub(super) command_pool: raii::CommandPool,
    pub(super) swapchain_image_acquired_semaphore: raii::Semaphore,
    pub(super) graphics_commands_completed_semaphore: raii::Semaphore,
    pub(super) graphics_commands_completed_fence: raii::Fence,
    render_device: Arc<RenderDevice>,
}

impl FrameSync {
    /// Create synchronization resources for a single in-flight frame.
    ///
    /// # Params
    ///
    /// * `render_device` - the render device used to create and destroy all
    ///   frame resources
    /// * `index` - the frame's index
    ///
    /// # Safety
    ///
    /// Unsafe because:
    ///   - all resources must be destroyed before application exit
    pub unsafe fn new(
        render_device: Arc<RenderDevice>,
        index: usize,
    ) -> Result<Self, GraphicsError> {
        let swapchain_image_acquired_semaphore = unsafe {
            raii::Semaphore::new(
                render_device.clone(),
                &vk::SemaphoreCreateInfo::default(),
            )?
        };
        swapchain_image_acquired_semaphore
            .set_debug_name(format!("Frame {index} Swapchain Image Acquired"));

        let graphics_commands_completed_semaphore = unsafe {
            raii::Semaphore::new(
                render_device.clone(),
                &vk::SemaphoreCreateInfo::default(),
            )?
        };
        graphics_commands_completed_semaphore.set_debug_name(format!(
            "Frame {index} Graphics Commands Completed"
        ));

        let graphics_commands_completed_fence = unsafe {
            let create_info = vk::FenceCreateInfo {
                flags: vk::FenceCreateFlags::SIGNALED,
                ..Default::default()
            };
            raii::Fence::new(render_device.clone(), &create_info)?
        };
        graphics_commands_completed_fence.set_debug_name(format!(
            "Frame {index} Graphics Commands Completed"
        ));

        let mut command_pool = unsafe {
            let create_info = vk::CommandPoolCreateInfo {
                flags: vk::CommandPoolCreateFlags::TRANSIENT,
                ..Default::default()
            };
            raii::CommandPool::new(render_device.clone(), &create_info)?
        };
        command_pool.set_debug_name(format!("Frame {index} Command Pool"));
        let _ = command_pool.allocate_primary_command_buffers(1);

        Ok(Self {
            index,
            command_pool,
            swapchain_image_acquired_semaphore,
            graphics_commands_completed_semaphore,
            graphics_commands_completed_fence,
            render_device,
        })
    }

    /// Wait for this frame's last graphics command submission to complete.
    ///
    /// # Params
    ///
    /// * `render_device` - the device used to create the frame sync resources
    pub fn wait_for_graphics_commands_to_complete(
        &self,
    ) -> Result<(), GraphicsError> {
        unsafe {
            self.render_device
                .device()
                .wait_for_fences(
                    &[self.graphics_commands_completed_fence.raw()],
                    true,
                    u64::MAX,
                )
                .context(
                    "Error while waiting for graphics commands to complete",
                )?
        }
        Ok(())
    }

    /// Reset and restart the command buffer for this frame.
    ///
    /// # Params
    ///
    /// * `render_device` - the device used to create the frame sync resources
    pub fn wait_and_restart_command_buffer(&self) -> Result<(), GraphicsError> {
        self.wait_for_graphics_commands_to_complete()?;
        unsafe {
            // SAFE because we wait for the previous submission's commands to
            // complete before resetting and restarting resources.
            self.render_device
                .device()
                .reset_fences(&[self.graphics_commands_completed_fence.raw()])
                .with_context(|| {
                    format!(
                        "Could not reset graphics completed fence for frame {}",
                        self.index
                    )
                })?;
            self.render_device
                .device()
                .reset_command_pool(
                    self.command_pool.raw(),
                    vk::CommandPoolResetFlags::empty(),
                )
                .with_context(|| {
                    format!(
                        "Could not reset command pool for frame {}",
                        self.index
                    )
                })?;
            let begin_info = vk::CommandBufferBeginInfo {
                flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
                ..Default::default()
            };
            self.render_device
                .device()
                .begin_command_buffer(
                    self.command_pool.primary_command_buffer(0),
                    &begin_info,
                )
                .with_context(|| {
                    format!(
                        "Could not begin command buffer for frame {}",
                        self.index
                    )
                })?;
        }
        Ok(())
    }
}