use super::ResizeError;
use super::context::Context;
use super::device::Device;
use super::frame::Frame;
use winit::window::Window;
use std::sync::Arc;
use vulkano::command_buffer::DynamicState;
use vulkano::device::{Device as LogicalDevice, Queue as DeviceQueue};
use vulkano::format::Format;
use vulkano::image::{AttachmentImage, SwapchainImage, ImageCreationError};
use vulkano::swapchain::{Surface, Swapchain as VlkSwapchain, SwapchainCreationError as VlkSwapchainCreationError};
use vulkano::pipeline::viewport::Viewport;
pub use vulkano::swapchain::PresentMode;
type ImageFormat = (Format, vulkano::swapchain::ColorSpace);
pub struct Swapchain {
pub(super) device: Arc<LogicalDevice>,
pub(super) swapchain: Arc<VlkSwapchain<Arc<Window>>>,
pub(super) images: Vec<Arc<SwapchainImage<Arc<Window>>>>,
pub(super) depths: Vec<Arc<AttachmentImage>>,
pub(super) depth_format: Format,
pub(super) inverse_depth: bool,
pub(super) dynamic_state: DynamicState,
pub(super) default_viewport: Viewport,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SwapchainCreationError {
Surface(vulkano::swapchain::SurfaceCreationError),
SurfaceCapabilities(vulkano::swapchain::CapabilitiesError),
Swapchain(VlkSwapchainCreationError),
Image(ImageCreationError),
NoCompatibleFormatFound,
UnsizedWindow,
}
impl Swapchain {
pub fn new(
context: &Context,
device: &Device,
window: Arc<Window>,
present_mode: PresentMode,
depth_format: Format,
) -> Result<Swapchain, SwapchainCreationError>
{
let logical_device = device.logical_device();
let dimensions: (u32, u32) = window.inner_size().into();
let surface = vulkano_win::create_vk_surface(window, context.instance.clone())?;
let (swapchain, images) = create_swapchain(device, surface, dimensions, &device.graphics_queue, present_mode)?;
let depths = {
let image_count = images.len();
let mut images = Vec::with_capacity(image_count);
for _ in 0..image_count {
images.push(AttachmentImage::transient(logical_device.clone(), [dimensions.0, dimensions.1], depth_format)?);
};
images
};
let mut result = Swapchain{
device: logical_device,
swapchain,
images,
depths,
depth_format,
inverse_depth: false,
dynamic_state: DynamicState::default(),
default_viewport: Viewport{ origin: [0f32; 2], dimensions: [0f32; 2], depth_range: 0f32..1f32 },
};
result.resize_viewport(dimensions);
Ok(result)
}
pub fn inverse_depth(&mut self, inverse: bool) {
self.inverse_depth = inverse;
let dimensions = {
let dimensions = self.depths[0].dimensions();
(dimensions[0], dimensions[1])
};
self.resize_viewport(dimensions);
}
pub fn resize(&mut self, dimensions: (u32, u32)) -> Result<(), ResizeError> {
self.resize_viewport(dimensions);
let (swapchain, images) = self.swapchain.recreate_with_dimensions([dimensions.0, dimensions.1])?;
self.swapchain = swapchain;
self.images = images;
self.depths = {
let image_count = self.images.len();
let mut images = Vec::with_capacity(image_count);
for _ in 0..image_count {
images.push(AttachmentImage::transient(self.device.clone(), [dimensions.0, dimensions.1], self.depth_format)?);
};
images
};
Ok(())
}
pub fn get_color_image_for(&self, frame: &Frame) -> Arc<SwapchainImage<Arc<Window>>> {
self.images[frame.swapchain_index].clone()
}
pub fn get_depth_image_for(&self, frame: &Frame) -> Arc<AttachmentImage> {
self.depths[frame.swapchain_index].clone()
}
pub fn default_viewport(&self) -> Viewport {
self.default_viewport.clone()
}
fn resize_viewport(&mut self, dimensions: (u32, u32)) {
self.default_viewport = {
let origin = [0f32; 2];
let dimensions = [dimensions.0 as f32, dimensions.1 as f32];
let depth_range = match self.inverse_depth {
true => 1f32..0f32,
false => 0f32..1f32,
};
Viewport{ origin, dimensions, depth_range }
};
match self.dynamic_state.viewports {
Some(ref mut vec) => {
match vec.len() {
0 => vec.push(self.default_viewport.clone()),
_ => vec[0] = self.default_viewport.clone(),
}
},
None => self.dynamic_state.viewports = Some(vec![self.default_viewport.clone()]),
};
}
}
impl From<vulkano::swapchain::SurfaceCreationError> for SwapchainCreationError {
fn from(err: vulkano::swapchain::SurfaceCreationError) -> Self { Self::Surface(err) }
}
impl From<ImageCreationError> for SwapchainCreationError {
fn from(err: ImageCreationError) -> Self { Self::Image(err) }
}
fn create_swapchain(
device: &Device,
surface: Arc<Surface<Arc<Window>>>,
dimensions: (u32, u32),
graphics_queue: &Arc<DeviceQueue>,
present_mode: PresentMode
) -> Result<(Arc<VlkSwapchain<Arc<Window>>>, Vec<Arc<SwapchainImage<Arc<Window>>>>), SwapchainCreationError> {
let capabilities = match surface.capabilities(device.physical_device()) {
Ok(caps) => caps,
Err(err) => return Err(SwapchainCreationError::SurfaceCapabilities(err)),
};
let usage = capabilities.supported_usage_flags;
let alpha = capabilities.supported_composite_alpha.iter().next().unwrap();
let (format, color_space) = select_format(capabilities.supported_formats)?;
let swapchain = VlkSwapchain::new(
device.logical_device(),
surface,
capabilities.min_image_count,
format,
[dimensions.0, dimensions.1],
1,
usage,
graphics_queue,
vulkano::swapchain::SurfaceTransform::Identity,
alpha,
present_mode,
vulkano::swapchain::FullscreenExclusive::Default,
true,
color_space
);
match swapchain {
Ok(swapchain) => Ok(swapchain),
Err(err) => Err(SwapchainCreationError::Swapchain(err)),
}
}
fn select_format(formats: Vec<ImageFormat>) -> Result<ImageFormat, SwapchainCreationError> {
if formats.is_empty() {
return Err(SwapchainCreationError::NoCompatibleFormatFound);
}
let mut format = formats[0];
for other in formats {
format = choose_better_format(format, other);
}
Ok(format)
}
fn choose_better_format(first: ImageFormat, _second: ImageFormat) -> ImageFormat {
first
}