use crate::pipeline::graphics::GraphicsPipelineCreationError;
use crate::pipeline::DynamicState;
use crate::{device::Device, Version};
use smallvec::SmallVec;
use std::collections::HashMap;
use std::{ops::Range, ptr};
#[derive(Debug, Clone)]
pub enum ViewportState {
Fixed {
data: Vec<(Viewport, Scissor)>,
},
FixedViewport {
viewports: Vec<Viewport>,
scissor_count_dynamic: bool,
},
FixedScissor {
scissors: Vec<Scissor>,
viewport_count_dynamic: bool,
},
Dynamic {
count: u32,
viewport_count_dynamic: bool,
scissor_count_dynamic: bool,
},
}
impl ViewportState {
#[inline]
pub fn new() -> Self {
Self::Fixed { data: Vec::new() }
}
#[inline]
pub fn viewport_fixed_scissor_fixed(
data: impl IntoIterator<Item = (Viewport, Scissor)>,
) -> Self {
Self::Fixed {
data: data.into_iter().collect(),
}
}
#[inline]
pub fn viewport_fixed_scissor_irrelevant(data: impl IntoIterator<Item = Viewport>) -> Self {
Self::Fixed {
data: data
.into_iter()
.map(|viewport| (viewport, Scissor::irrelevant()))
.collect(),
}
}
#[inline]
pub fn viewport_dynamic_scissor_irrelevant() -> Self {
Self::FixedScissor {
scissors: vec![Scissor::irrelevant()],
viewport_count_dynamic: false,
}
}
#[inline]
pub fn viewport_dynamic_scissor_dynamic(count: u32) -> Self {
Self::Dynamic {
count,
viewport_count_dynamic: false,
scissor_count_dynamic: false,
}
}
#[inline]
pub fn viewport_count_dynamic_scissor_count_dynamic() -> Self {
Self::Dynamic {
count: 0,
viewport_count_dynamic: true,
scissor_count_dynamic: true,
}
}
pub fn count(&self) -> Option<u32> {
Some(match *self {
ViewportState::Fixed { ref data } => data.len() as u32,
ViewportState::FixedViewport { ref viewports, .. } => viewports.len() as u32,
ViewportState::FixedScissor { ref scissors, .. } => scissors.len() as u32,
ViewportState::Dynamic {
viewport_count_dynamic: true,
scissor_count_dynamic: true,
..
} => return None,
ViewportState::Dynamic { count, .. } => count,
})
}
pub(crate) fn to_vulkan_viewports_scissors(
&self,
device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
) -> Result<
(
u32,
SmallVec<[ash::vk::Viewport; 2]>,
u32,
SmallVec<[ash::vk::Rect2D; 2]>,
),
GraphicsPipelineCreationError,
> {
Ok(match self {
ViewportState::Fixed { data } => {
let count = data.len() as u32;
assert!(count != 0); let viewports = data
.iter()
.map(|e| e.0.clone().into())
.collect::<SmallVec<[ash::vk::Viewport; 2]>>();
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
let scissors = data
.iter()
.map(|e| e.1.clone().into())
.collect::<SmallVec<[ash::vk::Rect2D; 2]>>();
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
(count, viewports, count, scissors)
}
ViewportState::FixedViewport {
viewports,
scissor_count_dynamic,
} => {
let viewport_count = viewports.len() as u32;
assert!(viewport_count != 0); let viewports = viewports
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Viewport; 2]>>();
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
let scissor_count = if *scissor_count_dynamic {
if !(device.api_version() >= Version::V1_3
|| device.enabled_features().extended_dynamic_state)
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::FixedViewport::scissor_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Scissor, true);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
viewport_count
};
(viewport_count, viewports, scissor_count, SmallVec::new())
}
ViewportState::FixedScissor {
scissors,
viewport_count_dynamic,
} => {
let scissor_count = scissors.len() as u32;
assert!(scissor_count != 0); let scissors = scissors
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Rect2D; 2]>>();
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
let viewport_count = if *viewport_count_dynamic {
if !(device.api_version() >= Version::V1_3
|| device.enabled_features().extended_dynamic_state)
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::FixedScissor::viewport_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Viewport, true);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
scissor_count
};
(viewport_count, SmallVec::new(), scissor_count, scissors)
}
ViewportState::Dynamic {
count,
viewport_count_dynamic,
scissor_count_dynamic,
} => {
if !(*viewport_count_dynamic && *scissor_count_dynamic) {
assert!(*count != 0); }
let viewport_count = if *viewport_count_dynamic {
if !(device.api_version() >= Version::V1_3
|| device.enabled_features().extended_dynamic_state)
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason:
"ViewportState::Dynamic::viewport_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Viewport, true);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
*count
};
let scissor_count = if *scissor_count_dynamic {
if !(device.api_version() >= Version::V1_3
|| device.enabled_features().extended_dynamic_state)
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::Dynamic::scissor_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Scissor, true);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
*count
};
(
viewport_count,
SmallVec::new(),
scissor_count,
SmallVec::new(),
)
}
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut HashMap<DynamicState, bool>,
viewport_count: u32,
viewports: &[ash::vk::Viewport],
scissor_count: u32,
scissors: &[ash::vk::Rect2D],
) -> Result<ash::vk::PipelineViewportStateCreateInfo, GraphicsPipelineCreationError> {
let viewport_scissor_count = u32::max(viewport_count, scissor_count);
if viewport_scissor_count > 1 && !device.enabled_features().multi_viewport {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "multi_viewport",
reason: "ViewportState viewport/scissor count was greater than 1",
});
}
if viewport_scissor_count > device.physical_device().properties().max_viewports {
return Err(GraphicsPipelineCreationError::MaxViewportsExceeded {
obtained: viewport_scissor_count,
max: device.physical_device().properties().max_viewports,
});
}
for vp in viewports.iter() {
if vp.width
> device
.physical_device()
.properties()
.max_viewport_dimensions[0] as f32
|| vp.height
> device
.physical_device()
.properties()
.max_viewport_dimensions[1] as f32
{
return Err(GraphicsPipelineCreationError::MaxViewportDimensionsExceeded);
}
if vp.x < device.physical_device().properties().viewport_bounds_range[0]
|| vp.x + vp.width > device.physical_device().properties().viewport_bounds_range[1]
|| vp.y < device.physical_device().properties().viewport_bounds_range[0]
|| vp.y + vp.height > device.physical_device().properties().viewport_bounds_range[1]
{
return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded);
}
}
Ok(ash::vk::PipelineViewportStateCreateInfo {
flags: ash::vk::PipelineViewportStateCreateFlags::empty(),
viewport_count,
p_viewports: if viewports.is_empty() {
ptr::null()
} else {
viewports.as_ptr()
}, scissor_count,
p_scissors: if scissors.is_empty() {
ptr::null()
} else {
scissors.as_ptr()
}, ..Default::default()
})
}
}
impl Default for ViewportState {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Viewport {
pub origin: [f32; 2],
pub dimensions: [f32; 2],
pub depth_range: Range<f32>,
}
impl From<Viewport> for ash::vk::Viewport {
#[inline]
fn from(val: Viewport) -> Self {
ash::vk::Viewport {
x: val.origin[0],
y: val.origin[1],
width: val.dimensions[0],
height: val.dimensions[1],
min_depth: val.depth_range.start,
max_depth: val.depth_range.end,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Scissor {
pub origin: [u32; 2],
pub dimensions: [u32; 2],
}
impl Scissor {
#[inline]
pub fn irrelevant() -> Scissor {
Scissor {
origin: [0, 0],
dimensions: [0x7fffffff, 0x7fffffff],
}
}
}
impl Default for Scissor {
#[inline]
fn default() -> Scissor {
Scissor::irrelevant()
}
}
impl From<Scissor> for ash::vk::Rect2D {
#[inline]
fn from(val: Scissor) -> Self {
ash::vk::Rect2D {
offset: ash::vk::Offset2D {
x: val.origin[0] as i32,
y: val.origin[1] as i32,
},
extent: ash::vk::Extent2D {
width: val.dimensions[0],
height: val.dimensions[1],
},
}
}
}