ash_bootstrap/
swapchain.rs

1use crate::Device;
2use crate::Instance;
3use crate::device::QueueType;
4use crate::error::FormatError;
5use ash::vk::{AllocationCallbacks, Handle, SwapchainKHR};
6use ash::{khr, vk};
7use std::ops::Deref;
8use std::sync::atomic::{AtomicU64, Ordering};
9use std::sync::{Arc, Mutex};
10
11#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
12enum Priority {
13    Main,
14    Fallback,
15}
16
17#[derive(Debug, Clone)]
18struct Format<'a> {
19    inner: vk::SurfaceFormat2KHR<'a>,
20    priority: Priority,
21}
22
23#[derive(Debug, Clone)]
24struct PresentMode {
25    inner: vk::PresentModeKHR,
26    priority: Priority,
27}
28
29pub struct SwapchainBuilder {
30    instance: Arc<Instance>,
31    device: Arc<Device>,
32    swapchain_device: Arc<khr::swapchain::Device>,
33    allocation_callbacks: Option<AllocationCallbacks<'static>>,
34    desired_formats: Vec<Format<'static>>,
35    create_flags: vk::SwapchainCreateFlagsKHR,
36    desired_width: u32,
37    desired_height: u32,
38    array_layer_count: u32,
39    min_image_count: u32,
40    required_min_image_count: u32,
41    image_usage_flags: vk::ImageUsageFlags,
42    composite_alpha_flags_khr: vk::CompositeAlphaFlagsKHR,
43    desired_present_modes: Vec<PresentMode>,
44    pre_transform: vk::SurfaceTransformFlagsKHR,
45    clipped: bool,
46    old_swapchain: AtomicU64,
47    graphics_queue_index: usize,
48    present_queue_index: usize,
49}
50
51struct SurfaceFormatDetails {
52    capabilities: vk::SurfaceCapabilitiesKHR,
53    formats: Vec<vk::SurfaceFormatKHR>,
54    present_modes: Vec<vk::PresentModeKHR>,
55}
56
57fn query_surface_support_details(
58    phys_device: vk::PhysicalDevice,
59    surface_instance: Option<&khr::surface::Instance>,
60    surface: Option<vk::SurfaceKHR>,
61) -> crate::Result<SurfaceFormatDetails> {
62    let Some((surface_instance, surface)) = surface_instance.zip(surface) else {
63        return Err(crate::SwapchainError::SurfaceHandleNotProvided.into());
64    };
65
66    let capabilities =
67        unsafe { surface_instance.get_physical_device_surface_capabilities(phys_device, surface) }?;
68    let formats =
69        unsafe { surface_instance.get_physical_device_surface_formats(phys_device, surface) }?;
70    let present_modes = unsafe {
71        surface_instance.get_physical_device_surface_present_modes(phys_device, surface)
72    }?;
73
74    Ok(SurfaceFormatDetails {
75        capabilities,
76        formats,
77        present_modes,
78    })
79}
80
81fn default_formats<'a>() -> Vec<Format<'a>> {
82    vec![
83        Format {
84            inner: vk::SurfaceFormat2KHR::default().surface_format(
85                vk::SurfaceFormatKHR::default()
86                    .format(vk::Format::B8G8R8A8_SRGB)
87                    .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
88            ),
89            priority: Priority::Main,
90        },
91        Format {
92            inner: vk::SurfaceFormat2KHR::default().surface_format(
93                vk::SurfaceFormatKHR::default()
94                    .format(vk::Format::R8G8B8_SRGB)
95                    .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
96            ),
97            priority: Priority::Fallback,
98        },
99    ]
100}
101
102fn default_present_modes() -> Vec<PresentMode> {
103    vec![
104        PresentMode {
105            inner: vk::PresentModeKHR::MAILBOX,
106            priority: Priority::Main,
107        },
108        PresentMode {
109            inner: vk::PresentModeKHR::FIFO,
110            priority: Priority::Fallback,
111        },
112    ]
113}
114
115fn find_desired_surface_format(
116    available: &[vk::SurfaceFormatKHR],
117    desired: &mut [Format],
118) -> crate::Result<vk::SurfaceFormatKHR> {
119    if !desired.is_sorted_by_key(|f| f.priority.clone()) {
120        desired.sort_unstable_by_key(|f| f.priority.clone());
121    }
122
123    for desired in desired.iter() {
124        for available in available {
125            if desired.inner.surface_format.format == available.format
126                && desired.inner.surface_format.color_space == available.color_space
127            {
128                return Ok(desired.inner.surface_format);
129            }
130        }
131    }
132
133    Err(crate::SwapchainError::NoSuitableDesiredFormat(FormatError {
134        available: available.to_vec(),
135        desired: desired.iter().map(|d| d.inner.surface_format).collect(),
136    })
137    .into())
138}
139
140fn find_best_surface_format(
141    available: &[vk::SurfaceFormatKHR],
142    desired: &mut [Format],
143) -> vk::SurfaceFormatKHR {
144    find_desired_surface_format(available, desired).unwrap_or(available[0])
145}
146
147fn find_present_mode(
148    available: &[vk::PresentModeKHR],
149    desired: &mut [PresentMode],
150) -> vk::PresentModeKHR {
151    if !desired.is_sorted_by_key(|f| f.priority.clone()) {
152        desired.sort_unstable_by_key(|f| f.priority.clone());
153    }
154
155    for desired in desired {
156        for available in available {
157            if &desired.inner == available {
158                return *available;
159            }
160        }
161    }
162
163    vk::PresentModeKHR::FIFO
164}
165
166impl SwapchainBuilder {
167    fn find_extent(&self, capabilities: &vk::SurfaceCapabilitiesKHR) -> vk::Extent2D {
168        if capabilities.current_extent.width != u32::MAX {
169            capabilities.current_extent
170        } else {
171            let mut actual_extent = vk::Extent2D::default()
172                .width(self.desired_width)
173                .height(self.desired_height);
174
175            actual_extent.width = capabilities
176                .min_image_extent
177                .width
178                .max(capabilities.max_image_extent.width.min(actual_extent.width));
179            actual_extent.height = capabilities.min_image_extent.height.max(
180                capabilities
181                    .max_image_extent
182                    .height
183                    .min(actual_extent.height),
184            );
185
186            actual_extent
187        }
188    }
189
190    pub fn new(instance: Arc<Instance>, device: Arc<Device>) -> Self {
191        Self {
192            graphics_queue_index: device.get_queue(QueueType::Graphics).unwrap().0,
193            present_queue_index: device.get_queue(QueueType::Present).unwrap().0,
194            swapchain_device: Arc::new(khr::swapchain::Device::new(
195                &instance.instance,
196                device.as_ref().as_ref(),
197            )),
198            instance,
199            device,
200            allocation_callbacks: None,
201            desired_formats: Vec::with_capacity(4),
202            create_flags: vk::SwapchainCreateFlagsKHR::default(),
203            desired_width: 256,
204            desired_height: 256,
205            array_layer_count: 1,
206            min_image_count: 0,
207            required_min_image_count: 0,
208            image_usage_flags: vk::ImageUsageFlags::COLOR_ATTACHMENT,
209            pre_transform: vk::SurfaceTransformFlagsKHR::default(),
210            desired_present_modes: Vec::with_capacity(4),
211            composite_alpha_flags_khr: vk::CompositeAlphaFlagsKHR::OPAQUE,
212            clipped: true,
213            old_swapchain: Default::default(),
214        }
215    }
216
217    pub fn desired_format(mut self, format: vk::SurfaceFormat2KHR<'static>) -> Self {
218        self.desired_formats.push(Format {
219            inner: format,
220            priority: Priority::Main,
221        });
222        self
223    }
224
225    pub fn desired_size(mut self, size: vk::Extent2D) -> Self {
226        self.desired_width = size.width;
227        self.desired_height = size.height;
228        self
229    }
230
231    pub fn fallback_format(mut self, format: vk::SurfaceFormat2KHR<'static>) -> Self {
232        self.desired_formats.push(Format {
233            inner: format,
234            priority: Priority::Fallback,
235        });
236        self
237    }
238
239    /// Use the default swapchain formats. This is done if no formats are provided.
240    ///
241    /// Default surface format is [
242    ///     [`vk::Format::B8G8R8A8_SRGB`],
243    ///     [`vk::ColorSpaceKHR::SRGB_NONLINEAR`]
244    /// ]
245    pub fn use_default_format_selection(mut self) -> Self {
246        self.desired_formats = default_formats();
247        self
248    }
249
250    pub fn desired_present_mode(mut self, present_mode: vk::PresentModeKHR) -> Self {
251        self.desired_present_modes.push(PresentMode {
252            inner: present_mode,
253            priority: Priority::Main,
254        });
255        self
256    }
257
258    pub fn fallback_present_mode(mut self, present_mode: vk::PresentModeKHR) -> Self {
259        self.desired_present_modes.push(PresentMode {
260            inner: present_mode,
261            priority: Priority::Fallback,
262        });
263        self
264    }
265
266    pub fn use_default_present_modes(mut self) -> Self {
267        self.desired_present_modes = default_present_modes();
268        self
269    }
270
271    /// Sets the desired minimum image count for the swapchain.
272    /// Note that the presentation engine is always free to create more images than requested.
273    /// You may pass one of the values specified in the BufferMode enum, or any integer value.
274    /// For instance, if you pass DOUBLE_BUFFERING, the presentation engine is allowed to give you a double buffering setup,
275    /// triple buffering, or more. This is up to the drivers.
276    pub fn desired_min_image_count(mut self, min_image_count: u32) -> Self {
277        self.min_image_count = min_image_count;
278        self
279    }
280
281    /// Set whether the Vulkan implementation is allowed to discard rendering operations that
282    /// affect regions of the surface that are not visible. Default is true.
283    /// # Note:
284    /// Applications should use the default of true if:
285    /// - They do not expect to read back the content of presentable images before presenting them or after reacquiring them
286    /// - If their fragment shaders do not have any side effects that require them to run for all pixels in the presentable image.
287    pub fn clipped(mut self, clipped: bool) -> Self {
288        self.clipped = clipped;
289        self
290    }
291
292    pub fn create_flags(mut self, flags: vk::SwapchainCreateFlagsKHR) -> Self {
293        self.create_flags = flags;
294        self
295    }
296
297    /// Set the bitmask of the image usage for acquired swapchain images.
298    /// If the surface capabilities cannot allow it, building the swapchain will result in the `SwapchainError::required_usage_not_supported` error.
299    pub fn image_usage_flags(mut self, flags: vk::ImageUsageFlags) -> Self {
300        self.image_usage_flags = flags;
301        self
302    }
303
304    /// Add a image usage to the bitmask for acquired swapchain images.
305    pub fn add_image_usage_flags(mut self, flags: vk::ImageUsageFlags) -> Self {
306        self.image_usage_flags |= flags;
307        self
308    }
309
310    pub fn allocation_callbacks(
311        mut self,
312        allocation_callbacks: AllocationCallbacks<'static>,
313    ) -> Self {
314        self.allocation_callbacks = Some(allocation_callbacks);
315        self
316    }
317
318    /// This method should be called with previously created [`Swapchain`].
319    ///
320    /// # Note:
321    /// This method will mark old swapchain and destroy it when creating a new one.
322    pub fn set_old_swapchain(&self, swapchain: Swapchain) {
323        if swapchain.destroy_image_views().is_err() {
324            #[cfg(feature = "enable_tracing")]
325            tracing::warn!("Could not destroy swapchain image views");
326            return;
327        };
328        self.old_swapchain
329            .store(swapchain.swapchain.as_raw(), Ordering::Relaxed);
330    }
331
332    pub fn build(&self) -> crate::Result<Swapchain> {
333        if self.instance.surface.is_none() {
334            return Err(crate::SwapchainError::SurfaceHandleNotProvided.into());
335        };
336
337        let mut desired_formats = self.desired_formats.clone();
338        if desired_formats.is_empty() {
339            desired_formats = default_formats();
340        };
341
342        let mut desired_present_modes = self.desired_present_modes.clone();
343        if desired_present_modes.is_empty() {
344            desired_present_modes = default_present_modes();
345        }
346
347        let surface_support = query_surface_support_details(
348            *self.device.physical_device().as_ref(),
349            self.instance.surface_instance.as_ref(),
350            self.instance.surface,
351        )?;
352
353        let mut image_count = self.min_image_count;
354        if image_count >= 1 {
355            if self.required_min_image_count < surface_support.capabilities.min_image_count {
356                return Err(crate::SwapchainError::RequiredMinImageCountTooLow.into());
357            }
358
359            image_count = surface_support.capabilities.min_image_count;
360        } else if image_count == 0 {
361            // We intentionally use minImageCount + 1 to maintain existing behavior,
362            // even if it typically results in triple buffering on most systems.
363            image_count = surface_support.capabilities.min_image_count + 1;
364        } else if image_count < surface_support.capabilities.min_image_count {
365            image_count = surface_support.capabilities.min_image_count
366        }
367
368        if surface_support.capabilities.max_image_count > 0
369            && image_count > surface_support.capabilities.max_image_count
370        {
371            image_count = surface_support.capabilities.max_image_count;
372        }
373
374        let surface_format =
375            find_best_surface_format(&surface_support.formats, &mut desired_formats);
376
377        let extent = self.find_extent(&surface_support.capabilities);
378
379        let mut image_array_layers = self.array_layer_count;
380        if surface_support.capabilities.max_image_array_layers < image_array_layers {
381            image_array_layers = surface_support.capabilities.max_image_array_layers;
382        }
383        if image_array_layers == 0 {
384            image_array_layers = 1;
385        }
386
387        let present_mode =
388            find_present_mode(&surface_support.present_modes, &mut desired_present_modes);
389
390        let is_unextended_present_mode =
391            matches!(
392                present_mode,
393                |vk::PresentModeKHR::IMMEDIATE| vk::PresentModeKHR::MAILBOX
394                    | vk::PresentModeKHR::FIFO
395                    | vk::PresentModeKHR::FIFO_RELAXED
396            );
397
398        if is_unextended_present_mode
399            && !surface_support
400                .capabilities
401                .supported_usage_flags
402                .contains(self.image_usage_flags)
403        {
404            return Err(crate::SwapchainError::RequiredUsageNotSupported.into());
405        };
406
407        let mut pre_transform = self.pre_transform;
408        if pre_transform == vk::SurfaceTransformFlagsKHR::default() {
409            pre_transform = surface_support.capabilities.current_transform;
410        }
411
412        let old_swapchain = self.old_swapchain.load(Ordering::Relaxed);
413
414        let mut swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
415            .flags(self.create_flags)
416            .surface(self.instance.surface.unwrap())
417            .min_image_count(image_count)
418            .image_format(surface_format.format)
419            .image_color_space(surface_format.color_space)
420            .image_extent(extent)
421            .image_array_layers(image_array_layers)
422            .image_usage(self.image_usage_flags)
423            .pre_transform(pre_transform)
424            .composite_alpha(self.composite_alpha_flags_khr)
425            .present_mode(present_mode)
426            .clipped(self.clipped)
427            .old_swapchain(SwapchainKHR::from_raw(old_swapchain));
428
429        let queue_family_indices = [
430            self.graphics_queue_index as _,
431            self.present_queue_index as _,
432        ];
433
434        if self.graphics_queue_index != self.present_queue_index {
435            swapchain_create_info.image_sharing_mode = vk::SharingMode::CONCURRENT;
436            swapchain_create_info =
437                swapchain_create_info.queue_family_indices(&queue_family_indices);
438        } else {
439            swapchain_create_info.image_sharing_mode = vk::SharingMode::EXCLUSIVE;
440        }
441
442        let swapchain = unsafe {
443            self.swapchain_device
444                .create_swapchain(&swapchain_create_info, self.allocation_callbacks.as_ref())
445        }
446        .map_err(|_| crate::SwapchainError::FailedCreateSwapchain)?;
447
448        if old_swapchain != 0 {
449            unsafe {
450                self.swapchain_device.destroy_swapchain(
451                    SwapchainKHR::from_raw(old_swapchain),
452                    self.allocation_callbacks.as_ref(),
453                )
454            }
455        }
456
457        Ok(Swapchain {
458            device: self.device.clone(),
459            swapchain,
460            extent,
461            swapchain_device: self.swapchain_device.clone(),
462            image_format: surface_format.format,
463            image_usage_flags: self.image_usage_flags,
464            instance_version: self.instance.instance_version,
465            allocation_callbacks: self.allocation_callbacks,
466            image_views: Mutex::new(Vec::with_capacity(image_count as _)),
467        })
468    }
469}
470
471pub struct Swapchain {
472    device: Arc<Device>,
473    swapchain: vk::SwapchainKHR,
474    swapchain_device: Arc<khr::swapchain::Device>,
475    pub image_format: vk::Format,
476    pub extent: vk::Extent2D,
477    image_usage_flags: vk::ImageUsageFlags,
478    instance_version: u32,
479    allocation_callbacks: Option<AllocationCallbacks<'static>>,
480    image_views: Mutex<Vec<vk::ImageView>>,
481}
482
483impl Swapchain {
484    pub fn get_images(&self) -> crate::Result<Vec<vk::Image>> {
485        let images = unsafe { self.swapchain_device.get_swapchain_images(self.swapchain) }?;
486
487        Ok(images)
488    }
489
490    pub fn destroy_image_views(&self) -> crate::Result<()> {
491        let mut image_views = self.image_views.lock().unwrap();
492
493        for image_view in image_views.drain(..) {
494            unsafe {
495                self.device
496                    .device()
497                    .destroy_image_view(image_view, self.allocation_callbacks.as_ref())
498            }
499        }
500
501        Ok(())
502    }
503
504    pub fn get_image_views(&self) -> crate::Result<Vec<vk::ImageView>> {
505        let images = self.get_images()?;
506
507        let mut desired_flags =
508            vk::ImageViewUsageCreateInfo::default().usage(self.image_usage_flags);
509
510        let views: Vec<_> = images
511            .into_iter()
512            .map(|image| {
513                let mut create_info = vk::ImageViewCreateInfo::default();
514
515                if self.instance_version >= vk::API_VERSION_1_1 {
516                    create_info = create_info.push_next(&mut desired_flags);
517                }
518
519                create_info.image = image;
520                create_info.view_type = vk::ImageViewType::TYPE_2D;
521                create_info.format = self.image_format;
522                create_info.components.r = vk::ComponentSwizzle::IDENTITY;
523                create_info.components.g = vk::ComponentSwizzle::IDENTITY;
524                create_info.components.b = vk::ComponentSwizzle::IDENTITY;
525                create_info.components.a = vk::ComponentSwizzle::IDENTITY;
526                create_info.subresource_range.aspect_mask = vk::ImageAspectFlags::COLOR;
527                create_info.subresource_range.base_mip_level = 0;
528                create_info.subresource_range.level_count = 1;
529                create_info.subresource_range.base_array_layer = 0;
530                create_info.subresource_range.layer_count = 1;
531
532                unsafe {
533                    self.device
534                        .device()
535                        .create_image_view(&create_info, self.allocation_callbacks.as_ref())
536                }
537                .map_err(Into::into)
538            })
539            .collect::<crate::Result<_>>()?;
540
541        {
542            let mut image_views = self.image_views.lock().unwrap();
543            *image_views = views.clone();
544        }
545
546        Ok(views)
547    }
548
549    pub fn destroy(&self) {
550        unsafe {
551            self.swapchain_device
552                .destroy_swapchain(self.swapchain, self.allocation_callbacks.as_ref())
553        };
554    }
555}
556
557impl AsRef<SwapchainKHR> for Swapchain {
558    fn as_ref(&self) -> &SwapchainKHR {
559        &self.swapchain
560    }
561}
562
563impl Deref for Swapchain {
564    type Target = khr::swapchain::Device;
565
566    fn deref(&self) -> &Self::Target {
567        &self.swapchain_device
568    }
569}