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 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 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 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 pub fn image_usage_flags(mut self, flags: vk::ImageUsageFlags) -> Self {
305 self.image_usage_flags = flags;
306 self
307 }
308
309 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 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 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 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}