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() {
167 let swapchain_ext = Device::expect_swapchain_ext(device);
168
169 unsafe {
170 swapchain_ext.destroy_swapchain(*swapchain, None);
171 }
172
173 *swapchain = vk::SwapchainKHR::null();
174 }
175 }
176
177 pub fn info(&self) -> SwapchainInfo {
179 self.info
180 }
181
182 #[profiling::function]
185 pub fn present_image(
186 &mut self,
187 image: SwapchainImage,
188 wait_semaphores: &[vk::Semaphore],
189 queue_family_index: u32,
190 queue_index: u32,
191 ) {
192 let queue_family_index = queue_family_index as usize;
193 let queue_index = queue_index as usize;
194
195 debug_assert!(
196 queue_family_index < self.device.physical_device.queue_families.len(),
197 "Queue family index must be within the range of the available queues created by the device."
198 );
199 debug_assert!(
200 queue_index
201 < self.device.physical_device.queue_families[queue_family_index].queue_count
202 as usize,
203 "Queue index must be within the range of the available queues created by the device."
204 );
205
206 let present_info = vk::PresentInfoKHR::default()
207 .wait_semaphores(wait_semaphores)
208 .swapchains(slice::from_ref(&self.swapchain))
209 .image_indices(slice::from_ref(&image.image_idx));
210
211 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
212
213 unsafe {
214 match swapchain_ext.queue_present(
215 self.device.queues[queue_family_index][queue_index],
216 &present_info,
217 ) {
218 Ok(_) => {
219 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
220 }
221 Err(err)
222 if err == vk::Result::ERROR_DEVICE_LOST
223 || err == vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
224 || err == vk::Result::ERROR_OUT_OF_DATE_KHR
225 || err == vk::Result::ERROR_SURFACE_LOST_KHR
226 || err == vk::Result::SUBOPTIMAL_KHR =>
227 {
228 self.suboptimal = true;
230 }
231 Err(err) => {
232 warn!("{err}");
236 }
237 }
238 }
239
240 let image_idx = image.image_idx as usize;
241 self.images[image_idx] = image;
242 }
243
244 #[profiling::function]
245 fn recreate_swapchain(&mut self) -> Result<(), DriverError> {
246 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
247
248 let (surface_capabilities, present_modes) = {
249 let surface_ext = Device::expect_surface_ext(&self.device);
250 let surface_capabilities = unsafe {
251 surface_ext.get_physical_device_surface_capabilities(
252 *self.device.physical_device,
253 *self.surface,
254 )
255 }
256 .inspect_err(|err| warn!("unable to get surface capabilities: {err}"))
257 .or(Err(DriverError::Unsupported))?;
258
259 let present_modes = unsafe {
260 surface_ext.get_physical_device_surface_present_modes(
261 *self.device.physical_device,
262 *self.surface,
263 )
264 }
265 .inspect_err(|err| warn!("unable to get surface present modes: {err}"))
266 .or(Err(DriverError::Unsupported))?;
267
268 (surface_capabilities, present_modes)
269 };
270
271 let desired_image_count =
272 Self::clamp_desired_image_count(self.info.desired_image_count, surface_capabilities);
273
274 let image_usage =
275 self.supported_surface_usage(surface_capabilities.supported_usage_flags)?;
276
277 let (surface_width, surface_height) = match surface_capabilities.current_extent.width {
278 std::u32::MAX => (
279 self.info.width.clamp(
281 surface_capabilities.min_image_extent.width,
282 surface_capabilities.max_image_extent.width,
283 ),
284 self.info.height.clamp(
285 surface_capabilities.min_image_extent.height,
286 surface_capabilities.max_image_extent.height,
287 ),
288 ),
289 _ => (
290 surface_capabilities.current_extent.width,
291 surface_capabilities.current_extent.height,
292 ),
293 };
294
295 if surface_width * surface_height == 0 {
296 return Err(DriverError::Unsupported);
297 }
298
299 let present_mode_preference = if self.info.sync_display {
300 vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO]
301 } else {
302 vec![vk::PresentModeKHR::MAILBOX, vk::PresentModeKHR::IMMEDIATE]
303 };
304 let present_mode = present_mode_preference
305 .into_iter()
306 .find(|mode| present_modes.contains(mode))
307 .unwrap_or(vk::PresentModeKHR::FIFO);
308
309 let pre_transform = if surface_capabilities
310 .supported_transforms
311 .contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
312 {
313 vk::SurfaceTransformFlagsKHR::IDENTITY
314 } else {
315 surface_capabilities.current_transform
316 };
317
318 let swapchain_ext = Device::expect_swapchain_ext(&self.device);
319 let swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
320 .surface(*self.surface)
321 .min_image_count(desired_image_count)
322 .image_color_space(self.info.surface.color_space)
323 .image_format(self.info.surface.format)
324 .image_extent(vk::Extent2D {
325 width: surface_width,
326 height: surface_height,
327 })
328 .image_usage(image_usage)
329 .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
330 .pre_transform(pre_transform)
331 .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
332 .present_mode(present_mode)
333 .clipped(true)
334 .old_swapchain(self.swapchain)
335 .image_array_layers(1);
336 let swapchain = unsafe { swapchain_ext.create_swapchain(&swapchain_create_info, None) }
337 .map_err(|err| {
338 warn!("{err}");
339
340 DriverError::Unsupported
341 })?;
342
343 let images =
344 unsafe { swapchain_ext.get_swapchain_images(swapchain) }.map_err(|err| match err {
345 vk::Result::INCOMPLETE => DriverError::InvalidData,
346 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
347 DriverError::OutOfMemory
348 }
349 _ => DriverError::Unsupported,
350 })?;
351 let images = images
352 .into_iter()
353 .enumerate()
354 .map(|(image_idx, image)| {
355 let mut image = Image::from_raw(
356 &self.device,
357 image,
358 ImageInfo::image_2d(
359 surface_width,
360 surface_height,
361 self.info.surface.format,
362 image_usage,
363 ),
364 );
365
366 let image_idx = image_idx as u32;
367 image.name = Some(format!("swapchain{image_idx}"));
368
369 Ok(SwapchainImage {
370 exec_idx: 0,
371 image,
372 image_idx,
373 })
374 })
375 .collect::<Result<Box<_>, _>>()?;
376
377 self.info.height = surface_height;
378 self.info.width = surface_width;
379 self.images = images;
380 self.old_swapchain = self.swapchain;
381 self.swapchain = swapchain;
382 self.suboptimal = false;
383
384 info!(
385 "swapchain {}x{} {present_mode:?}x{} {:?} {image_usage:#?}",
386 self.info.width,
387 self.info.height,
388 self.images.len(),
389 self.info.surface.format,
390 );
391
392 Ok(())
393 }
394
395 pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
399 let info: SwapchainInfo = info.into();
400
401 if self.info != info {
402 self.info = info;
403
404 trace!("info: {:?}", self.info);
405
406 self.suboptimal = true;
407 }
408 }
409
410 fn supported_surface_usage(
411 &mut self,
412 surface_capabilities: vk::ImageUsageFlags,
413 ) -> Result<vk::ImageUsageFlags, DriverError> {
414 let mut res = vk::ImageUsageFlags::empty();
415
416 for bit in 0..u32::BITS {
417 let usage = vk::ImageUsageFlags::from_raw((1 << bit) & surface_capabilities.as_raw());
418 if usage.is_empty() {
419 continue;
420 }
421
422 if Device::image_format_properties(
423 &self.device,
424 self.info.surface.format,
425 vk::ImageType::TYPE_2D,
426 vk::ImageTiling::OPTIMAL,
427 usage,
428 vk::ImageCreateFlags::empty(),
429 )
430 .inspect_err(|err| {
431 warn!(
432 "unable to get image format properties: {:?} {:?} {err}",
433 self.info.surface.format, usage
434 )
435 })?
436 .is_none()
437 {
438 continue;
439 }
440
441 res |= usage;
442 }
443
444 res &= !vk::ImageUsageFlags::ATTACHMENT_FEEDBACK_LOOP_EXT;
447
448 Ok(res)
449 }
450}
451
452impl Drop for Swapchain {
453 #[profiling::function]
454 fn drop(&mut self) {
455 if panicking() {
456 return;
457 }
458
459 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
460 Self::destroy_swapchain(&self.device, &mut self.swapchain);
461 }
462}
463
464#[derive(Clone, Copy, Debug, PartialEq)]
466pub enum SwapchainError {
467 DeviceLost,
469
470 Suboptimal,
472
473 SurfaceLost,
475}
476
477#[derive(Debug)]
479pub struct SwapchainImage {
480 pub(crate) exec_idx: usize,
481 image: Image,
482 image_idx: u32,
483}
484
485impl SwapchainImage {
486 pub(crate) fn clone_swapchain(this: &Self) -> Self {
487 let Self {
488 exec_idx,
489 image,
490 image_idx,
491 } = this;
492
493 Self {
494 exec_idx: *exec_idx,
495 image: Image::clone_swapchain(image),
496 image_idx: *image_idx,
497 }
498 }
499}
500
501impl Deref for SwapchainImage {
502 type Target = Image;
503
504 fn deref(&self) -> &Self::Target {
505 &self.image
506 }
507}
508
509#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
511#[builder(
512 build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"),
513 derive(Clone, Copy, Debug),
514 pattern = "owned"
515)]
516#[non_exhaustive]
517pub struct SwapchainInfo {
518 #[builder(default = "3")]
522 pub desired_image_count: u32,
523
524 pub height: u32,
526
527 pub surface: vk::SurfaceFormatKHR,
529
530 #[builder(default = "true")]
535 pub sync_display: bool,
536
537 pub width: u32,
539}
540
541impl SwapchainInfo {
542 #[inline(always)]
544 pub const fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo {
545 Self {
546 width,
547 height,
548 surface,
549 desired_image_count: 3,
550 sync_display: true,
551 }
552 }
553
554 #[inline(always)]
556 pub fn to_builder(self) -> SwapchainInfoBuilder {
557 SwapchainInfoBuilder {
558 desired_image_count: Some(self.desired_image_count),
559 height: Some(self.height),
560 surface: Some(self.surface),
561 sync_display: Some(self.sync_display),
562 width: Some(self.width),
563 }
564 }
565}
566
567impl From<SwapchainInfoBuilder> for SwapchainInfo {
568 fn from(info: SwapchainInfoBuilder) -> Self {
569 info.build()
570 }
571}
572
573impl SwapchainInfoBuilder {
574 #[inline(always)]
584 pub fn build(self) -> SwapchainInfo {
585 match self.fallible_build() {
586 Err(SwapchainInfoBuilderError(err)) => panic!("{err}"),
587 Ok(info) => info,
588 }
589 }
590}
591
592#[derive(Debug)]
593struct SwapchainInfoBuilderError(UninitializedFieldError);
594
595impl From<UninitializedFieldError> for SwapchainInfoBuilderError {
596 fn from(err: UninitializedFieldError) -> Self {
597 Self(err)
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604
605 type Info = SwapchainInfo;
606 type Builder = SwapchainInfoBuilder;
607
608 #[test]
609 pub fn swapchain_info() {
610 let info = Info::new(20, 24, vk::SurfaceFormatKHR::default());
611 let builder = info.to_builder().build();
612
613 assert_eq!(info, builder);
614 }
615
616 #[test]
617 pub fn swapchain_info_builder() {
618 let info = Info::new(23, 64, vk::SurfaceFormatKHR::default());
619 let builder = Builder::default()
620 .width(23)
621 .height(64)
622 .surface(vk::SurfaceFormatKHR::default())
623 .build();
624
625 assert_eq!(info, builder);
626 }
627
628 #[test]
629 #[should_panic(expected = "Field not initialized: height")]
630 pub fn swapchain_info_builder_uninit_height() {
631 Builder::default().build();
632 }
633
634 #[test]
635 #[should_panic(expected = "Field not initialized: surface")]
636 pub fn swapchain_info_builder_uninit_surface() {
637 Builder::default().height(42).build();
638 }
639
640 #[test]
641 #[should_panic(expected = "Field not initialized: width")]
642 pub fn swapchain_info_builder_uninit_width() {
643 Builder::default()
644 .height(42)
645 .surface(vk::SurfaceFormatKHR::default())
646 .build();
647 }
648}