1use erupt::{try_vk, utils::VulkanResult, vk, DeviceLoader, InstanceLoader, SmallVec, ObjectHandle};
9use std::{collections::VecDeque, mem};
10
11pub struct Swapchain {
13 options: SwapchainOptions,
14
15 frames: Vec<Frame>,
16 frame_index: usize,
17
18 surface: vk::SurfaceKHR,
19 physical_device: vk::PhysicalDevice,
20 handle: vk::SwapchainKHR,
21 generation: u64,
22 images: SmallVec<vk::Image>,
23 extent: vk::Extent2D,
24 format: vk::SurfaceFormatKHR,
25 needs_rebuild: bool,
26
27 old_swapchains: VecDeque<(vk::SwapchainKHR, u64)>,
28}
29
30impl Swapchain {
31 pub fn new(
34 options: SwapchainOptions,
35 surface: vk::SurfaceKHR,
36 physical_device: vk::PhysicalDevice,
37 device: &DeviceLoader,
38 extent: vk::Extent2D,
39 ) -> Self {
40 Self {
41 frames: (0..options.frames_in_flight)
42 .map(|_| unsafe {
43 Frame {
44 complete: device
45 .create_fence(
46 &vk::FenceCreateInfoBuilder::new()
47 .flags(vk::FenceCreateFlags::SIGNALED),
48 None,
49 )
50 .unwrap(),
51 acquire: device
52 .create_semaphore(&vk::SemaphoreCreateInfo::default(), None)
53 .unwrap(),
54 generation: 0,
55 }
56 })
57 .collect(),
58 frame_index: 0,
59
60 surface,
61 physical_device,
62 handle: vk::SwapchainKHR::null(),
63 generation: 0,
64 images: SmallVec::new(),
65 extent,
66 format: vk::SurfaceFormatKHR::default(),
67 needs_rebuild: true,
68
69 old_swapchains: VecDeque::new(),
70
71 options,
72 }
73 }
74
75 #[inline]
83 pub unsafe fn destroy(&mut self, device: &DeviceLoader) {
84 for frame in &self.frames {
85 device.destroy_fence(frame.complete, None);
86 device.destroy_semaphore(frame.acquire, None);
87 }
88
89 if self.handle != vk::SwapchainKHR::null() {
90 device.destroy_swapchain_khr(self.handle, None);
91 }
92
93 for &(swapchain, _) in &self.old_swapchains {
94 device.destroy_swapchain_khr(swapchain, None);
95 }
96 }
97
98 #[inline]
101 pub fn update(&mut self, extent: vk::Extent2D) {
102 self.extent = extent;
103 self.needs_rebuild = true;
104 }
105
106 #[inline]
108 pub fn frames_in_flight(&self) -> usize {
109 self.frames.len()
110 }
111
112 #[inline]
114 pub fn images(&self) -> &[vk::Image] {
115 &self.images
116 }
117
118 #[inline]
121 pub fn format(&self) -> vk::SurfaceFormatKHR {
122 self.format
123 }
124
125 #[inline]
127 pub fn extent(&self) -> vk::Extent2D {
128 self.extent
129 }
130
131 pub unsafe fn acquire(
141 &mut self,
142 instance: &InstanceLoader,
143 device: &DeviceLoader,
144 timeout_ns: u64,
145 ) -> VulkanResult<AcquiredFrame> {
146 let frame_index = self.frame_index;
147 let next_frame_index = (self.frame_index + 1) % self.frames.len();
148 let frame = &self.frames[frame_index];
149 let acquire = frame.acquire;
150 try_vk!(device.wait_for_fences(&[frame.complete], true, timeout_ns));
151
152 while let Some(&(swapchain, generation)) = self.old_swapchains.front() {
154 if self.frames[next_frame_index].generation == generation {
155 break;
156 }
157 device.destroy_swapchain_khr(swapchain, None);
158 self.old_swapchains.pop_front();
159 }
160
161 loop {
162 if !self.needs_rebuild {
163 let acquire_next_image =
164 device.acquire_next_image_khr(self.handle, !0, acquire, vk::Fence::null());
165 let suboptimal = acquire_next_image.raw == vk::Result::SUBOPTIMAL_KHR;
166 match acquire_next_image.result() {
167 Ok(index) => {
168 self.needs_rebuild = suboptimal;
169 let invalidate_images =
170 self.frames[frame_index].generation != self.generation;
171 self.frames[frame_index].generation = self.generation;
172 self.frame_index = next_frame_index;
173 device
174 .reset_fences(&[self.frames[frame_index].complete])
175 .unwrap();
176 return VulkanResult::new_ok(AcquiredFrame {
177 image_index: index as usize,
178 frame_index,
179 ready: acquire,
180 complete: self.frames[frame_index].complete,
181 invalidate_images,
182 });
183 }
184 Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => {}
185 Err(e) => return VulkanResult::new_err(e),
186 }
187 };
188 self.needs_rebuild = true;
189
190 let surface_capabilities = try_vk!(instance
192 .get_physical_device_surface_capabilities_khr(self.physical_device, self.surface));
193
194 self.extent = match surface_capabilities.current_extent.width {
195 std::u32::MAX => vk::Extent2D {
198 width: self.extent.width,
199 height: self.extent.height,
200 },
201 _ => surface_capabilities.current_extent,
202 };
203
204 let pre_transform = if surface_capabilities
205 .supported_transforms
206 .contains(vk::SurfaceTransformFlagsKHR::IDENTITY_KHR)
207 {
208 vk::SurfaceTransformFlagBitsKHR::IDENTITY_KHR
209 } else {
210 surface_capabilities.current_transform
211 };
212
213 let present_modes = try_vk!(instance.get_physical_device_surface_present_modes_khr(
214 self.physical_device,
215 self.surface,
216 None
217 ));
218 let present_mode = match present_modes
219 .iter()
220 .filter_map(|&mode| {
221 Some((
222 mode,
223 self.options
224 .present_mode_preference
225 .iter()
226 .position(|&pref| pref == mode)?,
227 ))
228 })
229 .min_by_key(|&(_, priority)| priority)
230 {
231 Some((mode, _)) => mode,
232 None => return VulkanResult::new_err(vk::Result::ERROR_OUT_OF_DATE_KHR),
233 };
234
235 let desired_image_count =
236 (surface_capabilities.min_image_count + 1).max(self.frames.len() as u32);
237 let image_count = if surface_capabilities.max_image_count > 0 {
238 surface_capabilities
239 .max_image_count
240 .min(desired_image_count)
241 } else {
242 desired_image_count
243 };
244
245 let surface_formats = try_vk!(instance.get_physical_device_surface_formats_khr(
246 self.physical_device,
247 self.surface,
248 None
249 ));
250 match surface_formats
251 .iter()
252 .filter_map(|&format| {
253 Some((
254 format,
255 self.options
256 .format_preference
257 .iter()
258 .position(|&pref| pref == format)?,
259 ))
260 })
261 .min_by_key(|&(_, priority)| priority)
262 {
263 Some((format, _)) => self.format = format,
264 None => return VulkanResult::new_err(vk::Result::ERROR_OUT_OF_DATE_KHR),
265 };
266
267 if self.handle != vk::SwapchainKHR::null() {
268 self.old_swapchains
269 .push_back((self.handle, self.generation));
270 }
271 let handle = try_vk!(device.create_swapchain_khr(
272 &vk::SwapchainCreateInfoKHRBuilder::new()
273 .surface(self.surface)
274 .min_image_count(image_count)
275 .image_color_space(self.format.color_space)
276 .image_format(self.format.format)
277 .image_extent(self.extent)
278 .image_usage(self.options.usage)
279 .image_sharing_mode(self.options.sharing_mode)
280 .pre_transform(pre_transform)
281 .composite_alpha(self.options.composite_alpha)
282 .present_mode(present_mode)
283 .clipped(true)
284 .image_array_layers(1)
285 .old_swapchain(mem::replace(&mut self.handle, vk::SwapchainKHR::null())),
286 None,
287 ));
288 self.generation = self.generation.wrapping_add(1);
289 self.handle = handle;
290 self.images = try_vk!(device.get_swapchain_images_khr(handle, None));
291 self.needs_rebuild = false;
292 }
293 }
294
295 #[inline]
307 pub unsafe fn queue_present(
308 &mut self,
309 device: &DeviceLoader,
310 queue: vk::Queue,
311 render_complete: vk::Semaphore,
312 image_index: usize,
313 ) -> VulkanResult<()> {
314 let queue_present = device.queue_present_khr(
315 queue,
316 &vk::PresentInfoKHRBuilder::new()
317 .wait_semaphores(&[render_complete])
318 .swapchains(&[self.handle])
319 .image_indices(&[image_index as u32]),
320 );
321
322 if let vk::Result::SUBOPTIMAL_KHR | vk::Result::ERROR_OUT_OF_DATE_KHR = queue_present.raw {
323 self.needs_rebuild = true;
324 VulkanResult::new_ok(())
325 } else {
326 queue_present
327 }
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct SwapchainOptions {
334 frames_in_flight: usize,
335 format_preference: Vec<vk::SurfaceFormatKHR>,
336 present_mode_preference: Vec<vk::PresentModeKHR>,
337 usage: vk::ImageUsageFlags,
338 sharing_mode: vk::SharingMode,
339 composite_alpha: vk::CompositeAlphaFlagBitsKHR,
340}
341
342impl SwapchainOptions {
343 #[inline]
345 pub fn new() -> Self {
346 Self::default()
347 }
348
349 #[inline]
351 pub fn frames_in_flight(&mut self, frames: usize) -> &mut Self {
352 self.frames_in_flight = frames;
353 self
354 }
355
356 #[inline]
358 pub fn format_preference(&mut self, formats: &[vk::SurfaceFormatKHR]) -> &mut Self {
359 self.format_preference = formats.into();
360 self
361 }
362
363 #[inline]
365 pub fn present_mode_preference(&mut self, modes: &[vk::PresentModeKHR]) -> &mut Self {
366 self.present_mode_preference = modes.into();
367 self
368 }
369
370 #[inline]
372 pub fn usage(&mut self, usage: vk::ImageUsageFlags) -> &mut Self {
373 self.usage = usage;
374 self
375 }
376
377 #[inline]
379 pub fn sharing_mode(&mut self, mode: vk::SharingMode) -> &mut Self {
380 self.sharing_mode = mode;
381 self
382 }
383
384 #[inline]
386 pub fn composite_alpha(&mut self, value: vk::CompositeAlphaFlagBitsKHR) -> &mut Self {
387 self.composite_alpha = value;
388 self
389 }
390}
391
392impl Default for SwapchainOptions {
393 fn default() -> Self {
394 Self {
395 frames_in_flight: 2,
396 format_preference: vec![
397 vk::SurfaceFormatKHR {
398 format: vk::Format::B8G8R8A8_SRGB,
399 color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR_KHR,
400 },
401 vk::SurfaceFormatKHR {
402 format: vk::Format::R8G8B8A8_SRGB,
403 color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR_KHR,
404 },
405 ],
406 present_mode_preference: vec![vk::PresentModeKHR::FIFO_KHR],
407 usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
408 sharing_mode: vk::SharingMode::EXCLUSIVE,
409 composite_alpha: vk::CompositeAlphaFlagBitsKHR::OPAQUE_KHR,
410 }
411 }
412}
413
414struct Frame {
415 complete: vk::Fence,
416 acquire: vk::Semaphore,
417 generation: u64,
418}
419
420#[derive(Debug, Copy, Clone)]
422pub struct AcquiredFrame {
423 pub image_index: usize,
425 pub frame_index: usize,
428 pub ready: vk::Semaphore,
430 pub complete: vk::Fence,
433 pub invalidate_images: bool,
439}