screen_13/driver/
swapchain.rs

1//! Native window presentation types.
2
3use {
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// TODO: This needs to track completed command buffers and not constantly create semaphores
16
17/// Provides the ability to present rendering results to a [`Surface`].
18#[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    /// Prepares a [`vk::SwapchainKHR`] object which is lazily created after calling
31    /// [`acquire_next_image`][Self::acquire_next_image].
32    #[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    /// Gets the next available swapchain image which should be rendered to and then presented using
53    /// [`present_image`][Self::present_image].
54    #[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                    // Try again to see if we can acquire an image this frame
115                    // (Makes redraw during resize look slightly better)
116                    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                    // Probably:
134                    // VK_ERROR_OUT_OF_HOST_MEMORY
135                    // VK_ERROR_OUT_OF_DEVICE_MEMORY
136
137                    // TODO: Maybe handle timeout in here
138
139                    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            // wait for device to be finished with swapchain before destroying it.
166            // This avoid crashes when resizing windows
167            #[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    /// Gets information about this swapchain.
183    pub fn info(&self) -> SwapchainInfo {
184        self.info
185    }
186
187    /// Presents an image which has been previously acquired using
188    /// [`acquire_next_image`][Self::acquire_next_image].
189    #[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                    // Handled in the next frame
234                    self.suboptimal = true;
235                }
236                Err(err) => {
237                    // Probably:
238                    // VK_ERROR_OUT_OF_HOST_MEMORY
239                    // VK_ERROR_OUT_OF_DEVICE_MEMORY
240                    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                // TODO: Maybe handle this case with aspect-correct clamping?
285                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    /// Sets information about this swapchain.
401    ///
402    /// Previously acquired swapchain images should be discarded after calling this function.
403    pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
404        let info: SwapchainInfo = info.into();
405
406        if self.info != info {
407            // attempt to reducing flickering when resizing windows on mac
408            #[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        // On mesa the device will return this usage flag as supported even when the extension
456        // that is needed for an image to have this flag isn't enabled
457        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/// Describes the condition of a swapchain.
476#[derive(Clone, Copy, Debug, PartialEq)]
477pub enum SwapchainError {
478    /// This frame is lost but more may be acquired later.
479    DeviceLost,
480
481    /// This frame is not lost but there may be a delay while the next frame is recreated.
482    Suboptimal,
483
484    /// The surface was lost and must be recreated, which includes any operating system window.
485    SurfaceLost,
486}
487
488/// An opaque type representing a swapchain image.
489#[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/// Information used to create a [`Swapchain`] instance.
521#[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    /// The desired, but not guaranteed, number of images that will be in the created swapchain.
530    ///
531    /// More images introduces more display lag, but smoother animation.
532    #[builder(default = "3")]
533    pub desired_image_count: u32,
534
535    /// The initial height of the surface.
536    pub height: u32,
537
538    /// The format and color space of the surface.
539    pub surface: vk::SurfaceFormatKHR,
540
541    /// Determines if frames will be submitted to the display in a synchronous fashion or if they
542    /// should be displayed as fast as possible instead.
543    ///
544    /// Turn on to eliminate visual tearing at the expense of latency.
545    #[builder(default = "true")]
546    pub sync_display: bool,
547
548    /// The initial width of the surface.
549    pub width: u32,
550}
551
552impl SwapchainInfo {
553    /// Specifies a default swapchain with the given `width`, `height` and `format` values.
554    #[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    /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`.
566    #[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    /// Builds a new `SwapchainInfo`.
586    ///
587    /// # Panics
588    ///
589    /// If any of the following values have not been set this function will panic:
590    ///
591    /// * `width`
592    /// * `height`
593    /// * `surface`
594    #[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}