use std::sync::Arc;
use anyhow::Result;
use ash::vk;
use crate::{
Allocator, AppSettings, CmdBuffer, DefaultAllocator, Device, Error, ExecutionManager, Fence,
Image, ImageView, Instance, Semaphore, Surface, Swapchain, WindowInterface,
};
use crate::pool::{Poolable, Pooled, ResourcePool};
use crate::sync::domain::ExecutionDomain;
use crate::sync::submit_batch::SubmitBatch;
use crate::util::deferred_delete::DeletionQueue;
use crate::wsi::swapchain::SwapchainImage;
#[derive(Derivative)]
#[derivative(Debug)]
struct PerFrame<A> {
#[derivative(Debug = "ignore")]
pub fence: Pooled<Fence<()>>,
pub image_ready: Arc<Semaphore>,
pub gpu_finished: Arc<Semaphore>,
#[derivative(Debug = "ignore")]
pub command_buffer: Option<Box<dyn CmdBuffer<A>>>,
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct InFlightContext {
pub swapchain_image: ImageView,
pub(crate) wait_semaphore: Arc<Semaphore>,
pub(crate) signal_semaphore: Arc<Semaphore>,
}
pub const FRAMES_IN_FLIGHT: usize = 2;
#[derive(Derivative)]
#[derivative(Debug)]
pub struct FrameManager<A: Allocator = DefaultAllocator> {
device: Device,
per_frame: [PerFrame<A>; FRAMES_IN_FLIGHT],
current_frame: u32,
current_image: u32,
swapchain: Swapchain,
swapchain_delete: DeletionQueue<Swapchain>,
pool: ResourcePool<A>,
}
#[derive(Debug, Copy, Clone)]
struct AcquiredImage {
pub index: u32,
pub resize_required: bool,
}
impl<A: Allocator> FrameManager<A> {
fn acquire_image(&mut self) -> Result<AcquiredImage> {
let frame = &mut self.per_frame[self.current_frame as usize];
frame.fence.wait()?;
let result = unsafe {
self.swapchain.acquire_next_image(
self.swapchain.handle(),
u64::MAX,
frame.image_ready.handle(),
vk::Fence::null(),
)
};
match result {
Ok((index, _)) => Ok(AcquiredImage {
index,
resize_required: false,
}),
Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Ok(AcquiredImage {
index: 0,
resize_required: true,
}),
Err(err) => Err(err.into()),
}
}
fn resize_swapchain<Window: WindowInterface>(
&mut self,
window: &Window,
surface: &Surface,
) -> Result<Swapchain> {
let mut new_swapchain = Swapchain {
handle: vk::SwapchainKHR::null(),
images: vec![],
format: self.swapchain.format(),
present_mode: self.swapchain.present_mode(),
extent: vk::Extent2D {
width: window.width(),
height: window.height(),
},
functions: self.swapchain.functions.clone(),
};
let image_count = self.swapchain.images.len();
let info = vk::SwapchainCreateInfoKHR {
s_type: vk::StructureType::SWAPCHAIN_CREATE_INFO_KHR,
p_next: std::ptr::null(),
flags: Default::default(),
surface: unsafe { surface.handle() },
min_image_count: image_count as u32,
image_format: self.swapchain.format().format,
image_color_space: self.swapchain.format().color_space,
image_extent: *new_swapchain.extent(),
image_array_layers: 1,
image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
image_sharing_mode: vk::SharingMode::EXCLUSIVE,
queue_family_index_count: 0,
p_queue_family_indices: std::ptr::null(),
pre_transform: surface.capabilities().current_transform,
composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
present_mode: self.swapchain.present_mode(),
clipped: vk::TRUE,
old_swapchain: unsafe { self.swapchain.handle() },
};
new_swapchain.handle = unsafe { self.swapchain.create_swapchain(&info, None)? };
new_swapchain.images =
unsafe { self.swapchain.get_swapchain_images(new_swapchain.handle)? }
.iter()
.map(move |image| -> Result<SwapchainImage> {
let image = Image::new_managed(
self.device.clone(),
*image,
new_swapchain.format.format,
vk::Extent3D {
width: new_swapchain.extent.width,
height: new_swapchain.extent.height,
depth: 1,
},
1,
1,
vk::SampleCountFlags::TYPE_1,
);
let view = image.view(vk::ImageAspectFlags::COLOR)?;
Ok(SwapchainImage {
image,
view,
})
})
.collect::<Result<Vec<SwapchainImage>>>()?;
Ok(new_swapchain)
}
fn submit<D: ExecutionDomain + 'static>(&mut self, batch: SubmitBatch<D>) -> Result<()> {
let per_frame = &mut self.per_frame[self.current_frame as usize];
per_frame.fence = batch.finish()?;
Ok(())
}
fn present(&self, exec: ExecutionManager<A>) -> Result<()> {
let per_frame = &self.per_frame[self.current_frame as usize];
let functions = &self.swapchain.functions;
let queue = exec.get_present_queue();
if let Some(queue) = queue {
let gpu_finished = unsafe { per_frame.gpu_finished.handle() };
let info = vk::PresentInfoKHR {
s_type: vk::StructureType::PRESENT_INFO_KHR,
p_next: std::ptr::null(),
wait_semaphore_count: 1,
p_wait_semaphores: &gpu_finished,
swapchain_count: 1,
p_swapchains: &self.swapchain.handle,
p_image_indices: &self.current_image,
p_results: std::ptr::null_mut(),
};
let result = unsafe { functions.queue_present(queue.handle(), &info).map(|_| ()) };
match result {
Ok(_) => Ok(()),
Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Ok(()),
Err(e) => Err(e.into()),
}
} else {
Err(Error::NoPresentQueue.into())
}
}
#[allow(dead_code)]
unsafe fn get_swapchain_image(&self) -> Result<ImageView> {
Ok(self.swapchain.images[self.current_image as usize]
.view
.clone())
}
}
impl<A: Allocator> FrameManager<A> {
pub fn new(device: Device, pool: ResourcePool<A>, swapchain: Swapchain) -> Result<Self> {
Ok(FrameManager {
device: device.clone(),
per_frame: (0..FRAMES_IN_FLIGHT)
.map(|_| -> Result<PerFrame<A>> {
Ok(PerFrame {
fence: Fence::new(device.clone(), true)?.into_pooled(&pool.fences, ()),
image_ready: Arc::new(Semaphore::new(device.clone())?),
gpu_finished: Arc::new(Semaphore::new(device.clone())?),
command_buffer: None,
})
})
.collect::<Result<Vec<PerFrame<A>>>>()?
.try_into()
.map_err(|_| Error::Uncategorized("Conversion to slice failed"))?,
current_frame: 0,
current_image: 0,
swapchain,
swapchain_delete: DeletionQueue::<Swapchain>::new((FRAMES_IN_FLIGHT + 2) as u32),
pool,
})
}
pub fn new_with_swapchain<W: WindowInterface>(
instance: &Instance,
device: Device,
pool: ResourcePool<A>,
settings: &AppSettings<W>,
surface: &Surface,
) -> Result<Self> {
let swapchain = Swapchain::new(instance, device.clone(), settings, surface)?;
FrameManager::new(device, pool, swapchain)
}
pub async fn new_frame<Window, D, F>(
&mut self,
exec: ExecutionManager<A>,
window: &Window,
surface: &Surface,
f: F,
) -> Result<()>
where
Window: WindowInterface,
D: ExecutionDomain + 'static,
F: FnOnce(InFlightContext) -> Result<SubmitBatch<D>>, {
self.swapchain_delete.next_frame();
self.current_frame = (self.current_frame + 1) % self.per_frame.len() as u32;
let AcquiredImage {
index,
resize_required,
} = self.acquire_image()?;
self.current_image = index;
if resize_required {
let mut new_swapchain = self.resize_swapchain(window, surface)?;
std::mem::swap(&mut new_swapchain, &mut self.swapchain);
self.swapchain_delete.push(new_swapchain);
let AcquiredImage {
index,
..
} = self.acquire_image()?;
self.current_image = index;
}
let submission = {
let per_frame = &mut self.per_frame[self.current_frame as usize];
if let Some(cmd) = &mut per_frame.command_buffer {
unsafe { cmd.delete(exec.clone())? }
}
per_frame.command_buffer = None;
let image = self.swapchain.images()[self.current_image as usize]
.view
.clone();
let ifc = InFlightContext {
swapchain_image: image,
wait_semaphore: per_frame.image_ready.clone(),
signal_semaphore: per_frame.gpu_finished.clone(),
};
f(ifc)?
};
self.submit(submission)?;
self.present(exec)
}
pub unsafe fn get_swapchain(&self) -> &Swapchain {
&self.swapchain
}
}