vulkanalia_bootstrap/
swapchain.rs

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