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        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    /// Sets information about this swapchain.
396    ///
397    /// Previously acquired swapchain images should be discarded after calling this function.
398    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        // On mesa the device will return this usage flag as supported even when the extension
445        // that is needed for an image to have this flag isn't enabled
446        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/// Describes the condition of a swapchain.
465#[derive(Clone, Copy, Debug, PartialEq)]
466pub enum SwapchainError {
467    /// This frame is lost but more may be acquired later.
468    DeviceLost,
469
470    /// This frame is not lost but there may be a delay while the next frame is recreated.
471    Suboptimal,
472
473    /// The surface was lost and must be recreated, which includes any operating system window.
474    SurfaceLost,
475}
476
477/// An opaque type representing a swapchain image.
478#[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/// Information used to create a [`Swapchain`] instance.
510#[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    /// The desired, but not guaranteed, number of images that will be in the created swapchain.
519    ///
520    /// More images introduces more display lag, but smoother animation.
521    #[builder(default = "3")]
522    pub desired_image_count: u32,
523
524    /// The initial height of the surface.
525    pub height: u32,
526
527    /// The format and color space of the surface.
528    pub surface: vk::SurfaceFormatKHR,
529
530    /// Determines if frames will be submitted to the display in a synchronous fashion or if they
531    /// should be displayed as fast as possible instead.
532    ///
533    /// Turn on to eliminate visual tearing at the expense of latency.
534    #[builder(default = "true")]
535    pub sync_display: bool,
536
537    /// The initial width of the surface.
538    pub width: u32,
539}
540
541impl SwapchainInfo {
542    /// Specifies a default swapchain with the given `width`, `height` and `format` values.
543    #[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    /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`.
555    #[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    /// Builds a new `SwapchainInfo`.
575    ///
576    /// # Panics
577    ///
578    /// If any of the following values have not been set this function will panic:
579    ///
580    /// * `width`
581    /// * `height`
582    /// * `surface`
583    #[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}