1use crate::{
2 RafxCommandBuffer, RafxDeviceContext, RafxError, RafxFence, RafxFormat,
3 RafxPresentSuccessResult, RafxQueue, RafxResult, RafxSemaphore, RafxSwapchain,
4 RafxSwapchainColorSpace, RafxSwapchainDef, RafxSwapchainImage, RafxTexture,
5};
6use crossbeam_channel::{Receiver, Sender};
7use std::sync::atomic::{AtomicUsize, Ordering};
8use std::sync::{Arc, Mutex};
9
10pub trait RafxSwapchainEventListener {
13 fn swapchain_created(
16 &mut self,
17 device_context: &RafxDeviceContext,
18 swapchain: &RafxSwapchain,
19 ) -> RafxResult<()>;
20
21 fn swapchain_destroyed(
24 &mut self,
25 device_context: &RafxDeviceContext,
26 swapchain: &RafxSwapchain,
27 ) -> RafxResult<()>;
28}
29
30struct RafxSwapchainHelperSharedState {
34 global_frame_index: AtomicUsize,
35 sync_frame_index: AtomicUsize,
36 image_available_semaphores: Vec<RafxSemaphore>,
37 render_finished_semaphores: Vec<RafxSemaphore>,
38 in_flight_fences: Vec<RafxFence>,
39 result_tx: Sender<RafxResult<RafxPresentSuccessResult>>,
40 result_rx: Receiver<RafxResult<RafxPresentSuccessResult>>,
41 swapchain: Arc<Mutex<RafxSwapchain>>,
43}
44
45impl RafxSwapchainHelperSharedState {
46 fn new(
47 device_context: &RafxDeviceContext,
48 swapchain: Arc<Mutex<RafxSwapchain>>,
49 ) -> RafxResult<Self> {
50 let image_count = swapchain.lock().unwrap().image_count();
51 let mut image_available_semaphores = Vec::with_capacity(image_count);
52 let mut render_finished_semaphores = Vec::with_capacity(image_count);
53 let mut in_flight_fences = Vec::with_capacity(image_count);
54
55 for _ in 0..image_count {
56 image_available_semaphores.push(device_context.create_semaphore()?);
57 render_finished_semaphores.push(device_context.create_semaphore()?);
58 in_flight_fences.push(device_context.create_fence()?);
59 }
60
61 let (result_tx, result_rx) = crossbeam_channel::unbounded();
62
63 Ok(RafxSwapchainHelperSharedState {
64 global_frame_index: AtomicUsize::new(0),
65 sync_frame_index: AtomicUsize::new(0),
66 image_available_semaphores,
67 render_finished_semaphores,
68 in_flight_fences,
69 result_tx,
70 result_rx,
71 swapchain,
72 })
73 }
74}
75
76pub struct RafxPresentableFrame {
82 shared_state: Option<Arc<RafxSwapchainHelperSharedState>>,
85 swapchain_image: RafxSwapchainImage,
86 global_frame_index: usize,
87 sync_frame_index: usize,
88}
89
90impl RafxPresentableFrame {
91 pub fn rotating_frame_index(&self) -> usize {
96 self.sync_frame_index
98 }
99
100 pub fn incrementing_frame_index(&self) -> usize {
101 self.global_frame_index
102 }
103
104 pub fn swapchain_texture(&self) -> &RafxTexture {
106 &self.swapchain_image.texture
107 }
108
109 pub fn swapchain(&self) -> &Arc<Mutex<RafxSwapchain>> {
113 &self.shared_state.as_ref().unwrap().swapchain
114 }
115
116 pub fn present(
119 mut self,
120 queue: &RafxQueue,
121 command_buffers: &[&RafxCommandBuffer],
122 ) -> RafxResult<RafxPresentSuccessResult> {
123 log::trace!(
124 "Calling RafxPresentableFrame::present with {} command buffers",
125 command_buffers.len()
126 );
127 let result = self.do_present(queue, command_buffers);
128
129 let shared_state = self.shared_state.take().unwrap();
131 shared_state.result_tx.send(result.clone()).unwrap();
132
133 result
134 }
135
136 pub fn present_with_error(
139 mut self,
140 queue: &RafxQueue,
141 error: RafxError,
142 ) {
143 log::warn!(
144 "Calling RafxPresentableFrame::present_with_error {:?}",
145 error
146 );
147
148 let _ = self.do_present(queue, &mut []);
155
156 let shared_state = self.shared_state.take().unwrap();
158 shared_state.result_tx.send(Err(error)).unwrap();
159 }
160
161 pub fn do_present(
162 &mut self,
163 queue: &RafxQueue,
164 command_buffers: &[&RafxCommandBuffer],
165 ) -> RafxResult<RafxPresentSuccessResult> {
166 let shared_state = self.shared_state.as_ref().unwrap();
168 let sync_frame_index = shared_state.sync_frame_index.load(Ordering::Relaxed);
169 assert!(self.sync_frame_index == sync_frame_index);
170
171 let frame_fence = &shared_state.in_flight_fences[sync_frame_index];
172 let wait_semaphores = [&shared_state.image_available_semaphores[sync_frame_index]];
173 let signal_semaphores = [&shared_state.render_finished_semaphores[sync_frame_index]];
174
175 queue.submit(
176 command_buffers,
177 &wait_semaphores,
178 &signal_semaphores,
179 Some(frame_fence),
180 )?;
181
182 let swapchain = shared_state.swapchain.lock().unwrap();
183
184 let result = queue.present(
186 &*swapchain,
187 &signal_semaphores,
188 self.swapchain_image.swapchain_image_index,
189 );
190
191 shared_state.sync_frame_index.store(
192 (sync_frame_index + 1) % shared_state.in_flight_fences.len(),
193 Ordering::Relaxed,
194 );
195 shared_state
196 .global_frame_index
197 .fetch_add(1, Ordering::Relaxed);
198
199 result
200 }
201}
202
203impl Drop for RafxPresentableFrame {
204 fn drop(&mut self) {
205 if self.shared_state.is_some() {
206 self.shared_state.take().unwrap().result_tx.send(Err(RafxError::StringError("SwapchainHelperPresentableFrame was dropped without calling present or present_with_error".to_string()))).unwrap();
207 }
208 }
209}
210
211pub enum TryAcquireNextImageResult {
212 Success(RafxPresentableFrame),
213
214 DeviceReset,
219}
220
221pub struct RafxSwapchainHelper {
222 device_context: RafxDeviceContext,
223 shared_state: Option<Arc<RafxSwapchainHelperSharedState>>,
224 format: RafxFormat,
225 color_space: RafxSwapchainColorSpace,
226 swapchain_def: RafxSwapchainDef,
227 image_count: usize,
228
229 expect_result_from_previous_frame: bool,
232}
233
234impl RafxSwapchainHelper {
235 pub fn new(
236 device_context: &RafxDeviceContext,
237 swapchain: RafxSwapchain,
238 mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
239 ) -> RafxResult<Self> {
240 let format = swapchain.format();
241 let color_space = swapchain.color_space();
242 let image_count = swapchain.image_count();
243 let swapchain_def = swapchain.swapchain_def().clone();
244
245 let shared_state = Arc::new(RafxSwapchainHelperSharedState::new(
246 device_context,
247 Arc::new(Mutex::new(swapchain)),
248 )?);
249
250 if let Some(event_listener) = event_listener.as_mut() {
251 let swapchain = shared_state.swapchain.lock().unwrap();
252 event_listener.swapchain_created(device_context, &*swapchain)?;
253 }
254
255 Ok(RafxSwapchainHelper {
256 device_context: device_context.clone(),
257 shared_state: Some(shared_state),
258 format,
259 color_space,
260 image_count,
261 swapchain_def,
262 expect_result_from_previous_frame: false,
263 })
264 }
265
266 pub fn destroy(
267 &mut self,
268 mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
269 ) -> RafxResult<()> {
270 log::debug!("Destroying swapchain helper");
271
272 self.wait_until_previous_frame_submitted()?;
275
276 if let Some(shared_state) = self.shared_state.take() {
277 let begin_wait_time = rafx_base::Instant::now();
278 while Arc::strong_count(&shared_state) > 1 {
279 if (rafx_base::Instant::now() - begin_wait_time).as_secs_f32() > 1.0 {
282 log::error!("A presentable frame was submitted but still isn't dropped. Can't clean up the swapchain");
284 break;
285 }
286 }
287
288 match Arc::try_unwrap(shared_state) {
289 Ok(shared_state) => {
290 log::debug!("wait for all fences to complete");
291 let fences: Vec<_> = shared_state.in_flight_fences.iter().map(|x| x).collect();
292 self.device_context.wait_for_fences(&fences)?;
293
294 if let Some(event_listener) = event_listener.as_mut() {
295 let old_swapchain = shared_state.swapchain.lock().unwrap();
296 log::debug!("destroy the swapchain");
297 event_listener
298 .swapchain_destroyed(&self.device_context, &*old_swapchain)?;
299 }
300 }
301 Err(_arc) => {
302 let error = "The swapchain could not be destroyed, a PresentableFrame exists that is using it";
303 log::error!("{}", error);
304 return Err(error)?;
305 }
306 }
307 }
308
309 Ok(())
310 }
311
312 pub fn format(&self) -> RafxFormat {
313 self.format
314 }
315
316 pub fn color_space(&self) -> RafxSwapchainColorSpace {
317 self.color_space
318 }
319
320 pub fn image_count(&self) -> usize {
321 self.image_count
322 }
323
324 pub fn swapchain_def(&self) -> &RafxSwapchainDef {
325 &self.swapchain_def
326 }
327
328 pub fn wait_until_previous_frame_submitted(
329 &mut self
330 ) -> RafxResult<Option<RafxPresentSuccessResult>> {
331 if self.expect_result_from_previous_frame {
332 self.expect_result_from_previous_frame = false;
333
334 Ok(Some(
335 self.shared_state
336 .as_ref()
337 .unwrap()
338 .result_rx
339 .recv()
340 .unwrap()?,
341 ))
342 } else {
343 Ok(None)
344 }
345 }
346
347 pub fn wait_until_sync_frame_idle(
348 &mut self,
349 sync_frame_index: usize,
350 ) -> RafxResult<()> {
351 self.shared_state.as_ref().unwrap().in_flight_fences[sync_frame_index].wait()
352 }
353
354 pub fn acquire_next_image(
355 &mut self,
356 window_width: u32,
357 window_height: u32,
358 event_listener: Option<&mut dyn RafxSwapchainEventListener>,
359 ) -> RafxResult<RafxPresentableFrame> {
360 let previous_frame_result = self.wait_until_previous_frame_submitted();
364
365 let next_sync_frame = self
370 .shared_state
371 .as_ref()
372 .unwrap()
373 .sync_frame_index
374 .load(Ordering::Relaxed);
375 self.wait_until_sync_frame_idle(next_sync_frame)?;
376
377 let rebuild_swapchain = match &previous_frame_result {
386 Ok(result) => {
387 match result {
388 Some(result) => match result {
390 RafxPresentSuccessResult::Success => false,
391 RafxPresentSuccessResult::SuccessSuboptimal => {
392 log::debug!("Swapchain is sub-optimal, rebuilding");
393 if window_height != self.swapchain_def.height
399 || window_width != self.swapchain_def.width
400 {
401 true
402 } else {
403 false
404 }
405 }
406 RafxPresentSuccessResult::DeviceReset => {
407 log::debug!("Swapchain sent DeviceReset, rebuilding");
408 true
409 }
410 },
411 None => false,
413 }
414 }
415 Err(e) => return Err(e.clone()),
417 };
418
419 if !rebuild_swapchain {
423 let result = self.try_acquire_next_image(window_width, window_height)?;
425 if let TryAcquireNextImageResult::Success(presentable_frame) = result {
426 return Ok(presentable_frame);
427 }
428
429 };
432
433 self.rebuild_swapchain(window_width, window_height, event_listener)?;
437
438 let result = self.try_acquire_next_image(window_width, window_height)?;
439 if let TryAcquireNextImageResult::Success(presentable_frame) = result {
440 Ok(presentable_frame)
441 } else {
442 Err(RafxError::StringError(
443 "Failed to recreate swapchain".to_string(),
444 ))
445 }
446 }
447
448 pub fn try_acquire_next_image(
449 &mut self,
450 window_width: u32,
451 window_height: u32,
452 ) -> RafxResult<TryAcquireNextImageResult> {
453 match self.do_try_acquire_next_image(window_width, window_height) {
454 #[cfg(feature = "rafx-vulkan")]
455 Err(RafxError::VkError(ash::vk::Result::ERROR_OUT_OF_DATE_KHR)) => {
456 Ok(TryAcquireNextImageResult::DeviceReset)
457 }
458 result @ _ => result,
459 }
460 }
461
462 fn do_try_acquire_next_image(
463 &mut self,
464 window_width: u32,
465 window_height: u32,
466 ) -> RafxResult<TryAcquireNextImageResult> {
467 self.wait_until_previous_frame_submitted()?;
473
474 let shared_state = self.shared_state.as_ref().unwrap();
476 let mut swapchain = shared_state.swapchain.lock().unwrap();
477 let swapchain_def = swapchain.swapchain_def();
478
479 if swapchain_def.width != window_width || swapchain_def.height != window_height {
480 log::debug!("Force swapchain rebuild due to changed window size");
481 return Ok(TryAcquireNextImageResult::DeviceReset);
482 }
483
484 let sync_frame_index = shared_state.sync_frame_index.load(Ordering::Relaxed);
487 let global_frame_index = shared_state.global_frame_index.load(Ordering::Relaxed);
488
489 let frame_fence = &shared_state.in_flight_fences[sync_frame_index];
491 self.device_context.wait_for_fences(&[frame_fence]).unwrap();
492
493 let image_available_semaphore = &shared_state.image_available_semaphores[sync_frame_index];
495 let swapchain_image = swapchain.acquire_next_image_semaphore(image_available_semaphore)?;
496
497 self.expect_result_from_previous_frame = true;
498
499 Ok(TryAcquireNextImageResult::Success(RafxPresentableFrame {
500 shared_state: Some(shared_state.clone()),
501 swapchain_image,
502 sync_frame_index,
503 global_frame_index,
504 }))
505 }
506
507 fn rebuild_swapchain(
508 &mut self,
509 window_width: u32,
510 window_height: u32,
511 mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
512 ) -> RafxResult<()> {
513 log::info!("Rebuild Swapchain");
514
515 let shared_state = self.shared_state.take().unwrap();
516 {
517 let mut swapchain = shared_state.swapchain.lock().unwrap();
518 if let Some(event_listener) = event_listener.as_mut() {
519 event_listener.swapchain_destroyed(&self.device_context, &*swapchain)?;
520 }
521
522 let mut swapchain_def = swapchain.swapchain_def().clone();
523 swapchain_def.width = window_width;
524 swapchain_def.height = window_height;
525
526 swapchain.rebuild(&swapchain_def)?;
527
528 if let Some(event_listener) = event_listener.as_mut() {
529 event_listener.swapchain_created(&self.device_context, &swapchain)?;
530 }
531
532 self.format = swapchain.format();
533 self.image_count = swapchain.image_count();
534 self.swapchain_def = swapchain_def;
535 }
536
537 self.shared_state = Some(Arc::new(RafxSwapchainHelperSharedState::new(
538 &self.device_context,
539 shared_state.swapchain.clone(),
540 )?));
541 Ok(())
542 }
543}
544
545impl Drop for RafxSwapchainHelper {
546 fn drop(&mut self) {
547 self.destroy(None).unwrap();
549 }
550}