335 lines
8.9 KiB
Rust
335 lines
8.9 KiB
Rust
use crate::prelude::*;
|
|
|
|
use anyhow::Result;
|
|
|
|
use std::cmp;
|
|
use std::sync::Mutex;
|
|
|
|
pub enum NextImageSynchronization<'a> {
|
|
Semaphore(&'a Semaphore<'a>),
|
|
Fence(&'a Fence<'a>),
|
|
}
|
|
|
|
impl<'a> From<&'a Semaphore<'a>> for NextImageSynchronization<'a> {
|
|
fn from(value: &'a Semaphore<'a>) -> Self {
|
|
Self::Semaphore(value)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a Fence<'a>> for NextImageSynchronization<'a> {
|
|
fn from(value: &'a Fence<'a>) -> Self {
|
|
Self::Fence(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Swapchain<'a> {
|
|
width: u32,
|
|
height: u32,
|
|
index: u32,
|
|
|
|
device: &'a Device,
|
|
surface: Surface<'a>,
|
|
|
|
create_info: VkSwapchainCreateInfoKHR,
|
|
swapchain: VkSwapchainKHR,
|
|
usage: VkImageUsageFlagBits,
|
|
|
|
raw: bool,
|
|
}
|
|
|
|
impl<'a> Swapchain<'a> {
|
|
pub fn new(
|
|
device: &'a Device,
|
|
surface: Surface<'a>,
|
|
vsync: bool,
|
|
image_count: u32,
|
|
image_usage: impl Into<VkImageUsageFlagBits>,
|
|
prefered_format: VkFormat,
|
|
array_layers: u32,
|
|
) -> Result<Swapchain<'a>> {
|
|
let surface_caps = surface.capabilities(&device)?;
|
|
|
|
let extent = if surface_caps.currentExtent.width == u32::max_value() {
|
|
return Err(anyhow::Error::msg("Surface has no extent"));
|
|
} else {
|
|
VkExtent2D {
|
|
width: surface_caps.currentExtent.width,
|
|
height: surface_caps.currentExtent.height,
|
|
}
|
|
};
|
|
|
|
let mut present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
if !vsync {
|
|
for present_mode_iter in surface.present_modes(&device)? {
|
|
if present_mode_iter == VK_PRESENT_MODE_MAILBOX_KHR {
|
|
present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
break;
|
|
} else if present_mode_iter == VK_PRESENT_MODE_IMMEDIATE_KHR {
|
|
present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
|
}
|
|
}
|
|
}
|
|
|
|
let swapchain_image_count = if surface_caps.maxImageCount < surface_caps.minImageCount {
|
|
cmp::max(image_count, surface_caps.minImageCount)
|
|
} else {
|
|
cmp::max(
|
|
cmp::min(image_count, surface_caps.maxImageCount),
|
|
surface_caps.minImageCount,
|
|
)
|
|
};
|
|
|
|
let pretransform =
|
|
if (surface_caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 {
|
|
VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR.into()
|
|
} else {
|
|
surface_caps.currentTransform
|
|
};
|
|
|
|
let (format, colorspace) = surface.format_colorspace(&device, prefered_format)?;
|
|
|
|
let swapchain_ci = VkSwapchainCreateInfoKHR::new(
|
|
0,
|
|
surface.vk_handle(),
|
|
swapchain_image_count,
|
|
format,
|
|
colorspace,
|
|
extent,
|
|
array_layers,
|
|
image_usage,
|
|
VK_SHARING_MODE_EXCLUSIVE,
|
|
&[],
|
|
pretransform,
|
|
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
present_mode,
|
|
device.physical_device().features().shaderClipDistance,
|
|
);
|
|
|
|
let swapchain = device.create_swapchain(&swapchain_ci)?;
|
|
|
|
Ok(Swapchain {
|
|
width: extent.width,
|
|
height: extent.height,
|
|
usage: swapchain_ci.imageUsage,
|
|
index: 0,
|
|
|
|
device,
|
|
surface,
|
|
|
|
create_info: swapchain_ci,
|
|
|
|
swapchain,
|
|
|
|
raw: false,
|
|
})
|
|
}
|
|
|
|
pub fn from_ci(device: &'a Device, swapchain_ci: &VkSwapchainCreateInfoKHR) -> Result<Self> {
|
|
Ok(Swapchain {
|
|
width: swapchain_ci.imageExtent.width,
|
|
height: swapchain_ci.imageExtent.height,
|
|
usage: swapchain_ci.imageUsage,
|
|
index: 0,
|
|
|
|
surface: Surface::from_vk_surface(
|
|
swapchain_ci.surface,
|
|
device.physical_device().instance(),
|
|
),
|
|
|
|
create_info: swapchain_ci.clone(),
|
|
|
|
swapchain: device.create_swapchain(swapchain_ci)?,
|
|
device,
|
|
|
|
raw: false,
|
|
})
|
|
}
|
|
|
|
pub fn from_raw(
|
|
device: &'a Device,
|
|
swapchain_ci: &VkSwapchainCreateInfoKHR,
|
|
swapchain: VkSwapchainKHR,
|
|
) -> Self {
|
|
Swapchain {
|
|
width: swapchain_ci.imageExtent.width,
|
|
height: swapchain_ci.imageExtent.height,
|
|
usage: swapchain_ci.imageUsage,
|
|
index: 0,
|
|
|
|
surface: Surface::from_vk_surface(
|
|
swapchain_ci.surface,
|
|
device.physical_device().instance(),
|
|
),
|
|
device,
|
|
|
|
create_info: swapchain_ci.clone(),
|
|
swapchain,
|
|
|
|
raw: true,
|
|
}
|
|
}
|
|
|
|
pub fn recreate(&mut self) -> Result<()> {
|
|
// wait for the device to get idle
|
|
self.device.wait_idle()?;
|
|
|
|
let surface_caps = self.surface.capabilities(self.device)?;
|
|
|
|
let extent = if surface_caps.currentExtent.width == u32::max_value()
|
|
|| surface_caps.currentExtent.height == u32::max_value()
|
|
{
|
|
return Err(anyhow::Error::msg("Surface has no extent"));
|
|
} else if surface_caps.currentExtent.width == 0 || surface_caps.currentExtent.height == 0 {
|
|
// don't recreate swapchain
|
|
return Ok(());
|
|
} else {
|
|
VkExtent2D {
|
|
width: surface_caps.currentExtent.width,
|
|
height: surface_caps.currentExtent.height,
|
|
}
|
|
};
|
|
|
|
let mut swapchain_ci = self.create_info.clone();
|
|
swapchain_ci.imageExtent = extent;
|
|
swapchain_ci.set_old_swapchain(self.swapchain);
|
|
|
|
let swapchain = self.device.create_swapchain(&swapchain_ci)?;
|
|
|
|
// destroy the old swapchain
|
|
self.destroy();
|
|
|
|
// replace swapchain
|
|
self.swapchain = swapchain;
|
|
|
|
// set new surface size
|
|
self.width = extent.width;
|
|
self.height = extent.height;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn acquire_next_image<'b>(
|
|
&mut self,
|
|
time_out: u64,
|
|
synchro: impl Into<NextImageSynchronization<'b>>,
|
|
) -> Result<OutOfDate<u32>> {
|
|
let synchro = synchro.into();
|
|
|
|
let res = self.device.acquire_next_image(
|
|
self.swapchain,
|
|
time_out,
|
|
match synchro {
|
|
NextImageSynchronization::Semaphore(semaphore) => Some(semaphore.vk_handle()),
|
|
NextImageSynchronization::Fence(_) => None,
|
|
},
|
|
match synchro {
|
|
NextImageSynchronization::Semaphore(_) => None,
|
|
NextImageSynchronization::Fence(fence) => Some(fence.vk_handle()),
|
|
},
|
|
);
|
|
|
|
if let Ok(r) = &res {
|
|
if let OutOfDate::Ok(i) = r {
|
|
self.index = *i;
|
|
}
|
|
}
|
|
|
|
res
|
|
}
|
|
|
|
/// set current
|
|
/// only use when externally acquired next index !!!
|
|
pub unsafe fn set_image_index(&mut self, index: u32) {
|
|
self.index = index;
|
|
}
|
|
|
|
pub fn current_index(&self) -> u32 {
|
|
self.index
|
|
}
|
|
|
|
pub fn vk_images(&self) -> Result<Vec<VkImage>> {
|
|
self.device.swapchain_images(self.swapchain)
|
|
}
|
|
|
|
pub fn wrap_images(
|
|
&self,
|
|
images: &[VkImage],
|
|
queue: &'a Mutex<Queue<'a>>,
|
|
assume_layout: bool,
|
|
) -> Result<Vec<Image<'a>>> {
|
|
let format = self.format();
|
|
let tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
if !Image::check_configuration(&self.device, tiling, format, self.usage) {
|
|
return Err(anyhow::Error::msg(format!(
|
|
"Image configuration not allowed (tiling: {:?}, format: {:?}, usage: {:?})",
|
|
tiling, format, self.usage,
|
|
)));
|
|
}
|
|
|
|
let mut swapchain_images = Vec::new();
|
|
|
|
for image in images {
|
|
swapchain_images.push(
|
|
Image::from_preinitialized(
|
|
*image,
|
|
format,
|
|
self.width(),
|
|
self.height(),
|
|
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
self.usage,
|
|
assume_layout,
|
|
)
|
|
.attach_sampler(Sampler::nearest_sampler().build(self.device)?)
|
|
.build(&self.device, queue)?,
|
|
);
|
|
}
|
|
|
|
Ok(swapchain_images)
|
|
}
|
|
|
|
pub fn width(&self) -> u32 {
|
|
self.width
|
|
}
|
|
|
|
pub fn height(&self) -> u32 {
|
|
self.height
|
|
}
|
|
|
|
pub fn format(&self) -> VkFormat {
|
|
self.create_info.imageFormat
|
|
}
|
|
|
|
#[inline]
|
|
fn destroy(&self) {
|
|
self.device.destroy_swapchain(self.swapchain)
|
|
}
|
|
}
|
|
|
|
impl<'a> VulkanDevice for Swapchain<'a> {
|
|
fn device(&self) -> &Device {
|
|
&self.device
|
|
}
|
|
}
|
|
|
|
impl<'a> VkHandle<VkSwapchainKHR> for Swapchain<'a> {
|
|
fn vk_handle(&self) -> VkSwapchainKHR {
|
|
self.swapchain
|
|
}
|
|
}
|
|
|
|
impl<'a> VkHandle<VkSwapchainKHR> for &'a Swapchain<'a> {
|
|
fn vk_handle(&self) -> VkSwapchainKHR {
|
|
self.swapchain
|
|
}
|
|
}
|
|
|
|
impl<'a> Drop for Swapchain<'a> {
|
|
fn drop(&mut self) {
|
|
if !self.raw {
|
|
self.destroy();
|
|
}
|
|
}
|
|
}
|