demo_vk/graphics/vulkan/frames_in_flight/
mod.rs1use {
2 crate::{
3 graphics::vulkan::{
4 raii, AcquireImageStatus, PresentImageStatus, Swapchain,
5 VulkanContext,
6 },
7 unwrap_here,
8 },
9 anyhow::{Context, Result},
10 ash::vk::{self, Handle},
11 std::{ffi::CString, sync::Arc},
12};
13
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum FrameStatus {
16 FrameStarted(Frame),
21
22 SwapchainNeedsRebuild,
24}
25
26#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29pub struct Frame {
30 command_buffer: vk::CommandBuffer,
31 swapchain_image_index: u32,
32 frame_index: usize,
33 swapchain_image: vk::Image,
34 swapchain_image_view: vk::ImageView,
35}
36
37impl Frame {
38 pub fn command_buffer(&self) -> vk::CommandBuffer {
39 self.command_buffer
40 }
41
42 pub fn swapchain_image_index(&self) -> u32 {
43 self.swapchain_image_index
44 }
45
46 pub fn frame_index(&self) -> usize {
47 self.frame_index
48 }
49
50 pub fn swapchain_image(&self) -> vk::Image {
51 self.swapchain_image
52 }
53
54 pub fn swapchain_image_view(&self) -> vk::ImageView {
55 self.swapchain_image_view
56 }
57}
58
59#[derive(Debug, Eq, PartialEq, Copy, Clone)]
61enum FrameSyncStatus {
62 Assembling,
66
67 Pending,
70}
71
72#[derive(Debug)]
74struct FrameSync {
75 status: FrameSyncStatus,
76 swapchain_image_acquired: raii::Semaphore,
77 graphics_commands_complete: raii::Fence,
78 command_pool: raii::CommandPool,
79 command_buffer: vk::CommandBuffer,
80}
81
82#[doc = simple_mermaid::mermaid!("./frames_in_flight_diagram.mmd")]
89#[derive(Debug)]
98pub struct FramesInFlight {
99 swapchain_image_present_semaphores: Vec<raii::Semaphore>,
102
103 frames: Vec<FrameSync>,
104 frame_index: usize,
105 cxt: Arc<VulkanContext>,
106}
107
108impl FramesInFlight {
109 pub fn new(
111 ctx: Arc<VulkanContext>,
112 swapchain_image_count: usize,
113 frame_count: usize,
114 ) -> Result<Self> {
115 let mut swapchain_image_present_semaphores =
117 Vec::with_capacity(swapchain_image_count);
118 for i in 0..swapchain_image_count {
119 swapchain_image_present_semaphores.push(unwrap_here!(
120 format!("Create present semaphore for swapchain image [{}]", i),
121 raii::Semaphore::new(
122 format!("Swapchain Image Present [{}]", i),
123 ctx.device.clone(),
124 &vk::SemaphoreCreateInfo::default(),
125 )
126 ));
127 }
128
129 let mut frames = Vec::with_capacity(frame_count);
131 for index in 0..frame_count {
132 let command_pool = unwrap_here!(
133 format!("Create command pool for frame {}", index),
134 raii::CommandPool::new(
135 format!("frame [{}]", index),
136 ctx.device.clone(),
137 &vk::CommandPoolCreateInfo {
138 flags: vk::CommandPoolCreateFlags::TRANSIENT,
139 queue_family_index: ctx.graphics_queue_family_index,
140 ..Default::default()
141 },
142 )
143 );
144 let command_buffer = unwrap_here!(
145 format!("Create command buffer for frame {}", index),
146 unsafe {
147 ctx.allocate_command_buffers(
148 &vk::CommandBufferAllocateInfo {
149 command_pool: command_pool.raw,
150 level: vk::CommandBufferLevel::PRIMARY,
151 command_buffer_count: 1,
152 ..Default::default()
153 },
154 )?
155 .first()
156 .copied()
157 .context("Expected exactly one command buffer")
158 }
159 );
160 let buffer_name =
161 CString::new(format!("Frame [{}] Commands", index)).unwrap();
162 ctx.device
163 .set_debug_name(&vk::DebugUtilsObjectNameInfoEXT {
164 object_type: vk::ObjectType::COMMAND_BUFFER,
165 object_handle: command_buffer.as_raw(),
166 p_object_name: buffer_name.as_ptr(),
167 ..Default::default()
168 })?;
169 frames.push(FrameSync {
170 status: FrameSyncStatus::Pending,
171 swapchain_image_acquired: unwrap_here!(
172 format!(
173 "Create image acquired semaphore for frame {index}"
174 ),
175 raii::Semaphore::new(
176 format!("image acquired - frame [{}]", index),
177 ctx.device.clone(),
178 &vk::SemaphoreCreateInfo::default(),
179 )
180 ),
181 graphics_commands_complete: unwrap_here!(
182 format!("Create graphics cmd fence for frame {index}"),
183 raii::Fence::new(
184 format!(
185 "graphics commands complete - frame [{}]",
186 index
187 ),
188 ctx.device.clone(),
189 &vk::FenceCreateInfo {
190 flags: vk::FenceCreateFlags::SIGNALED,
191 ..Default::default()
192 },
193 )
194 ),
195 command_pool,
196 command_buffer,
197 });
198 }
199 Ok(Self {
200 swapchain_image_present_semaphores,
201 frames,
202 frame_index: 0,
203 cxt: ctx,
204 })
205 }
206
207 pub fn rebuild_swapchain_semaphores(
214 &mut self,
215 ctx: &VulkanContext,
216 swapchain_image_count: usize,
217 ) -> Result<()> {
218 self.wait_for_all_frames_to_complete()?;
219
220 unsafe { ctx.device_wait_idle()? };
223
224 self.swapchain_image_present_semaphores.clear();
225 for i in 0..swapchain_image_count {
227 self.swapchain_image_present_semaphores.push(unwrap_here!(
228 format!("Create present semaphore for swapchain image [{}]", i),
229 raii::Semaphore::new(
230 format!("Swapchain Image Present [{}]", i),
231 ctx.device.clone(),
232 &vk::SemaphoreCreateInfo::default(),
233 )
234 ));
235 }
236
237 for (i, frame_sync) in self.frames.iter_mut().enumerate() {
238 frame_sync.swapchain_image_acquired = unwrap_here!(
239 format!("Rebuild frame {i} swapchain image acquired semaphore"),
240 raii::Semaphore::new(
241 format!("Swapchain Image Present [{}]", i),
242 ctx.device.clone(),
243 &vk::SemaphoreCreateInfo::default(),
244 )
245 );
246 }
247
248 Ok(())
249 }
250
251 pub fn frame_count(&self) -> usize {
253 self.frames.len()
254 }
255
256 pub fn wait_for_all_frames_to_complete(&self) -> Result<()> {
262 let fences = self
263 .frames
264 .iter()
265 .filter(|frame_sync| frame_sync.status == FrameSyncStatus::Pending)
266 .map(|frame_sync| frame_sync.graphics_commands_complete.raw)
267 .collect::<Vec<vk::Fence>>();
268 unsafe {
269 self.cxt
270 .wait_for_fences(&fences, true, u64::MAX)
271 .context("wait for all pending frames to complete")
272 }
273 }
274
275 pub fn start_frame(
287 &mut self,
288 swapchain: &Swapchain,
289 ) -> Result<FrameStatus> {
290 self.frame_index = (self.frame_index + 1) % self.frames.len();
291
292 let frame_sync = &mut self.frames[self.frame_index];
293
294 unwrap_here!("Wait for the previous submission to complete", unsafe {
297 self.cxt.wait_for_fences(
298 &[frame_sync.graphics_commands_complete.raw],
299 true,
300 u64::MAX,
301 )
302 });
303
304 let status = unwrap_here!(
306 "Acquire the next swapchain image",
307 swapchain.acquire_image(frame_sync.swapchain_image_acquired.raw)
308 );
309 let swapchain_image_index = match status {
310 AcquireImageStatus::ImageAcquired(index) => index,
311 _ => {
312 return Ok(FrameStatus::SwapchainNeedsRebuild);
313 }
314 };
315
316 unwrap_here!("Reset the frame's fence", unsafe {
323 self.cxt
324 .reset_fences(&[frame_sync.graphics_commands_complete.raw])
325 });
326 frame_sync.status = FrameSyncStatus::Assembling;
328
329 unwrap_here!("Reset the frame's command pool", unsafe {
331 self.cxt.reset_command_pool(
332 frame_sync.command_pool.raw,
333 vk::CommandPoolResetFlags::empty(),
334 )
335 });
336 unwrap_here!("Begin the frame's command buffer", unsafe {
337 self.cxt.begin_command_buffer(
338 frame_sync.command_buffer,
339 &vk::CommandBufferBeginInfo {
340 flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
341 ..Default::default()
342 },
343 )
344 });
345
346 Ok(FrameStatus::FrameStarted(Frame {
347 command_buffer: frame_sync.command_buffer,
348 swapchain_image_index,
349 frame_index: self.frame_index,
350 swapchain_image: swapchain.images()[swapchain_image_index as usize],
351 swapchain_image_view: swapchain.image_views()
352 [swapchain_image_index as usize]
353 .raw,
354 }))
355 }
356
357 pub fn present_frame(
359 &mut self,
360 swapchain: &Swapchain,
361 frame: Frame,
362 ) -> Result<PresentImageStatus> {
363 let frame_sync = &mut self.frames[frame.frame_index()];
364 unwrap_here!("End the frame's command buffer", unsafe {
365 self.cxt.end_command_buffer(frame_sync.command_buffer)
366 });
367
368 let wait_stage = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
369 unwrap_here!("Submit the frame's primary command buffer", unsafe {
370 self.cxt.queue_submit(
371 self.cxt.graphics_queue,
372 &[vk::SubmitInfo {
373 wait_semaphore_count: 1,
374 p_wait_semaphores: &frame_sync.swapchain_image_acquired.raw,
375 p_wait_dst_stage_mask: &wait_stage,
376 command_buffer_count: 1,
377 p_command_buffers: &frame_sync.command_buffer,
378 signal_semaphore_count: 1,
379 p_signal_semaphores: &self
380 .swapchain_image_present_semaphores
381 [frame.swapchain_image_index as usize]
382 .raw,
383 ..Default::default()
384 }],
385 frame_sync.graphics_commands_complete.raw,
386 )
387 });
388 frame_sync.status = FrameSyncStatus::Pending;
389
390 swapchain.present_image(
391 self.swapchain_image_present_semaphores
392 [frame.swapchain_image_index as usize]
393 .raw,
394 frame.swapchain_image_index(),
395 )
396 }
397}
398
399impl Drop for FramesInFlight {
400 fn drop(&mut self) {
401 self.wait_for_all_frames_to_complete().unwrap();
402 unsafe {
403 self.cxt.device_wait_idle().unwrap();
404 }
405 }
406}