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 debug_assert_eq!(desired_image_count, images.len() as u32);
378
379 self.info.height = surface_height;
380 self.info.width = surface_width;
381 self.images = images;
382 self.old_swapchain = self.swapchain;
383 self.swapchain = swapchain;
384 self.suboptimal = false;
385
386 info!(
387 "swapchain {}x{} {present_mode:?}x{} {:?} {image_usage:#?}",
388 self.info.width,
389 self.info.height,
390 self.images.len(),
391 self.info.surface.format,
392 );
393
394 Ok(())
395 }
396
397 pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
401 let info: SwapchainInfo = info.into();
402
403 if self.info != info {
404 self.info = info;
405
406 trace!("info: {:?}", self.info);
407
408 self.suboptimal = true;
409 }
410 }
411
412 fn supported_surface_usage(
413 &mut self,
414 surface_capabilities: vk::ImageUsageFlags,
415 ) -> Result<vk::ImageUsageFlags, DriverError> {
416 let mut res = vk::ImageUsageFlags::empty();
417
418 for bit in 0..u32::BITS {
419 let usage = vk::ImageUsageFlags::from_raw((1 << bit) & surface_capabilities.as_raw());
420 if usage.is_empty() {
421 continue;
422 }
423
424 if Device::image_format_properties(
425 &self.device,
426 self.info.surface.format,
427 vk::ImageType::TYPE_2D,
428 vk::ImageTiling::OPTIMAL,
429 usage,
430 vk::ImageCreateFlags::empty(),
431 )
432 .inspect_err(|err| {
433 warn!(
434 "unable to get image format properties: {:?} {:?} {err}",
435 self.info.surface.format, usage
436 )
437 })?
438 .is_none()
439 {
440 continue;
441 }
442
443 res |= usage;
444 }
445
446 Ok(res)
447 }
448}
449
450impl Drop for Swapchain {
451 #[profiling::function]
452 fn drop(&mut self) {
453 if panicking() {
454 return;
455 }
456
457 Self::destroy_swapchain(&self.device, &mut self.old_swapchain);
458 Self::destroy_swapchain(&self.device, &mut self.swapchain);
459 }
460}
461
462#[derive(Clone, Copy, Debug, PartialEq)]
464pub enum SwapchainError {
465 DeviceLost,
467
468 Suboptimal,
470
471 SurfaceLost,
473}
474
475#[derive(Debug)]
477pub struct SwapchainImage {
478 pub(crate) exec_idx: usize,
479 image: Image,
480 image_idx: u32,
481}
482
483impl SwapchainImage {
484 pub(crate) fn clone_swapchain(this: &Self) -> Self {
485 let Self {
486 exec_idx,
487 image,
488 image_idx,
489 } = this;
490
491 Self {
492 exec_idx: *exec_idx,
493 image: Image::clone_swapchain(image),
494 image_idx: *image_idx,
495 }
496 }
497}
498
499impl Deref for SwapchainImage {
500 type Target = Image;
501
502 fn deref(&self) -> &Self::Target {
503 &self.image
504 }
505}
506
507#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
509#[builder(
510 build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"),
511 derive(Clone, Copy, Debug),
512 pattern = "owned"
513)]
514#[non_exhaustive]
515pub struct SwapchainInfo {
516 #[builder(default = "3")]
520 pub desired_image_count: u32,
521
522 pub height: u32,
524
525 pub surface: vk::SurfaceFormatKHR,
527
528 #[builder(default = "true")]
533 pub sync_display: bool,
534
535 pub width: u32,
537}
538
539impl SwapchainInfo {
540 #[inline(always)]
542 pub const fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo {
543 Self {
544 width,
545 height,
546 surface,
547 desired_image_count: 3,
548 sync_display: true,
549 }
550 }
551
552 #[inline(always)]
554 pub fn to_builder(self) -> SwapchainInfoBuilder {
555 SwapchainInfoBuilder {
556 desired_image_count: Some(self.desired_image_count),
557 height: Some(self.height),
558 surface: Some(self.surface),
559 sync_display: Some(self.sync_display),
560 width: Some(self.width),
561 }
562 }
563}
564
565impl From<SwapchainInfoBuilder> for SwapchainInfo {
566 fn from(info: SwapchainInfoBuilder) -> Self {
567 info.build()
568 }
569}
570
571impl SwapchainInfoBuilder {
572 #[inline(always)]
582 pub fn build(self) -> SwapchainInfo {
583 match self.fallible_build() {
584 Err(SwapchainInfoBuilderError(err)) => panic!("{err}"),
585 Ok(info) => info,
586 }
587 }
588}
589
590#[derive(Debug)]
591struct SwapchainInfoBuilderError(UninitializedFieldError);
592
593impl From<UninitializedFieldError> for SwapchainInfoBuilderError {
594 fn from(err: UninitializedFieldError) -> Self {
595 Self(err)
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use super::*;
602
603 type Info = SwapchainInfo;
604 type Builder = SwapchainInfoBuilder;
605
606 #[test]
607 pub fn swapchain_info() {
608 let info = Info::new(20, 24, vk::SurfaceFormatKHR::default());
609 let builder = info.to_builder().build();
610
611 assert_eq!(info, builder);
612 }
613
614 #[test]
615 pub fn swapchain_info_builder() {
616 let info = Info::new(23, 64, vk::SurfaceFormatKHR::default());
617 let builder = Builder::default()
618 .width(23)
619 .height(64)
620 .surface(vk::SurfaceFormatKHR::default())
621 .build();
622
623 assert_eq!(info, builder);
624 }
625
626 #[test]
627 #[should_panic(expected = "Field not initialized: height")]
628 pub fn swapchain_info_builder_uninit_height() {
629 Builder::default().build();
630 }
631
632 #[test]
633 #[should_panic(expected = "Field not initialized: surface")]
634 pub fn swapchain_info_builder_uninit_surface() {
635 Builder::default().height(42).build();
636 }
637
638 #[test]
639 #[should_panic(expected = "Field not initialized: width")]
640 pub fn swapchain_info_builder_uninit_width() {
641 Builder::default()
642 .height(42)
643 .surface(vk::SurfaceFormatKHR::default())
644 .build();
645 }
646}