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        // TODO: Any cases where we need to wait for idle here?
165
166        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    /// Gets information about this swapchain.
178    pub fn info(&self) -> SwapchainInfo {
179        self.info
180    }
181
182    /// Presents an image which has been previously acquired using
183    /// [`acquire_next_image`][Self::acquire_next_image].
184    #[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                    // Handled in the next frame
229                    self.suboptimal = true;
230                }
231                Err(err) => {
232                    // Probably:
233                    // VK_ERROR_OUT_OF_HOST_MEMORY
234                    // VK_ERROR_OUT_OF_DEVICE_MEMORY
235                    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                // TODO: Maybe handle this case with aspect-correct clamping?
280                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    /// Sets information about this swapchain.
398    ///
399    /// Previously acquired swapchain images should be discarded after calling this function.
400    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/// Describes the condition of a swapchain.
463#[derive(Clone, Copy, Debug, PartialEq)]
464pub enum SwapchainError {
465    /// This frame is lost but more may be acquired later.
466    DeviceLost,
467
468    /// This frame is not lost but there may be a delay while the next frame is recreated.
469    Suboptimal,
470
471    /// The surface was lost and must be recreated, which includes any operating system window.
472    SurfaceLost,
473}
474
475/// An opaque type representing a swapchain image.
476#[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/// Information used to create a [`Swapchain`] instance.
508#[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    /// The desired, but not guaranteed, number of images that will be in the created swapchain.
517    ///
518    /// More images introduces more display lag, but smoother animation.
519    #[builder(default = "3")]
520    pub desired_image_count: u32,
521
522    /// The initial height of the surface.
523    pub height: u32,
524
525    /// The format and color space of the surface.
526    pub surface: vk::SurfaceFormatKHR,
527
528    /// Determines if frames will be submitted to the display in a synchronous fashion or if they
529    /// should be displayed as fast as possible instead.
530    ///
531    /// Turn on to eliminate visual tearing at the expense of latency.
532    #[builder(default = "true")]
533    pub sync_display: bool,
534
535    /// The initial width of the surface.
536    pub width: u32,
537}
538
539impl SwapchainInfo {
540    /// Specifies a default swapchain with the given `width`, `height` and `format` values.
541    #[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    /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`.
553    #[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    /// Builds a new `SwapchainInfo`.
573    ///
574    /// # Panics
575    ///
576    /// If any of the following values have not been set this function will panic:
577    ///
578    /// * `width`
579    /// * `height`
580    /// * `surface`
581    #[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}