use crate::{device, format::Format, image, Backend};
use std::{
any::Any,
borrow::Borrow,
cmp::{max, min},
fmt,
ops::RangeInclusive,
};
pub const DEFAULT_USAGE: image::Usage = image::Usage::COLOR_ATTACHMENT;
pub const DEFAULT_IMAGE_COUNT: SwapImageIndex = 3;
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
#[error("Surface lost")]
pub struct SurfaceLost;
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum SwapchainError {
#[error(transparent)]
OutOfMemory(#[from] device::OutOfMemory),
#[error(transparent)]
DeviceLost(#[from] device::DeviceLost),
#[error(transparent)]
SurfaceLost(#[from] SurfaceLost),
#[error("Window is in use")]
WindowInUse,
#[error("Accecssing NSView from wrong thread")]
WrongThread,
#[error("Swapchain can't be created for an unknown reason")]
Unknown,
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Extent2D {
pub width: image::Size,
pub height: image::Size,
}
impl From<image::Extent> for Extent2D {
fn from(ex: image::Extent) -> Self {
Extent2D {
width: ex.width,
height: ex.height,
}
}
}
impl From<(image::Size, image::Size)> for Extent2D {
fn from(tuple: (image::Size, image::Size)) -> Self {
Extent2D {
width: tuple.0,
height: tuple.1,
}
}
}
impl From<Extent2D> for (image::Size, image::Size) {
fn from(extent: Extent2D) -> Self {
(extent.width, extent.height)
}
}
impl Extent2D {
pub fn to_extent(&self) -> image::Extent {
image::Extent {
width: self.width,
height: self.height,
depth: 1,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Offset2D {
pub x: image::TexelCoordinate,
pub y: image::TexelCoordinate,
}
impl From<(image::TexelCoordinate, image::TexelCoordinate)> for Offset2D {
fn from(tuple: (image::TexelCoordinate, image::TexelCoordinate)) -> Self {
Offset2D {
x: tuple.0,
y: tuple.1,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SurfaceCapabilities {
pub image_count: RangeInclusive<SwapImageIndex>,
pub current_extent: Option<Extent2D>,
pub extents: RangeInclusive<Extent2D>,
pub max_image_layers: image::Layer,
pub usage: image::Usage,
pub present_modes: PresentMode,
pub composite_alpha_modes: CompositeAlphaMode,
}
impl SurfaceCapabilities {
fn clamped_extent(&self, default_extent: Extent2D) -> Extent2D {
match self.current_extent {
Some(current) => current,
None => {
let (min_width, max_width) = (self.extents.start().width, self.extents.end().width);
let (min_height, max_height) =
(self.extents.start().height, self.extents.end().height);
let width = min(max_width, max(default_extent.width, min_width));
let height = min(max_height, max(default_extent.height, min_height));
Extent2D { width, height }
}
}
}
}
pub trait Surface<B: Backend>: fmt::Debug + Any + Send + Sync {
fn supports_queue_family(&self, family: &B::QueueFamily) -> bool;
fn capabilities(&self, physical_device: &B::PhysicalDevice) -> SurfaceCapabilities;
fn supported_formats(&self, physical_device: &B::PhysicalDevice) -> Option<Vec<Format>>;
}
pub trait PresentationSurface<B: Backend>: Surface<B> {
type SwapchainImage: Borrow<B::Image> + Borrow<B::ImageView> + fmt::Debug + Send + Sync;
unsafe fn configure_swapchain(
&mut self,
device: &B::Device,
config: SwapchainConfig,
) -> Result<(), SwapchainError>;
unsafe fn unconfigure_swapchain(&mut self, device: &B::Device);
unsafe fn acquire_image(
&mut self,
timeout_ns: u64,
) -> Result<(Self::SwapchainImage, Option<Suboptimal>), AcquireError>;
}
pub type SwapImageIndex = u32;
bitflags!(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PresentMode: u32 {
const IMMEDIATE = 0x1;
const MAILBOX = 0x2;
const FIFO = 0x4;
const RELAXED = 0x8;
}
);
bitflags!(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CompositeAlphaMode: u32 {
const OPAQUE = 0x1;
const PREMULTIPLIED = 0x2;
const POSTMULTIPLIED = 0x4;
const INHERIT = 0x8;
}
);
#[derive(Debug, Clone)]
pub struct SwapchainConfig {
pub present_mode: PresentMode,
pub composite_alpha_mode: CompositeAlphaMode,
pub format: Format,
pub extent: Extent2D,
pub image_count: SwapImageIndex,
pub image_layers: image::Layer,
pub image_usage: image::Usage,
}
impl SwapchainConfig {
pub fn new(width: u32, height: u32, format: Format, image_count: SwapImageIndex) -> Self {
SwapchainConfig {
present_mode: PresentMode::FIFO,
composite_alpha_mode: CompositeAlphaMode::OPAQUE,
format,
extent: Extent2D { width, height },
image_count,
image_layers: 1,
image_usage: DEFAULT_USAGE,
}
}
pub fn framebuffer_attachment(&self) -> image::FramebufferAttachment {
image::FramebufferAttachment {
usage: self.image_usage,
view_caps: image::ViewCapabilities::empty(),
format: self.format,
}
}
pub fn from_caps(caps: &SurfaceCapabilities, format: Format, default_extent: Extent2D) -> Self {
let composite_alpha_mode = if caps
.composite_alpha_modes
.contains(CompositeAlphaMode::INHERIT)
{
CompositeAlphaMode::INHERIT
} else if caps
.composite_alpha_modes
.contains(CompositeAlphaMode::OPAQUE)
{
CompositeAlphaMode::OPAQUE
} else {
panic!("neither INHERIT or OPAQUE CompositeAlphaMode(s) are supported")
};
let present_mode = if caps.present_modes.contains(PresentMode::MAILBOX) {
PresentMode::MAILBOX
} else if caps.present_modes.contains(PresentMode::FIFO) {
PresentMode::FIFO
} else {
panic!("FIFO PresentMode is not supported")
};
SwapchainConfig {
present_mode,
composite_alpha_mode,
format,
extent: caps.clamped_extent(default_extent),
image_count: DEFAULT_IMAGE_COUNT
.max(*caps.image_count.start())
.min(*caps.image_count.end()),
image_layers: 1,
image_usage: DEFAULT_USAGE,
}
}
pub fn with_present_mode(mut self, mode: PresentMode) -> Self {
self.present_mode = mode;
self
}
pub fn with_composite_alpha_mode(mut self, mode: CompositeAlphaMode) -> Self {
self.composite_alpha_mode = mode;
self
}
pub fn with_image_usage(mut self, usage: image::Usage) -> Self {
self.image_usage = usage;
self
}
pub fn with_image_count(mut self, count: SwapImageIndex) -> Self {
self.image_count = count;
self
}
}
#[derive(Debug)]
pub struct Suboptimal;
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
#[error("Swapchain is out of date and needs to be re-created")]
pub struct OutOfDate;
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum AcquireError {
#[error(transparent)]
OutOfMemory(#[from] device::OutOfMemory),
#[error("No image ready (timeout: {timeout:})")]
NotReady {
timeout: bool,
},
#[error(transparent)]
OutOfDate(#[from] OutOfDate),
#[error(transparent)]
SurfaceLost(#[from] SurfaceLost),
#[error(transparent)]
DeviceLost(#[from] device::DeviceLost),
}
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum PresentError {
#[error(transparent)]
OutOfMemory(#[from] device::OutOfMemory),
#[error(transparent)]
OutOfDate(#[from] OutOfDate),
#[error(transparent)]
SurfaceLost(#[from] SurfaceLost),
#[error(transparent)]
DeviceLost(#[from] device::DeviceLost),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum InitError {
#[error("Specified window handle is unsupported")]
UnsupportedWindowHandle,
}