1use {
4 derive_builder::Builder,
5 log::{Level, log_enabled, trace, warn},
6 std::{
7 error::Error,
8 fmt::{Debug, Formatter},
9 mem::take,
10 ops::Deref,
11 slice,
12 thread::panicking,
13 },
14 vk_graph::{
15 Graph,
16 driver::{
17 DriverError,
18 ash::vk,
19 cmd_buf::{CommandBuffer, CommandBufferInfo},
20 device::Device,
21 fence::Fence,
22 surface::Surface,
23 swapchain::{
24 PresentError, PresentFailure, PresentInfo, PresentResult, Swapchain,
25 SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder,
26 },
27 },
28 node::SwapchainImageNode,
29 pool::{Pool, SubmissionPool},
30 submission::{QueueSubmitInfo, RecordSelection, SemaphoreSubmitInfo},
31 },
32};
33
34#[read_only::embed]
36pub struct Graphchain {
37 frame_idx: usize,
38 frames: Box<[FrameSlot]>,
39 image_frames: Vec<usize>,
40 last_present_queue_index: u32,
41 queue_family_index: u32,
42 recreate_pending: bool,
43 requested_info: GraphchainInfo,
44 strategy: PresentRetirementStrategy,
45
46 #[readonly]
53 pub info: EffectiveGraphchainInfo,
54
55 #[readonly]
59 pub swapchain: Swapchain,
60}
61
62impl Deref for ReadOnlyGraphchain {
63 type Target = Swapchain;
64
65 fn deref(&self) -> &Self::Target {
66 &self.swapchain
67 }
68}
69
70impl Graphchain {
71 pub fn new(surface: Surface, info: impl Into<GraphchainInfo>) -> Result<Self, DriverError> {
73 let mut info = info.into();
74
75 info.frame_capacity = info
77 .frame_capacity
78 .max(info.min_image_count as usize)
79 .max(1);
80
81 let swapchain_info: SwapchainInfo = info.into();
82 let swapchain = Swapchain::create(surface, swapchain_info)?;
83 let physical_device = &swapchain.surface.device.physical;
84 let queue_family_index = physical_device
85 .queue_families
86 .iter()
87 .enumerate()
88 .find_map(|(idx, _)| {
89 swapchain
90 .surface
91 .physical_device_support(idx as u32)
92 .ok()
93 .and_then(|supported| supported.then_some(idx as u32))
94 })
95 .ok_or(DriverError::Unsupported)?;
96
97 let strategy = if swapchain
99 .surface
100 .device
101 .physical
102 .vk_khr_present_id
103 .is_some()
104 && swapchain
105 .surface
106 .device
107 .physical
108 .vk_khr_present_wait
109 .is_some()
110 {
111 PresentRetirementStrategy::PresentWait(PresentWait::default())
112 } else {
113 PresentRetirementStrategy::PerImageSemaphore(PerImageSemaphore::default())
114 };
115
116 let mut frames = Vec::with_capacity(info.frame_capacity);
117 for _ in 0..info.frame_capacity {
118 let cmd_buf = CommandBuffer::create(
119 &swapchain.surface.device,
120 CommandBufferInfo::new(queue_family_index),
121 )?;
122 let fence = Fence::create(&swapchain.surface.device, false)?;
123 let swapchain_acquired = Device::create_semaphore(&swapchain.surface.device)?;
124 Device::try_set_debug_utils_object_name(
125 &swapchain.surface.device,
126 swapchain_acquired,
127 "graphchain acquired semaphore",
128 );
129
130 frames.push(FrameSlot {
131 cmd_buf,
132 fence,
133 swapchain_acquired,
134 });
135 }
136
137 let effective_info = {
138 let GraphchainInfo {
139 acquire_timeout,
140 clipped,
141 composite_alpha,
142 frame_capacity,
143 height,
144 min_image_count,
145 present_mode,
146 surface,
147 width,
148 } = info;
149
150 EffectiveGraphchainInfo {
151 acquire_timeout,
152 clipped,
153 composite_alpha,
154 frame_capacity,
155 frame_count: frame_capacity,
156 height,
157 image_count: swapchain.info.image_count as usize,
158 min_image_count,
159 present_mode,
160 surface,
161 width,
162 }
163 };
164
165 Ok(Self {
166 frame_idx: info.frame_capacity - 1,
167 frames: frames.into_boxed_slice(),
168 image_frames: Vec::new(),
169 last_present_queue_index: 0,
170 queue_family_index,
171 recreate_pending: false,
172 requested_info: info,
173 strategy,
174 read_only: ReadOnlyGraphchain {
175 info: effective_info,
176 swapchain,
177 },
178 })
179 }
180
181 pub fn acquire_next_image(&mut self) -> Result<Option<SwapchainImage>, GraphchainError> {
183 if self.recreate_pending {
184 for frame in &mut self.frames {
185 if frame.fence.is_queued() {
186 frame.fence.wait()?.reset()?;
187 }
188 }
189
190 self.strategy
191 .retire_pending(&mut self.read_only.swapchain)?;
192
193 if self.read_only.swapchain.recreate_pending {
194 self.read_only.swapchain.recreate()?;
195 }
196
197 self.strategy
198 .reset(&self.read_only.swapchain.surface.device);
199 self.image_frames.clear();
200 self.recreate_pending = false;
201 }
202
203 self.frame_idx += 1;
204 self.frame_idx %= self.frames.len();
205
206 let frame = &mut self.frames[self.frame_idx];
207
208 if frame.fence.is_queued() {
209 frame.fence.wait()?.reset()?;
210 }
211
212 self.strategy.prepare_frame(
213 &frame.cmd_buf.device,
214 &mut self.read_only.swapchain,
215 self.frame_idx,
216 )?;
217
218 let swapchain_image = match self
219 .read_only
220 .swapchain
221 .acquire_next_image(frame.swapchain_acquired)
222 {
223 Ok(acquired) => acquired,
224 Err(SwapchainError::DeviceLost) => return Err(GraphchainError::DeviceLost),
225 Err(SwapchainError::Driver(err)) => return Err(GraphchainError::Driver(err)),
226 Err(
227 SwapchainError::FullScreenExclusiveModeLost
228 | SwapchainError::NotReady
229 | SwapchainError::Timeout,
230 ) => return Ok(None),
231 Err(SwapchainError::SurfaceLost) => {
232 return Err(GraphchainError::SurfaceLost);
233 }
234 };
235
236 if swapchain_image.suboptimal {
237 self.recreate_pending = true;
238 }
239
240 let swapchain_info = self.read_only.swapchain.info;
241 self.read_only.info.acquire_timeout = swapchain_info.acquire_timeout;
242 self.read_only.info.clipped = swapchain_info.clipped;
243 self.read_only.info.composite_alpha = swapchain_info.composite_alpha;
244 self.read_only.info.frame_capacity = self.requested_info.frame_capacity;
245 self.read_only.info.height = swapchain_info.height;
246 self.read_only.info.image_count = swapchain_info.image_count as usize;
247 self.read_only.info.present_mode = swapchain_info.present_mode;
248 self.read_only.info.surface = swapchain_info.surface;
249 self.read_only.info.width = swapchain_info.width;
250
251 self.read_only.info.frame_count = self
253 .read_only
254 .info
255 .frame_capacity
256 .max(self.read_only.info.image_count);
257
258 self.ensure_frame_count()?;
259
260 while swapchain_image.index >= self.image_frames.len() as u32 {
261 self.image_frames.push(0);
262 }
263
264 self.image_frames[swapchain_image.index as usize] = self.frame_idx;
265 self.strategy.acquire_image(
266 &self.read_only.swapchain.surface.device,
267 self.frame_idx,
268 swapchain_image.index,
269 )?;
270
271 Ok(Some(swapchain_image))
272 }
273
274 #[profiling::function]
276 pub fn present_image<P>(
277 &mut self,
278 pool: &mut P,
279 graph: Graph,
280 image_node: SwapchainImageNode,
281 queue_index: u32,
282 ) -> Result<(), GraphchainError>
283 where
284 P: Pool<CommandBufferInfo, CommandBuffer> + SubmissionPool,
285 {
286 trace!("present_image");
287
288 let submission = graph.finalize();
289 let (image_idx, image_sync_info) = {
290 let image = submission.resource(image_node);
291
292 (image.index as usize, image.sync_info())
293 };
294 let frame_idx = self.image_frames[image_idx];
295 let frame = &mut self.frames[frame_idx];
296 let rendered =
297 self.strategy
298 .rendered_semaphore(&frame.cmd_buf.device, frame_idx, image_idx as u32)?;
299
300 frame.cmd_buf.begin(
301 &vk::CommandBufferBeginInfo::default()
302 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
303 )?;
304
305 let wait_signal = image_sync_info.subresources.first().map(|_| {
306 let wait = SemaphoreSubmitInfo {
307 semaphore: frame.swapchain_acquired,
308 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
309 value: 0,
310 };
311 let signal = SemaphoreSubmitInfo {
312 semaphore: rendered,
313 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
314 value: 0,
315 };
316
317 (wait, signal)
318 });
319
320 let (mut recording, wait, signal, record_remaining) =
321 if let Some((wait, signal)) = wait_signal {
322 (
323 submission.record(pool, &frame.cmd_buf, image_node)?,
324 wait,
325 signal,
326 true,
327 )
328 } else {
329 warn!("uninitialized swapchain image");
330
331 let wait = SemaphoreSubmitInfo {
332 semaphore: frame.swapchain_acquired,
333 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
334 value: 0,
335 };
336 let signal = SemaphoreSubmitInfo {
337 semaphore: rendered,
338 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
339 value: 0,
340 };
341
342 (
343 submission.record(pool, &frame.cmd_buf, RecordSelection::All)?,
344 wait,
345 signal,
346 false,
347 )
348 };
349
350 let supports_synchronization2 = recording.cmd_buf.device.physical.vk_khr_synchronization2;
351
352 let image = recording.resource(image_node);
353 let image_sync_info = image.sync_info();
354 let image_handle = image.handle;
355
356 if log_enabled!(Level::Trace) {
357 for sync_info in &image_sync_info.subresources {
358 trace!(
359 "image {:?} {:?}->{:?}",
360 image,
361 sync_info.layout,
362 vk::ImageLayout::PRESENT_SRC_KHR,
363 );
364 }
365 }
366
367 if supports_synchronization2 {
368 for sync_info in &image_sync_info.subresources {
369 Device::cmd_pipeline_barrier2(
370 &recording.cmd_buf.device,
371 recording.cmd_buf.handle,
372 &vk::DependencyInfo::default().image_memory_barriers(slice::from_ref(
373 &vk::ImageMemoryBarrier2::default()
374 .src_stage_mask(vk::PipelineStageFlags2::from_raw(
375 sync_info.stage_mask.as_raw() as u64,
376 ))
377 .src_access_mask(vk::AccessFlags2::from_raw(
378 sync_info.access_mask.as_raw() as u64,
379 ))
380 .dst_stage_mask(vk::PipelineStageFlags2::NONE)
381 .dst_access_mask(vk::AccessFlags2::empty())
382 .old_layout(sync_info.layout.unwrap_or(vk::ImageLayout::UNDEFINED))
383 .new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
384 .src_queue_family_index(
385 sync_info
386 .queue_family_index
387 .unwrap_or(vk::QUEUE_FAMILY_IGNORED),
388 )
389 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
390 .image(image_handle)
391 .subresource_range(sync_info.range),
392 )),
393 );
394 }
395 } else {
396 for sync_info in &image_sync_info.subresources {
397 unsafe {
398 recording.cmd_buf.device.cmd_pipeline_barrier(
399 recording.cmd_buf.handle,
400 sync_info.stage_mask,
401 vk::PipelineStageFlags::BOTTOM_OF_PIPE,
402 vk::DependencyFlags::empty(),
403 &[],
404 &[],
405 slice::from_ref(
406 &vk::ImageMemoryBarrier::default()
407 .src_access_mask(sync_info.access_mask)
408 .dst_access_mask(vk::AccessFlags::empty())
409 .old_layout(sync_info.layout.unwrap_or(vk::ImageLayout::UNDEFINED))
410 .new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
411 .src_queue_family_index(
412 sync_info
413 .queue_family_index
414 .unwrap_or(vk::QUEUE_FAMILY_IGNORED),
415 )
416 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
417 .image(image_handle)
418 .subresource_range(sync_info.range),
419 ),
420 )
421 };
422 }
423 }
424
425 if record_remaining {
426 recording.record(RecordSelection::All)?;
427 }
428
429 recording.cmd_buf.end()?;
430
431 let image = unsafe { recording.resource(image_node).to_detached() };
432
433 let mut recorded = recording.finish()?;
434 let waits = slice::from_ref(&wait);
435 let signals = slice::from_ref(&signal);
436 let submit_info = QueueSubmitInfo::queue_submit(waits, signals);
437 recorded.queue_submit(&mut frame.fence, queue_index, submit_info)?;
438
439 let present_info = PresentInfo {
441 image,
442 swapchain: &mut self.read_only.swapchain,
443 wait_semaphores: slice::from_ref(&rendered),
444 };
445
446 let present =
447 Swapchain::queue_present(self.queue_family_index, queue_index, [present_info]);
448 let present_result = match &present {
449 Ok(results) => Some(results.first().copied().expect("missing present result")),
450 Err(err) => err.results.first().copied().flatten(),
451 };
452
453 if present_result.is_some_and(PresentResult::suboptimal) {
454 self.recreate_pending = true;
455 }
456
457 if let Some(PresentResult::NotQueued(err)) = present_result {
458 match err {
459 PresentError::DeviceLost | PresentError::SurfaceLost => {}
460 PresentError::FullScreenExclusiveModeLost | PresentError::OutOfDate => {
461 self.recreate_pending = true;
462 }
463 }
464 }
465
466 if present_result.is_some_and(PresentResult::queued) {
467 self.last_present_queue_index = queue_index;
468 self.strategy.present_image(frame_idx, image_idx as u32);
469 }
470
471 if let Err(err) = present {
472 match err.source {
473 PresentFailure::DeviceLost => return Err(GraphchainError::DeviceLost),
474 PresentFailure::SurfaceLost => return Err(GraphchainError::SurfaceLost),
475 PresentFailure::Driver(err) => return Err(GraphchainError::Driver(err)),
476 PresentFailure::FullScreenExclusiveModeLost | PresentFailure::OutOfDate => {
477 self.recreate_pending = true;
478 }
479 }
480 }
481
482 Ok(())
483 }
484
485 pub fn set_info(&mut self, info: impl Into<GraphchainInfo>) {
492 let info = info.into();
493 let swapchain_info = info.into_swapchain_info();
494 let recreate = self.requested_info.height != info.height
495 || self.requested_info.clipped != info.clipped
496 || self.requested_info.min_image_count != info.min_image_count
497 || self.requested_info.present_mode != info.present_mode
498 || self.requested_info.composite_alpha != info.composite_alpha
499 || self.requested_info.surface != info.surface
500 || self.requested_info.width != info.width;
501
502 self.read_only.swapchain.set_info(swapchain_info);
503 self.requested_info = info;
504 self.read_only.info.acquire_timeout = info.acquire_timeout;
505 self.read_only.info.clipped = info.clipped;
506 self.read_only.info.min_image_count = info.min_image_count;
507
508 if recreate {
509 self.recreate_pending = true;
510 }
511 }
512
513 fn ensure_frame_count(&mut self) -> Result<(), GraphchainError> {
514 if self.frames.len() >= self.read_only.info.frame_count {
515 return Ok(());
516 }
517
518 let device = self.read_only.swapchain.surface.device.clone();
519 let mut frames = take(&mut self.frames).into_vec();
520
521 for _ in frames.len()..self.read_only.info.frame_count {
522 let cmd_buf =
523 CommandBuffer::create(&device, CommandBufferInfo::new(self.queue_family_index))?;
524 let fence = Fence::create(&device, false)?;
525 let swapchain_acquired = Device::create_semaphore(&device)?;
526 Device::try_set_debug_utils_object_name(
527 &device,
528 swapchain_acquired,
529 "graphchain acquired semaphore",
530 );
531
532 frames.push(FrameSlot {
533 cmd_buf,
534 fence,
535 swapchain_acquired,
536 });
537 }
538
539 self.frames = frames.into_boxed_slice();
540
541 Ok(())
542 }
543}
544
545impl Debug for Graphchain {
546 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
547 f.write_str("Graphchain")
548 }
549}
550
551impl Drop for Graphchain {
552 fn drop(&mut self) {
553 if panicking() {
554 return;
555 }
556
557 let Some(frame) = self.frames.first() else {
558 return;
559 };
560 let device = frame.cmd_buf.device.clone();
561
562 for frame in &mut self.frames {
563 if frame.fence.is_queued() && frame.fence.wait().is_err() {
564 return;
565 }
566 }
567
568 if self
569 .strategy
570 .retire_pending(&mut self.read_only.swapchain)
571 .is_err()
572 {
573 return;
574 }
575
576 if Device::with_queue(
577 &device,
578 self.queue_family_index,
579 self.last_present_queue_index,
580 |queue| Device::queue_wait_idle(&device, queue),
581 )
582 .is_err()
583 {
584 return;
585 }
586
587 for frame in &mut self.frames {
588 unsafe {
589 frame
590 .cmd_buf
591 .device
592 .destroy_semaphore(frame.swapchain_acquired, None);
593 }
594 }
595
596 self.strategy.destroy(&device);
597 }
598}
599
600#[derive(Clone, Copy, Debug)]
602pub enum GraphchainError {
603 DeviceLost,
605
606 SurfaceLost,
608
609 Driver(DriverError),
611}
612
613impl Error for GraphchainError {
614 fn source(&self) -> Option<&(dyn Error + 'static)> {
615 match self {
616 Self::Driver(err) => Some(err),
617 Self::DeviceLost | Self::SurfaceLost => None,
618 }
619 }
620}
621
622impl From<()> for GraphchainError {
623 fn from(_: ()) -> Self {
624 Self::DeviceLost
625 }
626}
627
628impl From<DriverError> for GraphchainError {
629 fn from(err: DriverError) -> Self {
630 Self::Driver(err)
631 }
632}
633
634impl From<SwapchainError> for GraphchainError {
635 fn from(err: SwapchainError) -> Self {
636 match err {
637 SwapchainError::DeviceLost => Self::DeviceLost,
638 SwapchainError::Driver(err) => Self::Driver(err),
639 SwapchainError::FullScreenExclusiveModeLost
640 | SwapchainError::NotReady
641 | SwapchainError::Timeout => Self::Driver(DriverError::Unsupported),
642 SwapchainError::SurfaceLost => Self::SurfaceLost,
643 }
644 }
645}
646
647impl std::fmt::Display for GraphchainError {
648 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
649 match self {
650 Self::DeviceLost => f.write_str("device lost"),
651 Self::SurfaceLost => f.write_str("surface lost"),
652 Self::Driver(err) => std::fmt::Display::fmt(err, f),
653 }
654 }
655}
656
657#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
659#[builder(
660 build_fn(private, name = "fallible_build"),
661 derive(Clone, Copy, Debug),
662 pattern = "owned"
663)]
664pub struct GraphchainInfo {
665 #[builder(default = "u64::MAX")]
669 pub acquire_timeout: u64,
670
671 #[builder(default = vk::CompositeAlphaFlagsKHR::OPAQUE)]
673 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
674
675 #[builder(default = "true")]
677 pub clipped: bool,
678
679 #[builder(default = "4")]
684 pub frame_capacity: usize,
685
686 #[builder(default)]
688 pub height: u32,
689
690 #[builder(default = "2")]
692 pub min_image_count: u32,
693
694 #[builder(default = vk::PresentModeKHR::MAILBOX)]
696 pub present_mode: vk::PresentModeKHR,
697
698 #[builder(default)]
700 pub surface: vk::SurfaceFormatKHR,
701
702 #[builder(default)]
704 pub width: u32,
705}
706
707#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
709pub struct EffectiveGraphchainInfo {
710 pub acquire_timeout: u64,
712
713 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
715
716 pub clipped: bool,
718
719 frame_capacity: usize,
720
721 pub frame_count: usize,
723
724 pub height: u32,
726
727 pub image_count: usize,
729
730 min_image_count: u32,
731
732 pub present_mode: vk::PresentModeKHR,
734
735 pub surface: vk::SurfaceFormatKHR,
737
738 pub width: u32,
740}
741
742impl GraphchainInfo {
743 #[inline(always)]
745 pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> GraphchainInfo {
746 Self {
747 acquire_timeout: u64::MAX,
748 clipped: true,
749 composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
750 frame_capacity: 4,
751 height,
752 min_image_count: 2,
753 present_mode: vk::PresentModeKHR::MAILBOX,
754 surface,
755 width,
756 }
757 }
758
759 pub fn builder() -> GraphchainInfoBuilder {
761 Default::default()
762 }
763
764 pub fn into_builder(self) -> GraphchainInfoBuilder {
766 GraphchainInfoBuilder {
767 acquire_timeout: Some(self.acquire_timeout),
768 clipped: Some(self.clipped),
769 composite_alpha: Some(self.composite_alpha),
770 frame_capacity: Some(self.frame_capacity),
771 height: Some(self.height),
772 min_image_count: Some(self.min_image_count),
773 present_mode: Some(self.present_mode),
774 surface: Some(self.surface),
775 width: Some(self.width),
776 }
777 }
778
779 pub fn into_swapchain_info(self) -> SwapchainInfo {
781 self.into()
782 }
783}
784
785impl EffectiveGraphchainInfo {
786 pub fn builder() -> GraphchainInfoBuilder {
788 Default::default()
789 }
790
791 pub fn into_builder(self) -> GraphchainInfoBuilder {
793 self.into_requested_info().into_builder()
794 }
795
796 pub fn into_requested_info(self) -> GraphchainInfo {
798 let Self {
799 acquire_timeout,
800 clipped,
801 composite_alpha,
802 frame_capacity,
803 frame_count: _,
804 height,
805 image_count: _,
806 min_image_count,
807 present_mode,
808 surface,
809 width,
810 } = self;
811
812 GraphchainInfo {
813 acquire_timeout,
814 clipped,
815 composite_alpha,
816 frame_capacity,
817 height,
818 min_image_count,
819 present_mode,
820 surface,
821 width,
822 }
823 }
824}
825
826impl From<EffectiveGraphchainInfo> for GraphchainInfo {
827 fn from(info: EffectiveGraphchainInfo) -> Self {
828 info.into_requested_info()
829 }
830}
831
832impl From<GraphchainInfoBuilder> for GraphchainInfo {
833 fn from(info: GraphchainInfoBuilder) -> Self {
834 info.build()
835 }
836}
837
838impl From<GraphchainInfo> for SwapchainInfo {
839 fn from(val: GraphchainInfo) -> Self {
840 SwapchainInfoBuilder::default()
841 .acquire_timeout(val.acquire_timeout)
842 .clipped(val.clipped)
843 .composite_alpha(val.composite_alpha)
844 .height(val.height)
845 .min_image_count(val.min_image_count)
846 .present_mode(val.present_mode)
847 .surface(val.surface)
848 .width(val.width)
849 .build()
850 }
851}
852
853impl From<GraphchainInfoBuilder> for SwapchainInfo {
854 fn from(info: GraphchainInfoBuilder) -> Self {
855 info.build().into_swapchain_info()
856 }
857}
858
859impl GraphchainInfoBuilder {
860 #[inline(always)]
862 pub fn build(self) -> GraphchainInfo {
863 self.fallible_build().expect("all fields have defaults")
864 }
865}
866
867enum PresentRetirementStrategy {
868 PresentWait(PresentWait),
869 PerImageSemaphore(PerImageSemaphore),
870}
871
872impl PresentRetirementStrategy {
873 fn prepare_frame(
874 &mut self,
875 device: &Device,
876 swapchain: &mut Swapchain,
877 frame_idx: usize,
878 ) -> Result<(), GraphchainError> {
879 match self {
880 Self::PresentWait(strategy) => strategy.prepare_frame(device, swapchain, frame_idx),
881 Self::PerImageSemaphore(strategy) => {
882 strategy.prepare_frame(device, swapchain, frame_idx)
883 }
884 }
885 }
886
887 fn acquire_image(
888 &mut self,
889 device: &Device,
890 frame_idx: usize,
891 image_idx: u32,
892 ) -> Result<(), DriverError> {
893 match self {
894 Self::PresentWait(strategy) => strategy.acquire_image(device, frame_idx, image_idx),
895 Self::PerImageSemaphore(strategy) => {
896 strategy.acquire_image(device, frame_idx, image_idx)
897 }
898 }
899 }
900
901 fn rendered_semaphore(
902 &mut self,
903 device: &Device,
904 frame_idx: usize,
905 image_idx: u32,
906 ) -> Result<vk::Semaphore, DriverError> {
907 match self {
908 Self::PresentWait(strategy) => {
909 strategy.rendered_semaphore(device, frame_idx, image_idx)
910 }
911 Self::PerImageSemaphore(strategy) => {
912 strategy.rendered_semaphore(device, frame_idx, image_idx)
913 }
914 }
915 }
916
917 fn present_image(&mut self, frame_idx: usize, image_idx: u32) {
918 match self {
919 Self::PresentWait(strategy) => strategy.present_image(frame_idx, image_idx),
920 Self::PerImageSemaphore(strategy) => strategy.present_image(frame_idx, image_idx),
921 }
922 }
923
924 fn retire_pending(&mut self, swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
925 match self {
926 Self::PresentWait(strategy) => strategy.retire_pending(swapchain),
927 Self::PerImageSemaphore(strategy) => strategy.retire_pending(swapchain),
928 }
929 }
930
931 fn reset(&mut self, device: &Device) {
932 match self {
933 Self::PresentWait(strategy) => strategy.reset(device),
934 Self::PerImageSemaphore(strategy) => strategy.reset(device),
935 }
936 }
937
938 fn destroy(&mut self, device: &Device) {
939 match self {
940 Self::PresentWait(strategy) => strategy.destroy(device),
941 Self::PerImageSemaphore(strategy) => strategy.destroy(device),
942 }
943 }
944}
945
946#[derive(Default)]
947struct PresentWait {
948 retired: Vec<vk::Semaphore>,
949 rendered: Vec<Option<vk::Semaphore>>,
950 pending_images: Vec<Option<u32>>,
951}
952
953impl PresentWait {
954 fn prepare_frame(
955 &mut self,
956 _device: &Device,
957 swapchain: &mut Swapchain,
958 frame_idx: usize,
959 ) -> Result<(), GraphchainError> {
960 while self.pending_images.len() <= frame_idx {
961 self.pending_images.push(None);
962 }
963
964 if let Some(image_idx) = self.pending_images[frame_idx].take() {
965 unsafe {
966 match swapchain.wait_for_present_image(image_idx, u64::MAX) {
967 Ok(()) => {}
968 Err(SwapchainError::DeviceLost) => {
969 return Err(GraphchainError::DeviceLost);
970 }
971 Err(SwapchainError::Driver(err)) => {
972 return Err(GraphchainError::Driver(err));
973 }
974 Err(SwapchainError::FullScreenExclusiveModeLost) => {}
975 Err(SwapchainError::NotReady | SwapchainError::Timeout) => {
976 return Err(GraphchainError::Driver(DriverError::Unsupported));
977 }
978 Err(SwapchainError::SurfaceLost) => {
979 return Err(GraphchainError::SurfaceLost);
980 }
981 }
982 }
983 }
984
985 Ok(())
986 }
987
988 fn acquire_image(
989 &mut self,
990 device: &Device,
991 _frame_idx: usize,
992 image_idx: u32,
993 ) -> Result<(), DriverError> {
994 self.ensure_rendered(device, image_idx).map(|_| ())
995 }
996
997 fn rendered_semaphore(
998 &mut self,
999 device: &Device,
1000 _frame_idx: usize,
1001 image_idx: u32,
1002 ) -> Result<vk::Semaphore, DriverError> {
1003 self.ensure_rendered(device, image_idx)
1004 }
1005
1006 fn ensure_rendered(
1007 &mut self,
1008 device: &Device,
1009 image_idx: u32,
1010 ) -> Result<vk::Semaphore, DriverError> {
1011 while self.rendered.len() <= image_idx as usize {
1012 self.rendered.push(None);
1013 }
1014
1015 if self.rendered[image_idx as usize].is_none() {
1016 let semaphore = Device::create_semaphore(device)?;
1017 Device::try_set_debug_utils_object_name(
1018 device,
1019 semaphore,
1020 "graphchain present-wait per-image rendered semaphore",
1021 );
1022
1023 self.rendered[image_idx as usize] = Some(semaphore);
1024 }
1025
1026 Ok(self.rendered[image_idx as usize].expect("missing rendered semaphore"))
1027 }
1028
1029 fn present_image(&mut self, frame_idx: usize, image_idx: u32) {
1030 while self.pending_images.len() <= frame_idx {
1031 self.pending_images.push(None);
1032 }
1033
1034 self.pending_images[frame_idx] = Some(image_idx);
1035 }
1036
1037 fn retire_pending(&mut self, swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
1038 for image_idx in self.pending_images.drain(..).flatten() {
1039 unsafe {
1040 match swapchain.wait_for_present_image(image_idx, u64::MAX) {
1041 Ok(()) | Err(SwapchainError::FullScreenExclusiveModeLost) => {}
1042 Err(SwapchainError::DeviceLost) => return Err(GraphchainError::DeviceLost),
1043 Err(SwapchainError::Driver(err)) => return Err(GraphchainError::Driver(err)),
1044 Err(SwapchainError::NotReady | SwapchainError::Timeout) => {
1045 return Err(GraphchainError::Driver(DriverError::Unsupported));
1046 }
1047 Err(SwapchainError::SurfaceLost) => return Err(GraphchainError::SurfaceLost),
1048 }
1049 }
1050 }
1051
1052 Ok(())
1053 }
1054
1055 fn reset(&mut self, device: &Device) {
1056 self.destroy_retired(device);
1057 self.pending_images.clear();
1058 self.retired.extend(self.rendered.drain(..).flatten());
1059 }
1060
1061 fn destroy(&mut self, device: &Device) {
1062 self.destroy_retired(device);
1063
1064 for semaphore in self
1065 .rendered
1066 .drain(..)
1067 .flatten()
1068 .chain(self.retired.drain(..))
1069 {
1070 unsafe {
1071 device.destroy_semaphore(semaphore, None);
1072 }
1073 }
1074 }
1075
1076 fn destroy_retired(&mut self, device: &Device) {
1077 for semaphore in self.retired.drain(..) {
1078 unsafe {
1079 device.destroy_semaphore(semaphore, None);
1080 }
1081 }
1082 }
1083}
1084
1085#[derive(Default)]
1086struct PerImageSemaphore {
1087 retired: Vec<vk::Semaphore>,
1088 rendered: Vec<Option<vk::Semaphore>>,
1089}
1090
1091impl PerImageSemaphore {
1092 fn prepare_frame(
1093 &mut self,
1094 _device: &Device,
1095 _swapchain: &mut Swapchain,
1096 _frame_idx: usize,
1097 ) -> Result<(), GraphchainError> {
1098 Ok(())
1099 }
1100
1101 fn acquire_image(
1102 &mut self,
1103 device: &Device,
1104 _frame_idx: usize,
1105 image_idx: u32,
1106 ) -> Result<(), DriverError> {
1107 self.ensure_rendered(device, image_idx).map(|_| ())
1108 }
1109
1110 fn rendered_semaphore(
1111 &mut self,
1112 device: &Device,
1113 _frame_idx: usize,
1114 image_idx: u32,
1115 ) -> Result<vk::Semaphore, DriverError> {
1116 self.ensure_rendered(device, image_idx)
1117 }
1118
1119 fn ensure_rendered(
1120 &mut self,
1121 device: &Device,
1122 image_idx: u32,
1123 ) -> Result<vk::Semaphore, DriverError> {
1124 while self.rendered.len() <= image_idx as usize {
1125 self.rendered.push(None);
1126 }
1127
1128 if self.rendered[image_idx as usize].is_none() {
1129 let semaphore = Device::create_semaphore(device)?;
1130 Device::try_set_debug_utils_object_name(
1131 device,
1132 semaphore,
1133 "graphchain per-image rendered semaphore",
1134 );
1135
1136 self.rendered[image_idx as usize] = Some(semaphore);
1137 }
1138
1139 Ok(self.rendered[image_idx as usize].expect("missing rendered semaphore"))
1140 }
1141
1142 fn present_image(&mut self, _frame_idx: usize, _image_idx: u32) {}
1143
1144 fn retire_pending(&mut self, _swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
1145 Ok(())
1146 }
1147
1148 fn reset(&mut self, device: &Device) {
1149 self.destroy_retired(device);
1150 self.retired.extend(self.rendered.drain(..).flatten());
1151 }
1152
1153 fn destroy(&mut self, device: &Device) {
1154 self.destroy_retired(device);
1155
1156 for semaphore in self
1157 .rendered
1158 .drain(..)
1159 .flatten()
1160 .chain(self.retired.drain(..))
1161 {
1162 unsafe {
1163 device.destroy_semaphore(semaphore, None);
1164 }
1165 }
1166 }
1167
1168 fn destroy_retired(&mut self, device: &Device) {
1169 for semaphore in self.retired.drain(..) {
1170 unsafe {
1171 device.destroy_semaphore(semaphore, None);
1172 }
1173 }
1174 }
1175}
1176
1177struct FrameSlot {
1178 cmd_buf: CommandBuffer,
1179 fence: Fence,
1180 swapchain_acquired: vk::Semaphore,
1181}
1182
1183#[cfg(test)]
1184mod test {
1185 use super::*;
1186
1187 type Info = GraphchainInfo;
1188 type Builder = GraphchainInfoBuilder;
1189 type Effective = EffectiveGraphchainInfo;
1190
1191 #[test]
1192 fn graphchain_info_round_trips_through_builder() {
1193 let info = Info {
1194 acquire_timeout: 42,
1195 clipped: false,
1196 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1197 frame_capacity: 6,
1198 height: 123,
1199 min_image_count: 3,
1200 present_mode: vk::PresentModeKHR::FIFO,
1201 surface: vk::SurfaceFormatKHR::default()
1202 .format(vk::Format::B8G8R8A8_UNORM)
1203 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1204 width: 456,
1205 };
1206
1207 assert_eq!(info, info.into_builder().build());
1208 }
1209
1210 #[test]
1211 fn graphchain_info_builder_defaults() {
1212 assert_eq!(
1213 Builder::default().build(),
1214 Info {
1215 acquire_timeout: u64::MAX,
1216 clipped: true,
1217 composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
1218 frame_capacity: 4,
1219 height: 0,
1220 min_image_count: 2,
1221 present_mode: vk::PresentModeKHR::MAILBOX,
1222 surface: vk::SurfaceFormatKHR::default(),
1223 width: 0,
1224 }
1225 );
1226 }
1227
1228 #[test]
1229 fn effective_graphchain_info_into_requested_info() {
1230 let info = Effective {
1231 acquire_timeout: 42,
1232 clipped: false,
1233 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1234 frame_capacity: 6,
1235 frame_count: 8,
1236 height: 123,
1237 image_count: 7,
1238 min_image_count: 3,
1239 present_mode: vk::PresentModeKHR::FIFO,
1240 surface: vk::SurfaceFormatKHR::default()
1241 .format(vk::Format::B8G8R8A8_UNORM)
1242 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1243 width: 456,
1244 };
1245
1246 assert_eq!(
1247 info.into_requested_info(),
1248 Info {
1249 acquire_timeout: 42,
1250 clipped: false,
1251 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1252 frame_capacity: 6,
1253 height: 123,
1254 min_image_count: 3,
1255 present_mode: vk::PresentModeKHR::FIFO,
1256 surface: vk::SurfaceFormatKHR::default()
1257 .format(vk::Format::B8G8R8A8_UNORM)
1258 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1259 width: 456,
1260 }
1261 );
1262 }
1263
1264 }