bort_vk/
swapchain.rs

1use crate::{
2    default_component_mapping, default_subresource_range, extent_2d_from_width_height, Device,
3    DeviceOwned, Fence, ImageAccess, ImageDimensions, ImageViewProperties, Queue, Semaphore,
4    Surface, ALLOCATION_CALLBACK_NONE,
5};
6use ash::{
7    extensions::khr,
8    prelude::VkResult,
9    vk::{self, Handle},
10};
11use std::{
12    cmp::{max, min},
13    error, fmt,
14    sync::Arc,
15};
16
17// Swapchain
18
19pub struct Swapchain {
20    handle: vk::SwapchainKHR,
21    swapchain_loader: khr::Swapchain,
22    properties: SwapchainProperties,
23    swapchain_images: Vec<Arc<SwapchainImage>>,
24
25    // dependencies
26    device: Arc<Device>,
27    surface: Arc<Surface>,
28}
29
30impl Swapchain {
31    pub fn new(
32        device: Arc<Device>,
33        surface: Arc<Surface>,
34        properties: SwapchainProperties,
35    ) -> Result<Self, SwapchainError> {
36        let swapchain_loader = khr::Swapchain::new(device.instance().inner(), device.inner());
37
38        let swapchain_create_info_builder =
39            properties.create_info_builder(surface.handle(), vk::SwapchainKHR::null());
40        let handle = unsafe {
41            swapchain_loader
42                .create_swapchain(&swapchain_create_info_builder, ALLOCATION_CALLBACK_NONE)
43        }
44        .map_err(|e| SwapchainError::Creation(e))?;
45
46        let vk_swapchain_images = unsafe { swapchain_loader.get_swapchain_images(handle) }
47            .map_err(|e| SwapchainError::GetSwapchainImages(e))?;
48
49        let swapchain_images: Vec<Arc<SwapchainImage>> = vk_swapchain_images
50            .into_iter()
51            .map(|image_handle| unsafe {
52                Arc::new(SwapchainImage::from_image_handle(
53                    device.clone(),
54                    image_handle,
55                    &properties,
56                ))
57            })
58            .collect();
59
60        Ok(Self {
61            handle,
62            swapchain_loader,
63            properties,
64            swapchain_images,
65
66            device,
67            surface,
68        })
69    }
70
71    /// On success, returns the next image's index and whether the swapchain is suboptimal for the surface.
72    pub fn aquire_next_image(
73        &self,
74        timeout: u64,
75        semaphore: Option<&Semaphore>,
76        fence: Option<&Fence>,
77    ) -> VkResult<(u32, bool)> {
78        let semaphore_handle = if let Some(semaphore) = semaphore {
79            semaphore.handle()
80        } else {
81            vk::Semaphore::null()
82        };
83        let fence_handle = if let Some(fence) = fence {
84            fence.handle()
85        } else {
86            vk::Fence::null()
87        };
88
89        unsafe {
90            self.swapchain_loader.acquire_next_image(
91                self.handle,
92                timeout,
93                semaphore_handle,
94                fence_handle,
95            )
96        }
97    }
98
99    /// Also destroys the old swapchain so make sure any resources depending on the swapchain and
100    /// swapchain images are dropped before calling this! E.g. swapchain image views and framebuffers...
101    pub fn recreate(&mut self, properties: SwapchainProperties) -> Result<(), SwapchainError> {
102        let (new_handle, swapchain_images) = Self::recreate_common(&self, &properties)?;
103
104        unsafe {
105            self.swapchain_loader
106                .destroy_swapchain(self.handle, ALLOCATION_CALLBACK_NONE)
107        };
108
109        self.handle = new_handle;
110        self.properties = properties;
111        self.swapchain_images = swapchain_images;
112
113        Ok(())
114    }
115
116    /// Same as `Self::recreate` but consumes an immutable (reference counted) `Swapchain` and
117    /// returns a new `Swapchain`.
118    ///
119    /// Unlike `Self::recreate` this doesn't destroy the old swapchain. If you want it to be cleaned
120    /// up, it must be dropped which requires any resources depending on the swapchain/swapchain images
121    /// to be dropped e.g. swapchain image views and framebuffers...
122    pub fn recreate_replace(
123        self: &Arc<Self>,
124        properties: SwapchainProperties,
125    ) -> Result<Arc<Self>, SwapchainError> {
126        let (new_handle, swapchain_images) = Self::recreate_common(&self, &properties)?;
127
128        Ok(Arc::new(Self {
129            handle: new_handle,
130            swapchain_loader: self.swapchain_loader.clone(),
131            properties,
132            swapchain_images,
133            device: self.device.clone(),
134            surface: self.surface.clone(),
135        }))
136    }
137
138    fn recreate_common(
139        self: &Self,
140        properties: &SwapchainProperties,
141    ) -> Result<(vk::SwapchainKHR, Vec<Arc<SwapchainImage>>), SwapchainError> {
142        let swapchain_create_info_builder =
143            properties.create_info_builder(self.surface.handle(), self.handle);
144
145        let new_handle = unsafe {
146            self.swapchain_loader
147                .create_swapchain(&swapchain_create_info_builder, ALLOCATION_CALLBACK_NONE)
148        }
149        .map_err(|e| SwapchainError::Creation(e))?;
150
151        let vk_swapchain_images = unsafe { self.swapchain_loader.get_swapchain_images(new_handle) }
152            .map_err(|e| SwapchainError::GetSwapchainImages(e))?;
153
154        let swapchain_images: Vec<Arc<SwapchainImage>> = vk_swapchain_images
155            .into_iter()
156            .map(|image_handle| unsafe {
157                Arc::new(SwapchainImage::from_image_handle(
158                    self.device.clone(),
159                    image_handle,
160                    &properties,
161                ))
162            })
163            .collect();
164
165        Ok((new_handle, swapchain_images))
166    }
167
168    pub fn image_view_properties(&self) -> ImageViewProperties {
169        let format = self.properties().surface_format.format;
170        let component_mapping = default_component_mapping();
171
172        let layer_count = self.properties().array_layers;
173        let view_type = if layer_count > 1 {
174            vk::ImageViewType::TYPE_2D_ARRAY
175        } else {
176            vk::ImageViewType::TYPE_2D
177        };
178        let subresource_range = vk::ImageSubresourceRange {
179            layer_count,
180            ..default_subresource_range(vk::ImageAspectFlags::COLOR)
181        };
182
183        ImageViewProperties {
184            format,
185            view_type,
186            component_mapping,
187            subresource_range,
188            ..ImageViewProperties::default()
189        }
190    }
191
192    pub fn queue_present(
193        &self,
194        queue: &Queue,
195        present_info: &vk::PresentInfoKHRBuilder,
196    ) -> VkResult<bool> {
197        unsafe {
198            self.swapchain_loader
199                .queue_present(queue.handle(), present_info)
200        }
201    }
202
203    // Getters
204
205    #[inline]
206    pub fn handle(&self) -> vk::SwapchainKHR {
207        self.handle
208    }
209
210    #[inline]
211    pub fn swapchain_loader(&self) -> &khr::Swapchain {
212        &self.swapchain_loader
213    }
214
215    #[inline]
216    pub fn properties(&self) -> &SwapchainProperties {
217        &self.properties
218    }
219
220    #[inline]
221    pub fn surface(&self) -> &Arc<Surface> {
222        &self.surface
223    }
224
225    #[inline]
226    pub fn swapchain_images(&self) -> &Vec<Arc<SwapchainImage>> {
227        &self.swapchain_images
228    }
229}
230
231impl DeviceOwned for Swapchain {
232    #[inline]
233    fn device(&self) -> &Arc<Device> {
234        &self.device
235    }
236
237    #[inline]
238    fn handle_raw(&self) -> u64 {
239        self.handle.as_raw()
240    }
241}
242
243impl Drop for Swapchain {
244    fn drop(&mut self) {
245        unsafe {
246            self.swapchain_loader
247                .destroy_swapchain(self.handle, ALLOCATION_CALLBACK_NONE)
248        };
249    }
250}
251
252// Swapchain Properties
253
254/// WARNING when using `default()` the following values should be overridden:
255/// - `surface_format`
256/// - `dimensions`
257/// - `image_usage`
258/// - `pre_transform`
259/// - `composite_alpha`
260#[derive(Debug, Clone)]
261pub struct SwapchainProperties {
262    pub flags: vk::SwapchainCreateFlagsKHR,
263    pub image_count: u32,
264    pub pre_transform: vk::SurfaceTransformFlagsKHR,
265    pub composite_alpha: vk::CompositeAlphaFlagsKHR,
266    pub present_mode: vk::PresentModeKHR,
267    pub clipping_enabled: bool,
268
269    // image properties
270    pub surface_format: vk::SurfaceFormatKHR,
271    pub width_height: [u32; 2],
272    pub array_layers: u32,
273    pub image_usage: vk::ImageUsageFlags,
274    pub sharing_mode: vk::SharingMode,
275    pub queue_family_indices: Vec<u32>,
276}
277
278impl Default for SwapchainProperties {
279    fn default() -> Self {
280        Self {
281            image_count: 1,
282            array_layers: 1,
283            sharing_mode: vk::SharingMode::EXCLUSIVE,
284            queue_family_indices: Vec::new(),
285            clipping_enabled: true,
286            present_mode: vk::PresentModeKHR::MAILBOX,
287            flags: vk::SwapchainCreateFlagsKHR::empty(),
288
289            // nonsense defaults. make sure you override these!
290            surface_format: vk::SurfaceFormatKHR::default(),
291            width_height: [1, 1],
292            image_usage: vk::ImageUsageFlags::empty(),
293            pre_transform: vk::SurfaceTransformFlagsKHR::default(),
294            composite_alpha: vk::CompositeAlphaFlagsKHR::empty(),
295        }
296    }
297}
298
299impl SwapchainProperties {
300    /// Prefers the following settings:
301    /// - present mode = `vk::PresentModeKHR::MAILBOX`
302    /// - pre-transform = `vk::SurfaceTransformFlagsKHR::IDENTITY`
303    ///
304    /// `preferred_image_count` is clamped based on `vk::SurfaceCapabilitiesKHR`.
305    ///
306    /// `surface_format`, `composite_alpha` and `image_usage` are unchecked.
307    ///
308    /// Sharing mode is set to `vk::SharingMode::EXCLUSIVE`, only 1 array layer, and clipping is enabled.
309    pub fn new_default(
310        device: &Device,
311        surface: &Surface,
312        preferred_image_count: u32,
313        surface_format: vk::SurfaceFormatKHR,
314        composite_alpha: vk::CompositeAlphaFlagsKHR,
315        image_usage: vk::ImageUsageFlags,
316        window_dimensions: [u32; 2],
317    ) -> Result<Self, SwapchainError> {
318        let surface_capabilities = surface
319            .get_physical_device_surface_capabilities(device.physical_device())
320            .map_err(|e| SwapchainError::GetPhysicalDeviceSurfaceCapabilities(e))?;
321
322        let mut image_count = max(preferred_image_count, surface_capabilities.min_image_count);
323        // max_image_count == 0 when there is no upper limit
324        if surface_capabilities.max_image_count != 0 {
325            image_count = min(image_count, surface_capabilities.max_image_count);
326        }
327
328        let extent = match surface_capabilities.current_extent.width {
329            std::u32::MAX => vk::Extent2D {
330                width: window_dimensions[0],
331                height: window_dimensions[1],
332            },
333            _ => surface_capabilities.current_extent,
334        };
335
336        let present_modes = surface
337            .get_physical_device_surface_present_modes(device.physical_device())
338            .map_err(|e| SwapchainError::GetPhysicalDeviceSurfacePresentModes(e))?;
339        let present_mode = present_modes
340            .into_iter()
341            .find(|&mode| mode == vk::PresentModeKHR::MAILBOX)
342            .unwrap_or(vk::PresentModeKHR::FIFO);
343
344        // should think more about this if targeting mobile in the future...
345        let pre_transform = if surface_capabilities
346            .supported_transforms
347            .contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
348        {
349            vk::SurfaceTransformFlagsKHR::IDENTITY
350        } else {
351            surface_capabilities.current_transform
352        };
353
354        Ok(Self {
355            image_count,
356            surface_format,
357            width_height: [extent.width, extent.height],
358            image_usage,
359            sharing_mode: vk::SharingMode::EXCLUSIVE,
360            pre_transform,
361            composite_alpha,
362            present_mode,
363            clipping_enabled: true,
364            ..Self::default()
365        })
366    }
367
368    pub fn create_info_builder(
369        &self,
370        surface_handle: vk::SurfaceKHR,
371        old_swapchain_handle: vk::SwapchainKHR,
372    ) -> vk::SwapchainCreateInfoKHRBuilder {
373        vk::SwapchainCreateInfoKHR::builder()
374            .flags(self.flags)
375            .surface(surface_handle)
376            .min_image_count(self.image_count)
377            .image_format(self.surface_format.format)
378            .image_color_space(self.surface_format.color_space)
379            .image_extent(extent_2d_from_width_height(self.width_height))
380            .image_array_layers(self.array_layers)
381            .image_usage(self.image_usage)
382            .image_sharing_mode(self.sharing_mode)
383            .pre_transform(self.pre_transform)
384            .composite_alpha(self.composite_alpha)
385            .present_mode(self.present_mode)
386            .clipped(self.clipping_enabled)
387            .old_swapchain(old_swapchain_handle)
388            .queue_family_indices(&self.queue_family_indices)
389    }
390
391    pub fn dimensions(&self) -> ImageDimensions {
392        ImageDimensions::Dim2d {
393            width: self.width_height[0],
394            height: self.width_height[1],
395            array_layers: 1,
396        }
397    }
398}
399
400// Swapchain Image
401
402pub struct SwapchainImage {
403    handle: vk::Image,
404    dimensions: ImageDimensions,
405
406    // dependencies
407    device: Arc<Device>,
408}
409
410impl SwapchainImage {
411    /// Safety: make sure image 'handle' was retreived from 'swapchain'
412    pub(crate) unsafe fn from_image_handle(
413        device: Arc<Device>,
414        handle: vk::Image,
415        swapchain_properties: &SwapchainProperties,
416    ) -> Self {
417        Self {
418            handle,
419            dimensions: swapchain_properties.dimensions(),
420            device,
421        }
422    }
423
424    // Getters
425
426    #[inline]
427    pub fn dimensions(&self) -> ImageDimensions {
428        self.dimensions
429    }
430}
431
432impl ImageAccess for SwapchainImage {
433    #[inline]
434    fn handle(&self) -> vk::Image {
435        self.handle
436    }
437
438    #[inline]
439    fn dimensions(&self) -> ImageDimensions {
440        self.dimensions
441    }
442}
443
444impl DeviceOwned for SwapchainImage {
445    #[inline]
446    fn device(&self) -> &Arc<Device> {
447        &self.device
448    }
449
450    #[inline]
451    fn handle_raw(&self) -> u64 {
452        self.handle.as_raw()
453    }
454}
455
456// Helper
457
458/// Checks surface support for the first compositie alpha flag in order of preference:
459/// 1. `vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED`
460/// 2. `vk::CompositeAlphaFlagsKHR::OPAQUE`
461/// 3. `vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED` (because cbf implimenting the logic for this)
462/// 4. `vk::CompositeAlphaFlagsKHR::INHERIT` (noooope)
463pub fn choose_composite_alpha(
464    surface_capabilities: vk::SurfaceCapabilitiesKHR,
465) -> vk::CompositeAlphaFlagsKHR {
466    let supported_composite_alpha = surface_capabilities.supported_composite_alpha;
467    let composite_alpha_preference_order = [
468        vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
469        vk::CompositeAlphaFlagsKHR::OPAQUE,
470        vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
471        vk::CompositeAlphaFlagsKHR::INHERIT,
472    ];
473    composite_alpha_preference_order
474        .into_iter()
475        .find(|&ca| supported_composite_alpha.contains(ca))
476        .expect("driver should support at least one type of composite alpha!")
477}
478
479#[derive(Debug, Clone)]
480pub enum SwapchainError {
481    GetPhysicalDeviceSurfaceCapabilities(vk::Result),
482    GetPhysicalDeviceSurfacePresentModes(vk::Result),
483    Creation(vk::Result),
484    GetSwapchainImages(vk::Result),
485}
486
487impl fmt::Display for SwapchainError {
488    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489        match self {
490            Self::GetPhysicalDeviceSurfaceCapabilities(e) => write!(
491                f,
492                "call to vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: {}",
493                e
494            ),
495            Self::GetPhysicalDeviceSurfacePresentModes(e) => write!(
496                f,
497                "call to vkGetPhysicalDeviceSurfacePresentModesKHR failed: {}",
498                e
499            ),
500            Self::Creation(e) => write!(f, "failed to create swapchain: {}", e),
501
502            Self::GetSwapchainImages(e) => {
503                write!(f, "call to vkGetSwapchainImagesKHR failed: {}", e)
504            }
505        }
506    }
507}
508
509impl error::Error for SwapchainError {
510    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
511        match self {
512            Self::GetPhysicalDeviceSurfaceCapabilities(e) => Some(e),
513            Self::GetPhysicalDeviceSurfacePresentModes(e) => Some(e),
514            Self::Creation(e) => Some(e),
515            Self::GetSwapchainImages(e) => Some(e),
516        }
517    }
518}