use crate::{
RafxCommandBuffer, RafxDeviceContext, RafxError, RafxFence, RafxFormat,
RafxPresentSuccessResult, RafxQueue, RafxResult, RafxSemaphore, RafxSwapchain,
RafxSwapchainDef, RafxSwapchainImage, RafxTexture,
};
use crossbeam_channel::{Receiver, Sender};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
pub trait RafxSwapchainEventListener {
fn swapchain_created(
&mut self,
device_context: &RafxDeviceContext,
swapchain: &RafxSwapchain,
) -> RafxResult<()>;
fn swapchain_destroyed(
&mut self,
device_context: &RafxDeviceContext,
swapchain: &RafxSwapchain,
) -> RafxResult<()>;
}
struct RafxSwapchainHelperSharedState {
sync_frame_index: AtomicUsize,
image_available_semaphores: Vec<RafxSemaphore>,
render_finished_semaphores: Vec<RafxSemaphore>,
in_flight_fences: Vec<RafxFence>,
result_tx: Sender<RafxResult<RafxPresentSuccessResult>>,
result_rx: Receiver<RafxResult<RafxPresentSuccessResult>>,
swapchain: Arc<Mutex<RafxSwapchain>>,
}
impl RafxSwapchainHelperSharedState {
fn new(
device_context: &RafxDeviceContext,
swapchain: Arc<Mutex<RafxSwapchain>>,
) -> RafxResult<Self> {
let image_count = swapchain.lock().unwrap().image_count();
let mut image_available_semaphores = Vec::with_capacity(image_count);
let mut render_finished_semaphores = Vec::with_capacity(image_count);
let mut in_flight_fences = Vec::with_capacity(image_count);
for _ in 0..image_count {
image_available_semaphores.push(device_context.create_semaphore()?);
render_finished_semaphores.push(device_context.create_semaphore()?);
in_flight_fences.push(device_context.create_fence()?);
}
let (result_tx, result_rx) = crossbeam_channel::unbounded();
Ok(RafxSwapchainHelperSharedState {
sync_frame_index: AtomicUsize::new(0),
image_available_semaphores,
render_finished_semaphores,
in_flight_fences,
result_tx,
result_rx,
swapchain,
})
}
}
pub struct RafxPresentableFrame {
shared_state: Option<Arc<RafxSwapchainHelperSharedState>>,
swapchain_image: RafxSwapchainImage,
sync_frame_index: usize,
}
impl RafxPresentableFrame {
pub fn rotating_frame_index(&self) -> usize {
self.sync_frame_index
}
pub fn swapchain_texture(&self) -> &RafxTexture {
&self.swapchain_image.texture
}
pub fn present(
mut self,
queue: &RafxQueue,
command_buffers: &[&RafxCommandBuffer],
) -> RafxResult<RafxPresentSuccessResult> {
log::trace!(
"Calling RafxPresentableFrame::present with {} command buffers",
command_buffers.len()
);
let result = self.do_present(queue, command_buffers);
let shared_state = self.shared_state.take().unwrap();
shared_state.result_tx.send(result.clone()).unwrap();
result
}
pub fn present_with_error(
mut self,
queue: &RafxQueue,
error: RafxError,
) {
log::trace!(
"Calling RafxPresentableFrame::present_with_error {:?}",
error
);
let _ = self.do_present(queue, &mut []);
let shared_state = self.shared_state.take().unwrap();
shared_state.result_tx.send(Err(error)).unwrap();
}
pub fn do_present(
&mut self,
queue: &RafxQueue,
command_buffers: &[&RafxCommandBuffer],
) -> RafxResult<RafxPresentSuccessResult> {
let shared_state = self.shared_state.as_ref().unwrap();
let sync_frame_index = shared_state.sync_frame_index.load(Ordering::Relaxed);
assert!(self.sync_frame_index == sync_frame_index);
let frame_fence = &shared_state.in_flight_fences[sync_frame_index];
let wait_semaphores = [&shared_state.image_available_semaphores[sync_frame_index]];
let signal_semaphores = [&shared_state.render_finished_semaphores[sync_frame_index]];
queue.submit(
command_buffers,
&wait_semaphores,
&signal_semaphores,
Some(frame_fence),
)?;
let wait_semaphores = [&shared_state.image_available_semaphores[sync_frame_index]];
let swapchain = shared_state.swapchain.lock().unwrap();
let result = queue.present(
&*swapchain,
&wait_semaphores,
self.swapchain_image.swapchain_image_index,
)?;
shared_state.sync_frame_index.store(
(sync_frame_index + 1) % shared_state.in_flight_fences.len(),
Ordering::Relaxed,
);
Ok(result)
}
}
impl Drop for RafxPresentableFrame {
fn drop(&mut self) {
if self.shared_state.is_some() {
self.shared_state.take().unwrap().result_tx.send(Err(RafxError::StringError("SwapchainHelperPresentableFrame was dropped without calling present or present_with_error".to_string()))).unwrap();
}
}
}
pub enum TryAcquireNextImageResult {
Success(RafxPresentableFrame),
DeviceReset,
}
pub struct RafxSwapchainHelper {
device_context: RafxDeviceContext,
shared_state: Option<Arc<RafxSwapchainHelperSharedState>>,
format: RafxFormat,
swapchain_def: RafxSwapchainDef,
image_count: usize,
expect_result_from_previous_frame: bool,
}
impl RafxSwapchainHelper {
pub fn new(
device_context: &RafxDeviceContext,
swapchain: RafxSwapchain,
mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
) -> RafxResult<Self> {
let format = swapchain.format();
let image_count = swapchain.image_count();
let swapchain_def = swapchain.swapchain_def().clone();
let shared_state = Arc::new(RafxSwapchainHelperSharedState::new(
device_context,
Arc::new(Mutex::new(swapchain)),
)?);
if let Some(event_listener) = event_listener.as_mut() {
let swapchain = shared_state.swapchain.lock().unwrap();
event_listener.swapchain_created(device_context, &*swapchain)?;
}
Ok(RafxSwapchainHelper {
device_context: device_context.clone(),
shared_state: Some(shared_state),
format,
image_count,
swapchain_def,
expect_result_from_previous_frame: false,
})
}
pub fn destroy(
&mut self,
mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
) -> RafxResult<()> {
log::debug!("Destroying swapchain helper");
self.wait_until_previous_frame_submitted()?;
if let Some(shared_state) = self.shared_state.take() {
let begin_wait_time = std::time::Instant::now();
while Arc::strong_count(&shared_state) > 1 {
if (std::time::Instant::now() - begin_wait_time).as_secs_f32() > 1.0 {
log::error!("A presentable frame was submitted but still isn't dropped. Can't clean up the swapchain");
break;
}
}
match Arc::try_unwrap(shared_state) {
Ok(shared_state) => {
log::debug!("wait for all fences to complete");
let fences: Vec<_> = shared_state.in_flight_fences.iter().map(|x| x).collect();
self.device_context.wait_for_fences(&fences)?;
if let Some(event_listener) = event_listener.as_mut() {
let old_swapchain = shared_state.swapchain.lock().unwrap();
log::debug!("destroy the swapchain");
event_listener
.swapchain_destroyed(&self.device_context, &*old_swapchain)?;
}
}
Err(_arc) => {
let error = "The swapchain could not be destroyed, a PresentableFrame exists that is using it";
log::error!("{}", error);
return Err(error)?;
}
}
}
Ok(())
}
pub fn format(&self) -> RafxFormat {
self.format
}
pub fn image_count(&self) -> usize {
self.image_count
}
pub fn swapchain_def(&self) -> &RafxSwapchainDef {
&self.swapchain_def
}
pub fn wait_until_previous_frame_submitted(
&mut self
) -> RafxResult<Option<RafxPresentSuccessResult>> {
if self.expect_result_from_previous_frame {
self.expect_result_from_previous_frame = false;
Ok(Some(
self.shared_state
.as_ref()
.unwrap()
.result_rx
.recv()
.unwrap()?,
))
} else {
Ok(None)
}
}
pub fn wait_until_sync_frame_idle(
&mut self,
sync_frame_index: usize,
) -> RafxResult<()> {
self.shared_state.as_ref().unwrap().in_flight_fences[sync_frame_index].wait()
}
pub fn acquire_next_image(
&mut self,
window_width: u32,
window_height: u32,
event_listener: Option<&mut dyn RafxSwapchainEventListener>,
) -> RafxResult<RafxPresentableFrame> {
let previous_frame_result = self.wait_until_previous_frame_submitted();
let next_sync_frame = self
.shared_state
.as_ref()
.unwrap()
.sync_frame_index
.load(Ordering::Relaxed);
self.wait_until_sync_frame_idle(next_sync_frame)?;
let rebuild_swapchain = match &previous_frame_result {
Ok(result) => {
match result {
Some(result) => match result {
RafxPresentSuccessResult::Success => false,
RafxPresentSuccessResult::SuccessSuboptimal => {
log::debug!("Swapchain is sub-optimal, rebuilding");
if window_height != self.swapchain_def.height
|| window_width != self.swapchain_def.width
{
true
} else {
false
}
}
RafxPresentSuccessResult::DeviceReset => {
log::debug!("Swapchain sent DeviceReset, rebuilding");
true
}
},
None => false,
}
}
Err(e) => return Err(e.clone()),
};
if !rebuild_swapchain {
let result = self.try_acquire_next_image(window_width, window_height)?;
if let TryAcquireNextImageResult::Success(presentable_frame) = result {
return Ok(presentable_frame);
}
};
self.rebuild_swapchain(window_width, window_height, event_listener)?;
let result = self.try_acquire_next_image(window_width, window_height)?;
if let TryAcquireNextImageResult::Success(presentable_frame) = result {
Ok(presentable_frame)
} else {
Err(RafxError::StringError(
"Failed to recreate swapchain".to_string(),
))
}
}
pub fn try_acquire_next_image(
&mut self,
window_width: u32,
window_height: u32,
) -> RafxResult<TryAcquireNextImageResult> {
self.wait_until_previous_frame_submitted()?;
let shared_state = self.shared_state.as_ref().unwrap();
let mut swapchain = shared_state.swapchain.lock().unwrap();
let swapchain_def = swapchain.swapchain_def();
if swapchain_def.width != window_width || swapchain_def.height != window_height {
log::debug!("Force swapchain rebuild due to changed window size");
return Ok(TryAcquireNextImageResult::DeviceReset);
}
let sync_frame_index = shared_state.sync_frame_index.load(Ordering::Relaxed);
let frame_fence = &shared_state.in_flight_fences[sync_frame_index];
self.device_context.wait_for_fences(&[frame_fence]).unwrap();
let image_available_semaphore = &shared_state.image_available_semaphores[sync_frame_index];
let swapchain_image = swapchain.acquire_next_image_semaphore(image_available_semaphore)?;
self.expect_result_from_previous_frame = true;
return Ok(TryAcquireNextImageResult::Success(RafxPresentableFrame {
shared_state: Some(shared_state.clone()),
swapchain_image,
sync_frame_index,
}));
}
fn rebuild_swapchain(
&mut self,
window_width: u32,
window_height: u32,
mut event_listener: Option<&mut dyn RafxSwapchainEventListener>,
) -> RafxResult<()> {
log::info!("Rebuild Swapchain");
let shared_state = self.shared_state.take().unwrap();
{
let mut swapchain = shared_state.swapchain.lock().unwrap();
if let Some(event_listener) = event_listener.as_mut() {
event_listener.swapchain_destroyed(&self.device_context, &*swapchain)?;
}
let mut swapchain_def = swapchain.swapchain_def().clone();
swapchain_def.width = window_width;
swapchain_def.height = window_height;
swapchain.rebuild(&swapchain_def)?;
if let Some(event_listener) = event_listener.as_mut() {
event_listener.swapchain_created(&self.device_context, &swapchain)?;
}
self.format = swapchain.format();
self.image_count = swapchain.image_count();
self.swapchain_def = swapchain_def;
}
self.shared_state = Some(Arc::new(RafxSwapchainHelperSharedState::new(
&self.device_context,
shared_state.swapchain.clone(),
)?));
Ok(())
}
}
impl Drop for RafxSwapchainHelper {
fn drop(&mut self) {
self.destroy(None).unwrap();
}
}