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
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_capabilities, present_modes) = {
254 let surface_ext = Device::expect_surface_ext(&self.device);
255 let surface_capabilities = unsafe {
256 surface_ext.get_physical_device_surface_capabilities(
257 *self.device.physical_device,
258 *self.surface,
259 )
260 }
261 .inspect_err(|err| warn!("unable to get surface capabilities: {err}"))
262 .or(Err(DriverError::Unsupported))?;
263
264 let present_modes = unsafe {
265 surface_ext.get_physical_device_surface_present_modes(
266 *self.device.physical_device,
267 *self.surface,
268 )
269 }
270 .inspect_err(|err| warn!("unable to get surface present modes: {err}"))
271 .or(Err(DriverError::Unsupported))?;
272
273 (surface_capabilities, present_modes)
274 };
275
276 let desired_image_count =
277 Self::clamp_desired_image_count(self.info.desired_image_count, surface_capabilities);
278
279 let image_usage =
280 self.supported_surface_usage(surface_capabilities.supported_usage_flags)?;
281
282 let (surface_width, surface_height) = match surface_capabilities.current_extent.width {
283 std::u32::MAX => (
284 self.info.width.clamp(
286 surface_capabilities.min_image_extent.width,
287 surface_capabilities.max_image_extent.width,
288 ),
289 self.info.height.clamp(
290 surface_capabilities.min_image_extent.height,
291 surface_capabilities.max_image_extent.height,
292 ),
293 ),
294 _ => (
295 surface_capabilities.current_extent.width,
296 surface_capabilities.current_extent.height,
297 ),
298 };
299
300 if surface_width * surface_height == 0 {
301 return Err(DriverError::Unsupported);
302 }
303
304 let present_mode_preference = if self.info.sync_display {
305 vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO]
306 } else {
307 vec![vk::PresentModeKHR::MAILBOX, vk::PresentModeKHR::IMMEDIATE]
308 };
309 let present_mode = present_mode_preference
310 .into_iter()
311 .find(|mode| present_modes.contains(mode))
312 .unwrap_or(vk::PresentModeKHR::FIFO);
313
314 let pre_transform = if surface_capabilities
315 .supported_transforms
316 .contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
317 {
318 vk::SurfaceTransformFlagsKHR::IDENTITY
319 } else {
320 surface_capabilities.current_transform
321 };
322
323 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
324 let swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
325 .surface(*self.surface)
326 .min_image_count(desired_image_count)
327 .image_color_space(self.info.surface.color_space)
328 .image_format(self.info.surface.format)
329 .image_extent(vk::Extent2D {
330 width: surface_width,
331 height: surface_height,
332 })
333 .image_usage(image_usage)
334 .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
335 .pre_transform(pre_transform)
336 .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
337 .present_mode(present_mode)
338 .clipped(true)
339 .old_swapchain(self.swapchain)
340 .image_array_layers(1);
341 let swapchain = unsafe { swapchain_ext.create_swapchain(&swapchain_create_info, None) }
342 .map_err(|err| {
343 warn!("{err}");
344
345 DriverError::Unsupported
346 })?;
347
348 let images =
349 unsafe { swapchain_ext.get_swapchain_images(swapchain) }.map_err(|err| match err {
350 vk::Result::INCOMPLETE => DriverError::InvalidData,
351 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
352 DriverError::OutOfMemory
353 }
354 _ => DriverError::Unsupported,
355 })?;
356 let images = images
357 .into_iter()
358 .enumerate()
359 .map(|(image_idx, image)| {
360 let mut image = Image::from_raw(
361 &self.device,
362 image,
363 ImageInfo::image_2d(
364 surface_width,
365 surface_height,
366 self.info.surface.format,
367 image_usage,
368 ),
369 );
370
371 let image_idx = image_idx as u32;
372 image.name = Some(format!("swapchain{image_idx}"));
373
374 Ok(SwapchainImage {
375 exec_idx: 0,
376 image,
377 image_idx,
378 })
379 })
380 .collect::<Result<Box<_>, _>>()?;
381
382 self.info.height = surface_height;
383 self.info.width = surface_width;
384 self.images = images;
385 self.old_swapchain = self.swapchain;
386 self.swapchain = swapchain;
387 self.suboptimal = false;
388
389 info!(
390 "swapchain {}x{} {present_mode:?}x{} {:?} {image_usage:#?}",
391 self.info.width,
392 self.info.height,
393 self.images.len(),
394 self.info.surface.format,
395 );
396
397 Ok(())
398 }
399
400 pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
404 let info: SwapchainInfo = info.into();
405
406 if self.info != info {
407 #[cfg(target_os = "macos")]
409 if let Err(err) = unsafe { self.device.device_wait_idle() } {
410 warn!("device_wait_idle() failed: {err}");
411 }
412
413 self.info = info;
414
415 trace!("info: {:?}", self.info);
416
417 self.suboptimal = true;
418 }
419 }
420
421 fn supported_surface_usage(
422 &mut self,
423 surface_capabilities: vk::ImageUsageFlags,
424 ) -> Result<vk::ImageUsageFlags, DriverError> {
425 let mut res = vk::ImageUsageFlags::empty();
426
427 for bit in 0..u32::BITS {
428 let usage = vk::ImageUsageFlags::from_raw((1 << bit) & surface_capabilities.as_raw());
429 if usage.is_empty() {
430 continue;
431 }
432
433 if Device::image_format_properties(
434 &self.device,
435 self.info.surface.format,
436 vk::ImageType::TYPE_2D,
437 vk::ImageTiling::OPTIMAL,
438 usage,
439 vk::ImageCreateFlags::empty(),
440 )
441 .inspect_err(|err| {
442 warn!(
443 "unable to get image format properties: {:?} {:?} {err}",
444 self.info.surface.format, usage
445 )
446 })?
447 .is_none()
448 {
449 continue;
450 }
451
452 res |= usage;
453 }
454
455 res &= !vk::ImageUsageFlags::ATTACHMENT_FEEDBACK_LOOP_EXT;
458
459 Ok(res)
460 }
461}
462
463impl Drop for Swapchain {
464 #[profiling::function]
465 fn drop(&mut self) {
466 if panicking() {
467 return;
468 }
469
470 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
471 Self::destroy_swapchain(&self.device, &mut self.swapchain);
472 }
473}
474
475#[derive(Clone, Copy, Debug, PartialEq)]
477pub enum SwapchainError {
478 DeviceLost,
480
481 Suboptimal,
483
484 SurfaceLost,
486}
487
488#[derive(Debug)]
490pub struct SwapchainImage {
491 pub(crate) exec_idx: usize,
492 image: Image,
493 image_idx: u32,
494}
495
496impl SwapchainImage {
497 pub(crate) fn clone_swapchain(this: &Self) -> Self {
498 let Self {
499 exec_idx,
500 image,
501 image_idx,
502 } = this;
503
504 Self {
505 exec_idx: *exec_idx,
506 image: Image::clone_swapchain(image),
507 image_idx: *image_idx,
508 }
509 }
510}
511
512impl Deref for SwapchainImage {
513 type Target = Image;
514
515 fn deref(&self) -> &Self::Target {
516 &self.image
517 }
518}
519
520#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
522#[builder(
523 build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"),
524 derive(Clone, Copy, Debug),
525 pattern = "owned"
526)]
527#[non_exhaustive]
528pub struct SwapchainInfo {
529 #[builder(default = "3")]
533 pub desired_image_count: u32,
534
535 pub height: u32,
537
538 pub surface: vk::SurfaceFormatKHR,
540
541 #[builder(default = "true")]
546 pub sync_display: bool,
547
548 pub width: u32,
550}
551
552impl SwapchainInfo {
553 #[inline(always)]
555 pub const fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo {
556 Self {
557 width,
558 height,
559 surface,
560 desired_image_count: 3,
561 sync_display: true,
562 }
563 }
564
565 #[inline(always)]
567 pub fn to_builder(self) -> SwapchainInfoBuilder {
568 SwapchainInfoBuilder {
569 desired_image_count: Some(self.desired_image_count),
570 height: Some(self.height),
571 surface: Some(self.surface),
572 sync_display: Some(self.sync_display),
573 width: Some(self.width),
574 }
575 }
576}
577
578impl From<SwapchainInfoBuilder> for SwapchainInfo {
579 fn from(info: SwapchainInfoBuilder) -> Self {
580 info.build()
581 }
582}
583
584impl SwapchainInfoBuilder {
585 #[inline(always)]
595 pub fn build(self) -> SwapchainInfo {
596 match self.fallible_build() {
597 Err(SwapchainInfoBuilderError(err)) => panic!("{err}"),
598 Ok(info) => info,
599 }
600 }
601}
602
603#[derive(Debug)]
604struct SwapchainInfoBuilderError(UninitializedFieldError);
605
606impl From<UninitializedFieldError> for SwapchainInfoBuilderError {
607 fn from(err: UninitializedFieldError) -> Self {
608 Self(err)
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615
616 type Info = SwapchainInfo;
617 type Builder = SwapchainInfoBuilder;
618
619 #[test]
620 pub fn swapchain_info() {
621 let info = Info::new(20, 24, vk::SurfaceFormatKHR::default());
622 let builder = info.to_builder().build();
623
624 assert_eq!(info, builder);
625 }
626
627 #[test]
628 pub fn swapchain_info_builder() {
629 let info = Info::new(23, 64, vk::SurfaceFormatKHR::default());
630 let builder = Builder::default()
631 .width(23)
632 .height(64)
633 .surface(vk::SurfaceFormatKHR::default())
634 .build();
635
636 assert_eq!(info, builder);
637 }
638
639 #[test]
640 #[should_panic(expected = "Field not initialized: height")]
641 pub fn swapchain_info_builder_uninit_height() {
642 Builder::default().build();
643 }
644
645 #[test]
646 #[should_panic(expected = "Field not initialized: surface")]
647 pub fn swapchain_info_builder_uninit_surface() {
648 Builder::default().height(42).build();
649 }
650
651 #[test]
652 #[should_panic(expected = "Field not initialized: width")]
653 pub fn swapchain_info_builder_uninit_width() {
654 Builder::default()
655 .height(42)
656 .surface(vk::SurfaceFormatKHR::default())
657 .build();
658 }
659}