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, TimestampQueryPool},
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<SubmissionQueryPool, 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 let submission_queries = SubmissionQueryPool {
439 timestamps: frame.fence.timestamps.clone(),
440 };
441
442 let present_info = PresentInfo {
444 image,
445 swapchain: &mut self.read_only.swapchain,
446 wait_semaphores: slice::from_ref(&rendered),
447 };
448
449 let present =
450 Swapchain::queue_present(self.queue_family_index, queue_index, [present_info]);
451 let present_result = match &present {
452 Ok(results) => Some(results.first().copied().expect("missing present result")),
453 Err(err) => err.results.first().copied().flatten(),
454 };
455
456 if present_result.is_some_and(PresentResult::suboptimal) {
457 self.recreate_pending = true;
458 }
459
460 if let Some(PresentResult::NotQueued(err)) = present_result {
461 match err {
462 PresentError::DeviceLost | PresentError::SurfaceLost => {}
463 PresentError::FullScreenExclusiveModeLost | PresentError::OutOfDate => {
464 self.recreate_pending = true;
465 }
466 }
467 }
468
469 if present_result.is_some_and(PresentResult::queued) {
470 self.last_present_queue_index = queue_index;
471 self.strategy.present_image(frame_idx, image_idx as u32);
472 }
473
474 if let Err(err) = present {
475 match err.source {
476 PresentFailure::DeviceLost => return Err(GraphchainError::DeviceLost),
477 PresentFailure::SurfaceLost => return Err(GraphchainError::SurfaceLost),
478 PresentFailure::Driver(err) => return Err(GraphchainError::Driver(err)),
479 PresentFailure::FullScreenExclusiveModeLost | PresentFailure::OutOfDate => {
480 self.recreate_pending = true;
481 }
482 }
483 }
484
485 Ok(submission_queries)
486 }
487
488 pub fn set_info(&mut self, info: impl Into<GraphchainInfo>) {
495 let info = info.into();
496 let swapchain_info = info.into_swapchain_info();
497 let recreate = self.requested_info.height != info.height
498 || self.requested_info.clipped != info.clipped
499 || self.requested_info.min_image_count != info.min_image_count
500 || self.requested_info.present_mode != info.present_mode
501 || self.requested_info.composite_alpha != info.composite_alpha
502 || self.requested_info.surface != info.surface
503 || self.requested_info.width != info.width;
504
505 self.read_only.swapchain.set_info(swapchain_info);
506 self.requested_info = info;
507 self.read_only.info.acquire_timeout = info.acquire_timeout;
508 self.read_only.info.clipped = info.clipped;
509 self.read_only.info.min_image_count = info.min_image_count;
510
511 if recreate {
512 self.recreate_pending = true;
513 }
514 }
515
516 fn ensure_frame_count(&mut self) -> Result<(), GraphchainError> {
517 if self.frames.len() >= self.read_only.info.frame_count {
518 return Ok(());
519 }
520
521 let device = self.read_only.swapchain.surface.device.clone();
522 let mut frames = take(&mut self.frames).into_vec();
523
524 for _ in frames.len()..self.read_only.info.frame_count {
525 let cmd_buf =
526 CommandBuffer::create(&device, CommandBufferInfo::new(self.queue_family_index))?;
527 let fence = Fence::create(&device, false)?;
528 let swapchain_acquired = Device::create_semaphore(&device)?;
529 Device::try_set_debug_utils_object_name(
530 &device,
531 swapchain_acquired,
532 "graphchain acquired semaphore",
533 );
534
535 frames.push(FrameSlot {
536 cmd_buf,
537 fence,
538 swapchain_acquired,
539 });
540 }
541
542 self.frames = frames.into_boxed_slice();
543
544 Ok(())
545 }
546}
547
548impl Debug for Graphchain {
549 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
550 f.write_str("Graphchain")
551 }
552}
553
554impl Drop for Graphchain {
555 fn drop(&mut self) {
556 if panicking() {
557 return;
558 }
559
560 let Some(frame) = self.frames.first() else {
561 return;
562 };
563 let device = frame.cmd_buf.device.clone();
564
565 for frame in &mut self.frames {
566 if frame.fence.is_queued() && frame.fence.wait().is_err() {
567 return;
568 }
569 }
570
571 if self
572 .strategy
573 .retire_pending(&mut self.read_only.swapchain)
574 .is_err()
575 {
576 return;
577 }
578
579 if Device::with_queue(
580 &device,
581 self.queue_family_index,
582 self.last_present_queue_index,
583 |queue| Device::queue_wait_idle(&device, queue),
584 )
585 .is_err()
586 {
587 return;
588 }
589
590 for frame in &mut self.frames {
591 unsafe {
592 frame
593 .cmd_buf
594 .device
595 .destroy_semaphore(frame.swapchain_acquired, None);
596 }
597 }
598
599 self.strategy.destroy(&device);
600 }
601}
602
603#[derive(Clone, Copy, Debug)]
605pub enum GraphchainError {
606 DeviceLost,
608
609 SurfaceLost,
611
612 Driver(DriverError),
614}
615
616impl Error for GraphchainError {
617 fn source(&self) -> Option<&(dyn Error + 'static)> {
618 match self {
619 Self::Driver(err) => Some(err),
620 Self::DeviceLost | Self::SurfaceLost => None,
621 }
622 }
623}
624
625impl From<()> for GraphchainError {
626 fn from(_: ()) -> Self {
627 Self::DeviceLost
628 }
629}
630
631impl From<DriverError> for GraphchainError {
632 fn from(err: DriverError) -> Self {
633 Self::Driver(err)
634 }
635}
636
637impl From<SwapchainError> for GraphchainError {
638 fn from(err: SwapchainError) -> Self {
639 match err {
640 SwapchainError::DeviceLost => Self::DeviceLost,
641 SwapchainError::Driver(err) => Self::Driver(err),
642 SwapchainError::FullScreenExclusiveModeLost
643 | SwapchainError::NotReady
644 | SwapchainError::Timeout => Self::Driver(DriverError::Unsupported),
645 SwapchainError::SurfaceLost => Self::SurfaceLost,
646 }
647 }
648}
649
650impl std::fmt::Display for GraphchainError {
651 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
652 match self {
653 Self::DeviceLost => f.write_str("device lost"),
654 Self::SurfaceLost => f.write_str("surface lost"),
655 Self::Driver(err) => std::fmt::Display::fmt(err, f),
656 }
657 }
658}
659
660#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
662#[builder(
663 build_fn(private, name = "fallible_build"),
664 derive(Clone, Copy, Debug),
665 pattern = "owned"
666)]
667pub struct GraphchainInfo {
668 #[builder(default = "u64::MAX")]
672 pub acquire_timeout: u64,
673
674 #[builder(default = vk::CompositeAlphaFlagsKHR::OPAQUE)]
676 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
677
678 #[builder(default = "true")]
680 pub clipped: bool,
681
682 #[builder(default = "4")]
687 pub frame_capacity: usize,
688
689 #[builder(default)]
691 pub height: u32,
692
693 #[builder(default = "2")]
695 pub min_image_count: u32,
696
697 #[builder(default = vk::PresentModeKHR::MAILBOX)]
699 pub present_mode: vk::PresentModeKHR,
700
701 #[builder(default)]
703 pub surface: vk::SurfaceFormatKHR,
704
705 #[builder(default)]
707 pub width: u32,
708}
709
710#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
712pub struct EffectiveGraphchainInfo {
713 pub acquire_timeout: u64,
715
716 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
718
719 pub clipped: bool,
721
722 frame_capacity: usize,
723
724 pub frame_count: usize,
726
727 pub height: u32,
729
730 pub image_count: usize,
732
733 min_image_count: u32,
734
735 pub present_mode: vk::PresentModeKHR,
737
738 pub surface: vk::SurfaceFormatKHR,
740
741 pub width: u32,
743}
744
745impl GraphchainInfo {
746 #[inline(always)]
748 pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> GraphchainInfo {
749 Self {
750 acquire_timeout: u64::MAX,
751 clipped: true,
752 composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
753 frame_capacity: 4,
754 height,
755 min_image_count: 2,
756 present_mode: vk::PresentModeKHR::MAILBOX,
757 surface,
758 width,
759 }
760 }
761
762 pub fn builder() -> GraphchainInfoBuilder {
764 Default::default()
765 }
766
767 pub fn into_builder(self) -> GraphchainInfoBuilder {
769 GraphchainInfoBuilder {
770 acquire_timeout: Some(self.acquire_timeout),
771 clipped: Some(self.clipped),
772 composite_alpha: Some(self.composite_alpha),
773 frame_capacity: Some(self.frame_capacity),
774 height: Some(self.height),
775 min_image_count: Some(self.min_image_count),
776 present_mode: Some(self.present_mode),
777 surface: Some(self.surface),
778 width: Some(self.width),
779 }
780 }
781
782 pub fn into_swapchain_info(self) -> SwapchainInfo {
784 self.into()
785 }
786}
787
788impl EffectiveGraphchainInfo {
789 pub fn builder() -> GraphchainInfoBuilder {
791 Default::default()
792 }
793
794 pub fn into_builder(self) -> GraphchainInfoBuilder {
796 self.into_requested_info().into_builder()
797 }
798
799 pub fn into_requested_info(self) -> GraphchainInfo {
801 let Self {
802 acquire_timeout,
803 clipped,
804 composite_alpha,
805 frame_capacity,
806 frame_count: _,
807 height,
808 image_count: _,
809 min_image_count,
810 present_mode,
811 surface,
812 width,
813 } = self;
814
815 GraphchainInfo {
816 acquire_timeout,
817 clipped,
818 composite_alpha,
819 frame_capacity,
820 height,
821 min_image_count,
822 present_mode,
823 surface,
824 width,
825 }
826 }
827}
828
829impl From<EffectiveGraphchainInfo> for GraphchainInfo {
830 fn from(info: EffectiveGraphchainInfo) -> Self {
831 info.into_requested_info()
832 }
833}
834
835impl From<GraphchainInfoBuilder> for GraphchainInfo {
836 fn from(info: GraphchainInfoBuilder) -> Self {
837 info.build()
838 }
839}
840
841impl From<GraphchainInfo> for SwapchainInfo {
842 fn from(val: GraphchainInfo) -> Self {
843 SwapchainInfoBuilder::default()
844 .acquire_timeout(val.acquire_timeout)
845 .clipped(val.clipped)
846 .composite_alpha(val.composite_alpha)
847 .height(val.height)
848 .min_image_count(val.min_image_count)
849 .present_mode(val.present_mode)
850 .surface(val.surface)
851 .width(val.width)
852 .build()
853 }
854}
855
856impl From<GraphchainInfoBuilder> for SwapchainInfo {
857 fn from(info: GraphchainInfoBuilder) -> Self {
858 info.build().into_swapchain_info()
859 }
860}
861
862impl GraphchainInfoBuilder {
863 #[inline(always)]
865 pub fn build(self) -> GraphchainInfo {
866 self.fallible_build().expect("all fields have defaults")
867 }
868}
869
870enum PresentRetirementStrategy {
871 PresentWait(PresentWait),
872 PerImageSemaphore(PerImageSemaphore),
873}
874
875impl PresentRetirementStrategy {
876 fn prepare_frame(
877 &mut self,
878 device: &Device,
879 swapchain: &mut Swapchain,
880 frame_idx: usize,
881 ) -> Result<(), GraphchainError> {
882 match self {
883 Self::PresentWait(strategy) => strategy.prepare_frame(device, swapchain, frame_idx),
884 Self::PerImageSemaphore(strategy) => {
885 strategy.prepare_frame(device, swapchain, frame_idx)
886 }
887 }
888 }
889
890 fn acquire_image(
891 &mut self,
892 device: &Device,
893 frame_idx: usize,
894 image_idx: u32,
895 ) -> Result<(), DriverError> {
896 match self {
897 Self::PresentWait(strategy) => strategy.acquire_image(device, frame_idx, image_idx),
898 Self::PerImageSemaphore(strategy) => {
899 strategy.acquire_image(device, frame_idx, image_idx)
900 }
901 }
902 }
903
904 fn rendered_semaphore(
905 &mut self,
906 device: &Device,
907 frame_idx: usize,
908 image_idx: u32,
909 ) -> Result<vk::Semaphore, DriverError> {
910 match self {
911 Self::PresentWait(strategy) => {
912 strategy.rendered_semaphore(device, frame_idx, image_idx)
913 }
914 Self::PerImageSemaphore(strategy) => {
915 strategy.rendered_semaphore(device, frame_idx, image_idx)
916 }
917 }
918 }
919
920 fn present_image(&mut self, frame_idx: usize, image_idx: u32) {
921 match self {
922 Self::PresentWait(strategy) => strategy.present_image(frame_idx, image_idx),
923 Self::PerImageSemaphore(strategy) => strategy.present_image(frame_idx, image_idx),
924 }
925 }
926
927 fn retire_pending(&mut self, swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
928 match self {
929 Self::PresentWait(strategy) => strategy.retire_pending(swapchain),
930 Self::PerImageSemaphore(strategy) => strategy.retire_pending(swapchain),
931 }
932 }
933
934 fn reset(&mut self, device: &Device) {
935 match self {
936 Self::PresentWait(strategy) => strategy.reset(device),
937 Self::PerImageSemaphore(strategy) => strategy.reset(device),
938 }
939 }
940
941 fn destroy(&mut self, device: &Device) {
942 match self {
943 Self::PresentWait(strategy) => strategy.destroy(device),
944 Self::PerImageSemaphore(strategy) => strategy.destroy(device),
945 }
946 }
947}
948
949#[derive(Default)]
950struct PresentWait {
951 retired: Vec<vk::Semaphore>,
952 rendered: Vec<Option<vk::Semaphore>>,
953 pending_images: Vec<Option<u32>>,
954}
955
956impl PresentWait {
957 fn prepare_frame(
958 &mut self,
959 _device: &Device,
960 swapchain: &mut Swapchain,
961 frame_idx: usize,
962 ) -> Result<(), GraphchainError> {
963 while self.pending_images.len() <= frame_idx {
964 self.pending_images.push(None);
965 }
966
967 if let Some(image_idx) = self.pending_images[frame_idx].take() {
968 unsafe {
969 match swapchain.wait_for_present_image(image_idx, u64::MAX) {
970 Ok(()) => {}
971 Err(SwapchainError::DeviceLost) => {
972 return Err(GraphchainError::DeviceLost);
973 }
974 Err(SwapchainError::Driver(err)) => {
975 return Err(GraphchainError::Driver(err));
976 }
977 Err(SwapchainError::FullScreenExclusiveModeLost) => {}
978 Err(SwapchainError::NotReady | SwapchainError::Timeout) => {
979 return Err(GraphchainError::Driver(DriverError::Unsupported));
980 }
981 Err(SwapchainError::SurfaceLost) => {
982 return Err(GraphchainError::SurfaceLost);
983 }
984 }
985 }
986 }
987
988 Ok(())
989 }
990
991 fn acquire_image(
992 &mut self,
993 device: &Device,
994 _frame_idx: usize,
995 image_idx: u32,
996 ) -> Result<(), DriverError> {
997 self.ensure_rendered(device, image_idx).map(|_| ())
998 }
999
1000 fn rendered_semaphore(
1001 &mut self,
1002 device: &Device,
1003 _frame_idx: usize,
1004 image_idx: u32,
1005 ) -> Result<vk::Semaphore, DriverError> {
1006 self.ensure_rendered(device, image_idx)
1007 }
1008
1009 fn ensure_rendered(
1010 &mut self,
1011 device: &Device,
1012 image_idx: u32,
1013 ) -> Result<vk::Semaphore, DriverError> {
1014 while self.rendered.len() <= image_idx as usize {
1015 self.rendered.push(None);
1016 }
1017
1018 if self.rendered[image_idx as usize].is_none() {
1019 let semaphore = Device::create_semaphore(device)?;
1020 Device::try_set_debug_utils_object_name(
1021 device,
1022 semaphore,
1023 "graphchain present-wait per-image rendered semaphore",
1024 );
1025
1026 self.rendered[image_idx as usize] = Some(semaphore);
1027 }
1028
1029 Ok(self.rendered[image_idx as usize].expect("missing rendered semaphore"))
1030 }
1031
1032 fn present_image(&mut self, frame_idx: usize, image_idx: u32) {
1033 while self.pending_images.len() <= frame_idx {
1034 self.pending_images.push(None);
1035 }
1036
1037 self.pending_images[frame_idx] = Some(image_idx);
1038 }
1039
1040 fn retire_pending(&mut self, swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
1041 for image_idx in self.pending_images.drain(..).flatten() {
1042 unsafe {
1043 match swapchain.wait_for_present_image(image_idx, u64::MAX) {
1044 Ok(()) | Err(SwapchainError::FullScreenExclusiveModeLost) => {}
1045 Err(SwapchainError::DeviceLost) => return Err(GraphchainError::DeviceLost),
1046 Err(SwapchainError::Driver(err)) => return Err(GraphchainError::Driver(err)),
1047 Err(SwapchainError::NotReady | SwapchainError::Timeout) => {
1048 return Err(GraphchainError::Driver(DriverError::Unsupported));
1049 }
1050 Err(SwapchainError::SurfaceLost) => return Err(GraphchainError::SurfaceLost),
1051 }
1052 }
1053 }
1054
1055 Ok(())
1056 }
1057
1058 fn reset(&mut self, device: &Device) {
1059 self.destroy_retired(device);
1060 self.pending_images.clear();
1061 self.retired.extend(self.rendered.drain(..).flatten());
1062 }
1063
1064 fn destroy(&mut self, device: &Device) {
1065 self.destroy_retired(device);
1066
1067 for semaphore in self
1068 .rendered
1069 .drain(..)
1070 .flatten()
1071 .chain(self.retired.drain(..))
1072 {
1073 unsafe {
1074 device.destroy_semaphore(semaphore, None);
1075 }
1076 }
1077 }
1078
1079 fn destroy_retired(&mut self, device: &Device) {
1080 for semaphore in self.retired.drain(..) {
1081 unsafe {
1082 device.destroy_semaphore(semaphore, None);
1083 }
1084 }
1085 }
1086}
1087
1088#[derive(Default)]
1089struct PerImageSemaphore {
1090 retired: Vec<vk::Semaphore>,
1091 rendered: Vec<Option<vk::Semaphore>>,
1092}
1093
1094impl PerImageSemaphore {
1095 fn prepare_frame(
1096 &mut self,
1097 _device: &Device,
1098 _swapchain: &mut Swapchain,
1099 _frame_idx: usize,
1100 ) -> Result<(), GraphchainError> {
1101 Ok(())
1102 }
1103
1104 fn acquire_image(
1105 &mut self,
1106 device: &Device,
1107 _frame_idx: usize,
1108 image_idx: u32,
1109 ) -> Result<(), DriverError> {
1110 self.ensure_rendered(device, image_idx).map(|_| ())
1111 }
1112
1113 fn rendered_semaphore(
1114 &mut self,
1115 device: &Device,
1116 _frame_idx: usize,
1117 image_idx: u32,
1118 ) -> Result<vk::Semaphore, DriverError> {
1119 self.ensure_rendered(device, image_idx)
1120 }
1121
1122 fn ensure_rendered(
1123 &mut self,
1124 device: &Device,
1125 image_idx: u32,
1126 ) -> Result<vk::Semaphore, DriverError> {
1127 while self.rendered.len() <= image_idx as usize {
1128 self.rendered.push(None);
1129 }
1130
1131 if self.rendered[image_idx as usize].is_none() {
1132 let semaphore = Device::create_semaphore(device)?;
1133 Device::try_set_debug_utils_object_name(
1134 device,
1135 semaphore,
1136 "graphchain per-image rendered semaphore",
1137 );
1138
1139 self.rendered[image_idx as usize] = Some(semaphore);
1140 }
1141
1142 Ok(self.rendered[image_idx as usize].expect("missing rendered semaphore"))
1143 }
1144
1145 fn present_image(&mut self, _frame_idx: usize, _image_idx: u32) {}
1146
1147 fn retire_pending(&mut self, _swapchain: &mut Swapchain) -> Result<(), GraphchainError> {
1148 Ok(())
1149 }
1150
1151 fn reset(&mut self, device: &Device) {
1152 self.destroy_retired(device);
1153 self.retired.extend(self.rendered.drain(..).flatten());
1154 }
1155
1156 fn destroy(&mut self, device: &Device) {
1157 self.destroy_retired(device);
1158
1159 for semaphore in self
1160 .rendered
1161 .drain(..)
1162 .flatten()
1163 .chain(self.retired.drain(..))
1164 {
1165 unsafe {
1166 device.destroy_semaphore(semaphore, None);
1167 }
1168 }
1169 }
1170
1171 fn destroy_retired(&mut self, device: &Device) {
1172 for semaphore in self.retired.drain(..) {
1173 unsafe {
1174 device.destroy_semaphore(semaphore, None);
1175 }
1176 }
1177 }
1178}
1179
1180struct FrameSlot {
1181 cmd_buf: CommandBuffer,
1182 fence: Fence,
1183 swapchain_acquired: vk::Semaphore,
1184}
1185
1186#[derive(Clone, Debug)]
1194#[non_exhaustive]
1195pub struct SubmissionQueryPool {
1196 pub timestamps: TimestampQueryPool,
1198}
1199
1200#[cfg(test)]
1201mod test {
1202 use super::*;
1203
1204 type Info = GraphchainInfo;
1205 type Builder = GraphchainInfoBuilder;
1206 type Effective = EffectiveGraphchainInfo;
1207
1208 #[test]
1209 fn graphchain_info_round_trips_through_builder() {
1210 let info = Info {
1211 acquire_timeout: 42,
1212 clipped: false,
1213 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1214 frame_capacity: 6,
1215 height: 123,
1216 min_image_count: 3,
1217 present_mode: vk::PresentModeKHR::FIFO,
1218 surface: vk::SurfaceFormatKHR::default()
1219 .format(vk::Format::B8G8R8A8_UNORM)
1220 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1221 width: 456,
1222 };
1223
1224 assert_eq!(info, info.into_builder().build());
1225 }
1226
1227 #[test]
1228 fn graphchain_info_builder_defaults() {
1229 assert_eq!(
1230 Builder::default().build(),
1231 Info {
1232 acquire_timeout: u64::MAX,
1233 clipped: true,
1234 composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
1235 frame_capacity: 4,
1236 height: 0,
1237 min_image_count: 2,
1238 present_mode: vk::PresentModeKHR::MAILBOX,
1239 surface: vk::SurfaceFormatKHR::default(),
1240 width: 0,
1241 }
1242 );
1243 }
1244
1245 #[test]
1246 fn effective_graphchain_info_into_requested_info() {
1247 let info = Effective {
1248 acquire_timeout: 42,
1249 clipped: false,
1250 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1251 frame_capacity: 6,
1252 frame_count: 8,
1253 height: 123,
1254 image_count: 7,
1255 min_image_count: 3,
1256 present_mode: vk::PresentModeKHR::FIFO,
1257 surface: vk::SurfaceFormatKHR::default()
1258 .format(vk::Format::B8G8R8A8_UNORM)
1259 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1260 width: 456,
1261 };
1262
1263 assert_eq!(
1264 info.into_requested_info(),
1265 Info {
1266 acquire_timeout: 42,
1267 clipped: false,
1268 composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
1269 frame_capacity: 6,
1270 height: 123,
1271 min_image_count: 3,
1272 present_mode: vk::PresentModeKHR::FIFO,
1273 surface: vk::SurfaceFormatKHR::default()
1274 .format(vk::Format::B8G8R8A8_UNORM)
1275 .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
1276 width: 456,
1277 }
1278 );
1279 }
1280
1281 }