1use {
4 super::{
5 DriverError, Surface,
6 device::Device,
7 image::{Image, ImageInfo},
8 },
9 ash::vk,
10 derive_builder::{Builder, UninitializedFieldError},
11 log::{debug, info, trace, warn},
12 std::{mem::replace, ops::Deref, slice, sync::Arc, thread::panicking},
13};
14
15#[derive(Debug)]
19pub struct Swapchain {
20 device: Arc<Device>,
21 images: Box<[SwapchainImage]>,
22 info: SwapchainInfo,
23 old_swapchain: vk::SwapchainKHR,
24 suboptimal: bool,
25 surface: Surface,
26 swapchain: vk::SwapchainKHR,
27}
28
29impl Swapchain {
30 #[profiling::function]
33 pub fn new(
34 device: &Arc<Device>,
35 surface: Surface,
36 info: impl Into<SwapchainInfo>,
37 ) -> Result<Self, DriverError> {
38 let device = Arc::clone(device);
39 let info = info.into();
40
41 Ok(Swapchain {
42 device,
43 images: Default::default(),
44 info,
45 old_swapchain: vk::SwapchainKHR::null(),
46 suboptimal: true,
47 surface,
48 swapchain: vk::SwapchainKHR::null(),
49 })
50 }
51
52 #[profiling::function]
55 pub fn acquire_next_image(
56 &mut self,
57 acquired: vk::Semaphore,
58 ) -> Result<SwapchainImage, SwapchainError> {
59 for _ in 0..2 {
60 if self.suboptimal {
61 self.recreate_swapchain().map_err(|err| {
62 if matches!(err, DriverError::Unsupported) {
63 SwapchainError::Suboptimal
64 } else {
65 SwapchainError::SurfaceLost
66 }
67 })?;
68 }
69
70 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
71
72 let image_idx = unsafe {
73 swapchain_ext.acquire_next_image(
74 self.swapchain,
75 u64::MAX,
76 acquired,
77 vk::Fence::null(),
78 )
79 }
80 .map(|(idx, suboptimal)| {
81 if suboptimal {
82 debug!("acquired image is suboptimal");
83 }
84
85 self.suboptimal = suboptimal;
86
87 idx
88 });
89
90 match image_idx {
91 Ok(image_idx) => {
92 let image_idx = image_idx as usize;
93
94 assert!(image_idx < self.images.len());
95
96 let image = unsafe { self.images.get_unchecked(image_idx) };
97 let image = SwapchainImage::clone_swapchain(image);
98
99 return Ok(replace(
100 unsafe { self.images.get_unchecked_mut(image_idx) },
101 image,
102 ));
103 }
104 Err(err)
105 if err == vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
106 || err == vk::Result::ERROR_OUT_OF_DATE_KHR
107 || err == vk::Result::NOT_READY
108 || err == vk::Result::TIMEOUT =>
109 {
110 warn!("unable to acquire image: {err}");
111
112 self.suboptimal = true;
113
114 continue;
117 }
118 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
119 warn!("unable to acquire image: {err}");
120
121 self.suboptimal = true;
122
123 return Err(SwapchainError::DeviceLost);
124 }
125 Err(err) if err == vk::Result::ERROR_SURFACE_LOST_KHR => {
126 warn!("unable to acquire image: {err}");
127
128 self.suboptimal = true;
129
130 return Err(SwapchainError::SurfaceLost);
131 }
132 Err(err) => {
133 warn!("unable to acquire image: {err}");
140
141 return Err(SwapchainError::SurfaceLost);
142 }
143 }
144 }
145
146 Err(SwapchainError::Suboptimal)
147 }
148
149 fn clamp_desired_image_count(
150 desired_image_count: u32,
151 surface_capabilities: vk::SurfaceCapabilitiesKHR,
152 ) -> u32 {
153 let mut desired_image_count = desired_image_count.max(surface_capabilities.min_image_count);
154
155 if surface_capabilities.max_image_count != 0 {
156 desired_image_count = desired_image_count.min(surface_capabilities.max_image_count);
157 }
158
159 desired_image_count.min(u8::MAX as u32)
160 }
161
162 #[profiling::function]
163 fn destroy_swapchain(device: &Device, swapchain: &mut vk::SwapchainKHR) {
164 if *swapchain != vk::SwapchainKHR::null() {
165 #[cfg(target_os = "macos")]
168 if let Err(err) = unsafe { device.device_wait_idle() } {
169 warn!("device_wait_idle() failed: {err}");
170 }
171
172 let swapchain_ext = Device::expect_swapchain_ext(device);
173
174 unsafe {
175 swapchain_ext.destroy_swapchain(*swapchain, None);
176 }
177
178 *swapchain = vk::SwapchainKHR::null();
179 }
180 }
181
182 pub fn info(&self) -> SwapchainInfo {
184 self.info.clone()
185 }
186
187 #[profiling::function]
190 pub fn present_image(
191 &mut self,
192 image: SwapchainImage,
193 wait_semaphores: &[vk::Semaphore],
194 queue_family_index: u32,
195 queue_index: u32,
196 ) {
197 let queue_family_index = queue_family_index as usize;
198 let queue_index = queue_index as usize;
199
200 debug_assert!(
201 queue_family_index < self.device.physical_device.queue_families.len(),
202 "Queue family index must be within the range of the available queues created by the device."
203 );
204 debug_assert!(
205 queue_index
206 < self.device.physical_device.queue_families[queue_family_index].queue_count
207 as usize,
208 "Queue index must be within the range of the available queues created by the device."
209 );
210
211 let present_info = vk::PresentInfoKHR::default()
212 .wait_semaphores(wait_semaphores)
213 .swapchains(slice::from_ref(&self.swapchain))
214 .image_indices(slice::from_ref(&image.image_idx));
215
216 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
217
218 unsafe {
219 match swapchain_ext.queue_present(
220 self.device.queues[queue_family_index][queue_index],
221 &present_info,
222 ) {
223 Ok(_) => {
224 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
225 }
226 Err(err)
227 if err == vk::Result::ERROR_DEVICE_LOST
228 || err == vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
229 || err == vk::Result::ERROR_OUT_OF_DATE_KHR
230 || err == vk::Result::ERROR_SURFACE_LOST_KHR
231 || err == vk::Result::SUBOPTIMAL_KHR =>
232 {
233 self.suboptimal = true;
235 }
236 Err(err) => {
237 warn!("{err}");
241 }
242 }
243 }
244
245 let image_idx = image.image_idx as usize;
246 self.images[image_idx] = image;
247 }
248
249 #[profiling::function]
250 fn recreate_swapchain(&mut self) -> Result<(), DriverError> {
251 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
252
253 let surface_caps = Surface::capabilities(&self.surface)?;
254 let present_modes = Surface::present_modes(&self.surface)?;
255
256 let desired_image_count =
257 Self::clamp_desired_image_count(self.info.desired_image_count, surface_caps);
258
259 let image_usage = self.supported_surface_usage(surface_caps.supported_usage_flags)?;
260
261 let (surface_width, surface_height) = match surface_caps.current_extent.width {
262 std::u32::MAX => (
263 self.info.width.clamp(
265 surface_caps.min_image_extent.width,
266 surface_caps.max_image_extent.width,
267 ),
268 self.info.height.clamp(
269 surface_caps.min_image_extent.height,
270 surface_caps.max_image_extent.height,
271 ),
272 ),
273 _ => (
274 surface_caps.current_extent.width,
275 surface_caps.current_extent.height,
276 ),
277 };
278
279 if surface_width * surface_height == 0 {
280 return Err(DriverError::Unsupported);
281 }
282
283 let present_mode = self
284 .info
285 .present_modes
286 .iter()
287 .copied()
288 .find(|mode| present_modes.contains(mode))
289 .unwrap_or(vk::PresentModeKHR::FIFO);
290
291 let pre_transform = if surface_caps
292 .supported_transforms
293 .contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
294 {
295 vk::SurfaceTransformFlagsKHR::IDENTITY
296 } else {
297 surface_caps.current_transform
298 };
299
300 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
301 let swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
302 .surface(*self.surface)
303 .min_image_count(desired_image_count)
304 .image_color_space(self.info.surface.color_space)
305 .image_format(self.info.surface.format)
306 .image_extent(vk::Extent2D {
307 width: surface_width,
308 height: surface_height,
309 })
310 .image_usage(image_usage)
311 .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
312 .pre_transform(pre_transform)
313 .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
314 .present_mode(present_mode)
315 .clipped(true)
316 .old_swapchain(self.swapchain)
317 .image_array_layers(1);
318 let swapchain = unsafe { swapchain_ext.create_swapchain(&swapchain_create_info, None) }
319 .map_err(|err| {
320 warn!("{err}");
321
322 DriverError::Unsupported
323 })?;
324
325 let images =
326 unsafe { swapchain_ext.get_swapchain_images(swapchain) }.map_err(|err| match err {
327 vk::Result::INCOMPLETE => DriverError::InvalidData,
328 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
329 DriverError::OutOfMemory
330 }
331 _ => DriverError::Unsupported,
332 })?;
333 let images = images
334 .into_iter()
335 .enumerate()
336 .map(|(image_idx, image)| {
337 let mut image = Image::from_raw(
338 &self.device,
339 image,
340 ImageInfo::image_2d(
341 surface_width,
342 surface_height,
343 self.info.surface.format,
344 image_usage,
345 ),
346 );
347
348 let image_idx = image_idx as u32;
349 image.name = Some(format!("swapchain{image_idx}"));
350
351 Ok(SwapchainImage {
352 exec_idx: 0,
353 image,
354 image_idx,
355 })
356 })
357 .collect::<Result<Box<_>, _>>()?;
358
359 self.info.height = surface_height;
360 self.info.width = surface_width;
361 self.images = images;
362 self.old_swapchain = self.swapchain;
363 self.swapchain = swapchain;
364 self.suboptimal = false;
365
366 info!(
367 "swapchain {}x{} {present_mode:?}x{} {:?} {image_usage:#?}",
368 self.info.width,
369 self.info.height,
370 self.images.len(),
371 self.info.surface.format,
372 );
373
374 Ok(())
375 }
376
377 pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
381 let info: SwapchainInfo = info.into();
382
383 if self.info != info {
384 #[cfg(target_os = "macos")]
386 if let Err(err) = unsafe { self.device.device_wait_idle() } {
387 warn!("device_wait_idle() failed: {err}");
388 }
389
390 self.info = info;
391
392 trace!("info: {:?}", self.info);
393
394 self.suboptimal = true;
395 }
396 }
397
398 fn supported_surface_usage(
399 &mut self,
400 surface_capabilities: vk::ImageUsageFlags,
401 ) -> Result<vk::ImageUsageFlags, DriverError> {
402 let mut res = vk::ImageUsageFlags::empty();
403
404 for bit in 0..u32::BITS {
405 let usage = vk::ImageUsageFlags::from_raw((1 << bit) & surface_capabilities.as_raw());
406 if usage.is_empty() {
407 continue;
408 }
409
410 if Device::image_format_properties(
411 &self.device,
412 self.info.surface.format,
413 vk::ImageType::TYPE_2D,
414 vk::ImageTiling::OPTIMAL,
415 usage,
416 vk::ImageCreateFlags::empty(),
417 )
418 .inspect_err(|err| {
419 warn!(
420 "unable to get image format properties: {:?} {:?} {err}",
421 self.info.surface.format, usage
422 )
423 })?
424 .is_none()
425 {
426 continue;
427 }
428
429 res |= usage;
430 }
431
432 res &= !vk::ImageUsageFlags::ATTACHMENT_FEEDBACK_LOOP_EXT;
435
436 Ok(res)
437 }
438}
439
440impl Drop for Swapchain {
441 #[profiling::function]
442 fn drop(&mut self) {
443 if panicking() {
444 return;
445 }
446
447 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
448 Self::destroy_swapchain(&self.device, &mut self.swapchain);
449 }
450}
451
452#[derive(Clone, Copy, Debug, PartialEq)]
454pub enum SwapchainError {
455 DeviceLost,
457
458 Suboptimal,
460
461 SurfaceLost,
463}
464
465#[derive(Debug)]
467pub struct SwapchainImage {
468 pub(crate) exec_idx: usize,
469 image: Image,
470 image_idx: u32,
471}
472
473impl SwapchainImage {
474 pub(crate) fn clone_swapchain(this: &Self) -> Self {
475 let Self {
476 exec_idx,
477 image,
478 image_idx,
479 } = this;
480
481 Self {
482 exec_idx: *exec_idx,
483 image: Image::clone_swapchain(image),
484 image_idx: *image_idx,
485 }
486 }
487}
488
489impl Deref for SwapchainImage {
490 type Target = Image;
491
492 fn deref(&self) -> &Self::Target {
493 &self.image
494 }
495}
496
497#[derive(Builder, Clone, Debug, Eq, Hash, PartialEq)]
499#[builder(
500 build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"),
501 derive(Clone, Debug),
502 pattern = "owned"
503)]
504#[non_exhaustive]
505pub struct SwapchainInfo {
506 #[builder(default = "3")]
510 pub desired_image_count: u32,
511
512 pub height: u32,
514
515 pub surface: vk::SurfaceFormatKHR,
517
518 #[builder(default = vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO])]
559 pub present_modes: Vec<vk::PresentModeKHR>,
560
561 pub width: u32,
563}
564
565impl SwapchainInfo {
566 #[inline(always)]
568 pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo {
569 Self {
570 width,
571 height,
572 surface,
573 desired_image_count: 3,
574 present_modes: vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO],
575 }
576 }
577
578 #[inline(always)]
580 pub fn to_builder(self) -> SwapchainInfoBuilder {
581 SwapchainInfoBuilder {
582 desired_image_count: Some(self.desired_image_count),
583 height: Some(self.height),
584 surface: Some(self.surface),
585 present_modes: Some(self.present_modes),
586 width: Some(self.width),
587 }
588 }
589}
590
591impl From<SwapchainInfoBuilder> for SwapchainInfo {
592 fn from(info: SwapchainInfoBuilder) -> Self {
593 info.build()
594 }
595}
596
597impl SwapchainInfoBuilder {
598 #[inline(always)]
608 pub fn build(self) -> SwapchainInfo {
609 match self.fallible_build() {
610 Err(SwapchainInfoBuilderError(err)) => panic!("{err}"),
611 Ok(info) => info,
612 }
613 }
614}
615
616#[derive(Debug)]
617struct SwapchainInfoBuilderError(UninitializedFieldError);
618
619impl From<UninitializedFieldError> for SwapchainInfoBuilderError {
620 fn from(err: UninitializedFieldError) -> Self {
621 Self(err)
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628
629 type Info = SwapchainInfo;
630 type Builder = SwapchainInfoBuilder;
631
632 #[test]
633 pub fn swapchain_info() {
634 let info = Info::new(20, 24, vk::SurfaceFormatKHR::default());
635 let builder = info.clone().to_builder().build();
636
637 assert_eq!(info, builder);
638 }
639
640 #[test]
641 pub fn swapchain_info_builder() {
642 let info = Info::new(23, 64, vk::SurfaceFormatKHR::default());
643 let builder = Builder::default()
644 .width(23)
645 .height(64)
646 .surface(vk::SurfaceFormatKHR::default())
647 .build();
648
649 assert_eq!(info, builder);
650 }
651
652 #[test]
653 #[should_panic(expected = "Field not initialized: height")]
654 pub fn swapchain_info_builder_uninit_height() {
655 Builder::default().build();
656 }
657
658 #[test]
659 #[should_panic(expected = "Field not initialized: surface")]
660 pub fn swapchain_info_builder_uninit_surface() {
661 Builder::default().height(42).build();
662 }
663
664 #[test]
665 #[should_panic(expected = "Field not initialized: width")]
666 pub fn swapchain_info_builder_uninit_width() {
667 Builder::default()
668 .height(42)
669 .surface(vk::SurfaceFormatKHR::default())
670 .build();
671 }
672}