plate/
swapchain.rs

1use std::sync::Arc;
2
3use ash::{extensions::khr, vk};
4
5use crate::{Device, sync::*, image::*, Format, Error, Surface};
6
7/// Errors from the swapchain module.
8#[derive(thiserror::Error, Debug)]
9pub enum SwapchainError {
10    /// None of the available image formats match the depth requirements.
11    #[error("No suitable depth format is available")]
12    NoSuitableDepthFormat,
13}
14
15/// The Swapchain is responsible for providing images to be rendered to the screen.
16pub struct Swapchain {
17    device: Arc<Device>,
18    #[allow(dead_code)]
19    surface: Surface,
20
21    swapchain_loader: khr::Swapchain,
22    swapchain: vk::SwapchainKHR,
23
24    extent: vk::Extent2D,
25
26    pub images: Vec<Image>,
27    pub surface_format: Format,
28    pub depth_format: Format,
29}
30
31impl Drop for Swapchain {
32    fn drop(&mut self) {
33        unsafe {
34            self.swapchain_loader
35                .destroy_swapchain(self.swapchain, None);
36        }
37    }
38}
39
40impl Swapchain {
41    /// Creates a Swapchain.
42    ///
43    /// # Examples
44    /// 
45    /// ```no_run
46    /// # struct Vertex(f32);
47    /// # let event_loop = winit::event_loop::EventLoop::new();
48    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
49    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
50    /// let swapchain = plate::Swapchain::new(&device, &window)?;
51    /// # Ok::<(), Box<dyn std::error::Error>>(())
52    /// ```
53    pub fn new(
54        device: &Arc<Device>,
55        window: &winit::window::Window,
56    ) -> Result<Self, Error> {
57        let surface = Surface::new(&device.instance, &window)?;
58
59        let (
60            swapchain_loader,
61            swapchain,
62            extent,
63            images,
64            surface_format,
65            depth_format,
66        ) = Self::create_swapchain(device, &surface, window, None)?;
67
68        Ok(Self {
69            device: Arc::clone(&device),
70            surface,
71            swapchain_loader,
72            swapchain,
73            extent,
74            images,
75            surface_format,
76            depth_format,
77        })
78    }
79
80    /// Recreates the swapchain.
81    ///
82    /// Sould be called if the window was resized or the surface format has changed.
83    ///
84    /// # Examples
85    /// 
86    /// ```no_run
87    /// # struct Vertex(f32);
88    /// # let event_loop = winit::event_loop::EventLoop::new();
89    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
90    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
91    /// let mut swapchain = plate::Swapchain::new(&device, &window)?;
92    /// swapchain.recreate(&window)?;
93    /// # Ok::<(), Box<dyn std::error::Error>>(())
94    /// ```
95    pub fn recreate(&mut self, window: &winit::window::Window) -> Result<(), Error> {
96        self.device.wait_idle()?;
97        
98        let (
99            swapchain_loader,
100            swapchain,
101            extent,
102            images,
103            surface_format,
104            depth_format,
105        ) = Self::create_swapchain(&self.device, &self.surface, window, Some(self.swapchain))?;
106
107        unsafe {
108            self.swapchain_loader
109                .destroy_swapchain(self.swapchain, None);
110        }
111
112        self.swapchain_loader = swapchain_loader;
113        self.swapchain = swapchain;
114        self.extent = extent;
115        self.images = images;
116        self.surface_format = surface_format;
117        self.depth_format = depth_format;
118
119        Ok(())
120    }
121
122    /// Acquires the next available swapchain image.
123    ///
124    /// Returns the index of the next available image from the swapchain and whetherthe swapchain
125    /// is suboptimal. Will signal the provided semaphore when done.
126    ///
127    /// # Examples
128    /// 
129    /// ```no_run
130    /// # let event_loop = winit::event_loop::EventLoop::new();
131    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
132    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
133    /// # let mut swapchain = plate::Swapchain::new(&device, &window)?;
134    /// # let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?;
135    /// let (image_index, _) = swapchain.next_image(&acquire_sem).unwrap();
136    /// # Ok::<(), Box<dyn std::error::Error>>(())
137    /// ```
138    pub fn next_image(&self, semaphore: &Semaphore) -> Result<(u32, bool), Error> {
139        Ok(unsafe {
140            self.swapchain_loader.acquire_next_image(
141                self.swapchain,
142                u64::MAX,
143                **semaphore,
144                vk::Fence::null(),
145            )?
146        })
147    }
148
149    /// Present the image at `image_index` to the screen.
150    ///
151    /// Will wait on wait_semaphore.
152    ///
153    /// # Examples
154    ///
155    /// ```no_run
156    /// # let event_loop = winit::event_loop::EventLoop::new();
157    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
158    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
159    /// # let mut swapchain = plate::Swapchain::new(&device, &window)?;
160    /// # let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?;
161    /// let image_index = 0;
162    /// swapchain.present(image_index, &present_sem).unwrap();
163    /// # Ok::<(), Box<dyn std::error::Error>>(())
164    /// ```
165    pub fn present(&self, image_index: u32, wait_semaphore: &Semaphore) -> Result<bool, Error> {
166        let swapchains = [self.swapchain];
167        let wait_semaphores = [**wait_semaphore];
168        let image_indices = [image_index];
169
170        let present_info = vk::PresentInfoKHR::builder()
171            .wait_semaphores(&wait_semaphores)
172            .swapchains(&swapchains)
173            .image_indices(&image_indices);
174
175        Ok(unsafe { self.swapchain_loader.queue_present(self.device.queue.queue, &present_info)? })
176    }
177
178    /// Returns the aspect ration of the extent.
179    ///
180    /// # Examples
181    /// 
182    /// ```no_run
183    /// # struct Vertex(f32);
184    /// # let event_loop = winit::event_loop::EventLoop::new();
185    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
186    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
187    /// # let mut swapchain = plate::Swapchain::new(&device, &window)?;
188    /// let aspect_ratio = swapchain.aspect_ratio();
189    /// # Ok::<(), Box<dyn std::error::Error>>(())
190    /// ```
191    pub fn aspect_ratio(&self) -> f32 {
192        (self.extent.width as f32) / (self.extent.height as f32)
193    }
194
195    /// Returns the swapchain extent.
196    ///
197    /// # Examples
198    ///
199    /// ```no_run
200    /// # let event_loop = winit::event_loop::EventLoop::new();
201    /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?;
202    /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?;
203    /// # let mut swapchain = plate::Swapchain::new(&device, &window)?;
204    /// let (width, height) = swapchain.extent();
205    /// # Ok::<(), Box<dyn std::error::Error>>(())
206    /// ```
207    pub fn extent(&self) -> (u32, u32) {
208        (self.extent.width, self.extent.height)
209    }
210
211    fn create_swapchain(
212        device: &Arc<Device>,
213        surface: &Surface,
214        window: &winit::window::Window,
215        old_swapchain: Option<vk::SwapchainKHR>,
216    ) -> Result<(
217        khr::Swapchain,
218        vk::SwapchainKHR,
219        vk::Extent2D,
220        Vec<Image>,
221        Format,
222        Format,
223    ), Error> {
224        let surface_capabilities = unsafe {
225            surface
226                .surface_loader
227                .get_physical_device_surface_capabilities(
228                    device.physical_device,
229                    surface.surface,
230                )?
231        };
232        let surface_formats = unsafe {
233            surface
234                .surface_loader
235                .get_physical_device_surface_formats(
236                    device.physical_device,
237                    surface.surface,
238                )?
239        };
240        let present_modes = unsafe {
241            surface
242                .surface_loader
243                .get_physical_device_surface_present_modes(
244                    device.physical_device,
245                    surface.surface,
246                )?
247        };
248
249        let image_format = surface_formats
250            .iter()
251            .find(|format| {
252                format.format == vk::Format::R8G8B8A8_SRGB
253                    && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
254            })
255            .unwrap_or(&surface_formats[0]);
256
257        let present_mode = *present_modes
258            .iter()
259            .find(|mode| **mode == vk::PresentModeKHR::FIFO)
260            .unwrap_or(&present_modes[0]);
261
262        let window_extent = window.inner_size();
263        let extent = vk::Extent2D {
264            width: window_extent.width.clamp(
265                surface_capabilities.min_image_extent.width,
266                surface_capabilities.max_image_extent.width,
267            ),
268            height: window_extent.height.clamp(
269                surface_capabilities.min_image_extent.height,
270                surface_capabilities.max_image_extent.height,
271            ),
272        };
273
274        let queue_families = [device.queue.family];
275
276        let image_count = if surface_capabilities.max_image_count == 0 {
277            surface_capabilities.min_image_count + 1
278        } else {
279            (surface_capabilities.min_image_count + 1).min(surface_capabilities.max_image_count)
280        };
281
282        let mut swapchain_info = vk::SwapchainCreateInfoKHR::builder()
283            .surface(surface.surface)
284            .min_image_count(image_count)
285            .image_format(image_format.format)
286            .image_color_space(image_format.color_space)
287            .image_extent(extent)
288            .image_array_layers(1)
289            .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
290            .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
291            .queue_family_indices(&queue_families)
292            .pre_transform(surface_capabilities.current_transform)
293            .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
294            .present_mode(present_mode)
295            .clipped(true);
296
297        match old_swapchain {
298            Some(swapchain) => swapchain_info = swapchain_info.old_swapchain(swapchain),
299            None => (),
300        };
301
302        let swapchain_loader = khr::Swapchain::new(&device.instance, &device);
303        let swapchain = unsafe { swapchain_loader.create_swapchain(&swapchain_info, None)? };
304
305        let images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? }.into_iter()
306            .map(|i| Image::from_vk_image(device, i, None, extent.width, extent.height, image_format.format, ImageAspectFlags::COLOR))
307            .collect::<Result<Vec<_>, _>>()?;
308
309        let depth_format = [
310            vk::Format::D32_SFLOAT,
311            vk::Format::D32_SFLOAT_S8_UINT,
312            vk::Format::D24_UNORM_S8_UINT
313        ].into_iter()
314            .find(|format| {
315                let props = unsafe { device.instance.get_physical_device_format_properties(device.physical_device, *format) };
316                props.optimal_tiling_features.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
317            })
318            .ok_or(SwapchainError::NoSuitableDepthFormat)?;
319
320        Ok((
321            swapchain_loader,
322            swapchain,
323            extent,
324            images,
325            image_format.format,
326            depth_format,
327        ))
328    }
329}