use crate::{Engine, Result, Texture};
use super::{PresentationCapabilities, PresentationSurface, SdlWindow};
pub struct Presenter<'window> {
swapchain: Option<super::PresentationSwapchain>,
surface: Option<PresentationSurface<'window>>,
pending_drawable_extent: Option<[u32; 2]>,
}
impl<'window> Presenter<'window> {
pub fn new(engine: &Engine, window: &'window SdlWindow) -> Result<Self> {
Ok(Self {
swapchain: None,
surface: Some(engine.create_presentation_surface(window)?),
pending_drawable_extent: None,
})
}
pub fn has_graphics(&self) -> bool {
self.surface().has_graphics()
}
pub fn supports_swapchain(&self) -> bool {
self.surface().supports_swapchain()
}
pub fn has_present(&self) -> bool {
self.surface().present_queue_family().is_some()
}
pub fn capabilities(&self) -> Result<PresentationCapabilities> {
self.surface().capabilities()
}
pub fn swapchain_extent(&self, window: &SdlWindow) -> Result<Option<[u32; 2]>> {
self.surface().swapchain_extent(window.drawable_extent())
}
pub fn can_create_swapchain(&self, window: &SdlWindow) -> Result<bool> {
if !self.has_graphics() || !self.has_present() || !self.supports_swapchain() {
return Ok(false);
}
Ok(
self
.surface()
.swapchain_config(window.drawable_extent())?
.is_some(),
)
}
pub fn init_swapchain(&mut self, window: &SdlWindow) -> Result<bool> {
self.close_swapchain()?;
let extent = self
.pending_drawable_extent
.take()
.unwrap_or_else(|| window.drawable_extent());
self.swapchain = self.surface().create_swapchain(extent)?;
Ok(self.swapchain.is_some())
}
pub fn wait_presentation_idle(&mut self) -> Result<()> {
if let Some(swapchain) = &mut self.swapchain {
swapchain.wait_idle()?;
}
Ok(())
}
pub fn close_swapchain(&mut self) -> Result<()> {
self.wait_presentation_idle()?;
self.swapchain = None;
Ok(())
}
pub fn recreate_swapchain(&mut self, window: &SdlWindow) -> Result<bool> {
self.close_swapchain()?;
self.init_swapchain(window)
}
pub fn notify_pixel_size_changed(&mut self, width: u32, height: u32) {
self.pending_drawable_extent = Some([width, height]);
}
pub fn needs_swapchain_recreate(&self) -> bool {
self.pending_drawable_extent.is_some()
}
pub fn has_swapchain(&self) -> bool {
self
.swapchain
.as_ref()
.is_some_and(super::PresentationSwapchain::is_valid)
}
pub fn swapchain_image_count(&self) -> usize {
self
.swapchain
.as_ref()
.map_or(0, super::PresentationSwapchain::image_count)
}
pub fn frames_in_flight(&self) -> usize {
self
.swapchain
.as_ref()
.map_or(0, super::PresentationSwapchain::frames_in_flight)
}
pub fn present_empty_frame(&mut self) -> Result<bool> {
self.present_clear_frame(crate::Color::default())
}
pub fn present_clear_frame(&mut self, color: crate::Color) -> Result<bool> {
if !color
.to_array()
.iter()
.all(|value| value.is_finite() && (0.0..=1.0).contains(value))
{
return Err(crate::Error::invalid_argument(
"Presenter clear color must be finite RGBA values in [0, 1]",
));
}
self
.swapchain
.as_mut()
.ok_or_else(|| crate::Error::invalid_argument("Presenter has no initialized swapchain"))?
.present_clear(color.to_array())
}
pub fn present_texture(&mut self, texture: &Texture) -> Result<bool> {
let swapchain = self
.swapchain
.as_mut()
.ok_or_else(|| crate::Error::invalid_argument("Presenter has no initialized swapchain"))?;
if let Some(native) = texture.native() {
return swapchain.present_texture(native);
}
if let Some((image, _view, width, height, retention)) = texture.render_target_image() {
return swapchain.present_render_target(image, [width, height], retention);
}
Err(crate::Error::failed_precondition(
"Presenter::present_texture requires a buffer-backed or render-target-backed Texture",
))
}
pub fn present_compose(
&mut self,
compose_image: ash::vk::Image,
compose_width: u32,
compose_height: u32,
descriptor_set: ash::vk::DescriptorSet,
pipeline_layout: ash::vk::PipelineLayout,
record: impl FnOnce(&ash::Device, ash::vk::CommandBuffer) -> crate::Result<()>,
) -> crate::Result<bool> {
self
.swapchain
.as_mut()
.ok_or_else(|| crate::Error::invalid_argument("Presenter has no initialized swapchain"))?
.present_compose(
compose_image,
compose_width,
compose_height,
descriptor_set,
pipeline_layout,
record,
)
}
pub fn swapchain_extent_px(&self) -> Option<[u32; 2]> {
self.swapchain.as_ref().map(|sc| sc.extent_px())
}
fn surface(&self) -> &PresentationSurface<'window> {
self.surface.as_ref().expect("Presenter surface is live")
}
}
impl Drop for Presenter<'_> {
fn drop(&mut self) {
if self
.swapchain
.as_ref()
.is_some_and(super::PresentationSwapchain::has_submitted_frames)
{
if let Some(swapchain) = self.swapchain.take() {
std::mem::forget(swapchain);
}
if let Some(surface) = self.surface.take() {
std::mem::forget(surface);
}
return;
}
self.swapchain = None;
self.surface = None;
}
}