use crate::{device::Device, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version};
use smallvec::{smallvec, SmallVec};
use std::ops::RangeInclusive;
#[derive(Clone, Debug)]
pub struct ViewportState {
pub viewports: SmallVec<[Viewport; 1]>,
pub scissors: SmallVec<[Scissor; 1]>,
pub _ne: crate::NonExhaustive,
}
impl Default for ViewportState {
#[inline]
fn default() -> Self {
Self {
viewports: smallvec![Viewport::default()],
scissors: smallvec![Scissor::default()],
_ne: crate::NonExhaustive(()),
}
}
}
impl ViewportState {
#[deprecated(since = "0.34.0")]
#[inline]
pub fn new() -> Self {
Self {
viewports: SmallVec::new(),
scissors: SmallVec::new(),
_ne: crate::NonExhaustive(()),
}
}
#[deprecated(since = "0.34.0")]
pub fn viewport_fixed_scissor_fixed(
data: impl IntoIterator<Item = (Viewport, Scissor)>,
) -> Self {
let (viewports, scissors) = data.into_iter().unzip();
Self {
viewports,
scissors,
_ne: crate::NonExhaustive(()),
}
}
#[deprecated(since = "0.34.0")]
pub fn viewport_fixed_scissor_irrelevant(data: impl IntoIterator<Item = Viewport>) -> Self {
let viewports: SmallVec<_> = data.into_iter().collect();
let scissors = smallvec![Scissor::default(); viewports.len()];
Self {
viewports,
scissors,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let Self {
viewports,
scissors,
_ne: _,
} = self;
let properties = device.physical_device().properties();
for (index, viewport) in viewports.iter().enumerate() {
viewport
.validate(device)
.map_err(|err| err.add_context(format!("viewports[{}].0", index)))?;
}
for (index, scissor) in scissors.iter().enumerate() {
let &Scissor { offset, extent } = scissor;
if (i32::try_from(offset[0]).ok())
.zip(i32::try_from(extent[0]).ok())
.and_then(|(o, e)| o.checked_add(e))
.is_none()
{
return Err(Box::new(ValidationError {
context: format!("scissors[{}]", index).into(),
problem: "`offset[0] + extent[0]` is greater than `i32::MAX`".into(),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-offset-02822"],
..Default::default()
}));
}
if (i32::try_from(offset[1]).ok())
.zip(i32::try_from(extent[1]).ok())
.and_then(|(o, e)| o.checked_add(e))
.is_none()
{
return Err(Box::new(ValidationError {
context: format!("scissors[{}]", index).into(),
problem: "`offset[1] + extent[1]` is greater than `i32::MAX`".into(),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-offset-02823"],
..Default::default()
}));
}
}
if viewports.len() > 1 && !device.enabled_features().multi_viewport {
return Err(Box::new(ValidationError {
context: "viewports".into(),
problem: "the length is greater than 1".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"multi_viewport",
)])]),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216"],
}));
}
if scissors.len() > 1 && !device.enabled_features().multi_viewport {
return Err(Box::new(ValidationError {
context: "scissors".into(),
problem: "the length is greater than 1".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"multi_viewport",
)])]),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"],
}));
}
if viewports.len() > properties.max_viewports as usize {
return Err(Box::new(ValidationError {
context: "viewports".into(),
problem: "the length exceeds the `max_viewports` limit".into(),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218"],
..Default::default()
}));
}
if scissors.len() > properties.max_viewports as usize {
return Err(Box::new(ValidationError {
context: "scissors".into(),
problem: "the length exceeds the `max_viewports` limit".into(),
vuids: &["VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219"],
..Default::default()
}));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Viewport {
pub offset: [f32; 2],
pub extent: [f32; 2],
pub depth_range: RangeInclusive<f32>,
}
impl Default for Viewport {
#[inline]
fn default() -> Self {
Self {
offset: [0.0; 2],
extent: [1.0; 2],
depth_range: 0.0..=1.0,
}
}
}
impl Viewport {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
offset,
extent,
ref depth_range,
} = self;
let properties = device.physical_device().properties();
if extent[0] <= 0.0 {
return Err(Box::new(ValidationError {
context: "extent[0]".into(),
problem: "is not greater than zero".into(),
vuids: &["VUID-VkViewport-width-01770"],
..Default::default()
}));
}
if extent[0] > properties.max_viewport_dimensions[0] as f32 {
return Err(Box::new(ValidationError {
context: "extent[0]".into(),
problem: "exceeds the `max_viewport_dimensions[0]` limit".into(),
vuids: &["VUID-VkViewport-width-01771"],
..Default::default()
}));
}
if extent[1] <= 0.0
&& !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_maintenance1)
{
return Err(Box::new(ValidationError {
context: "extent[1]".into(),
problem: "is not greater than zero".into(),
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance1")]),
]),
vuids: &["VUID-VkViewport-apiVersion-07917"],
}));
}
if extent[1].abs() > properties.max_viewport_dimensions[1] as f32 {
return Err(Box::new(ValidationError {
context: "extent[1]".into(),
problem: "exceeds the `max_viewport_dimensions[1]` limit".into(),
vuids: &["VUID-VkViewport-height-01773"],
..Default::default()
}));
}
if offset[0] < properties.viewport_bounds_range[0] {
return Err(Box::new(ValidationError {
problem: "`offset[0]` is less than the `viewport_bounds_range[0]` property".into(),
vuids: &["VUID-VkViewport-x-01774"],
..Default::default()
}));
}
if offset[0] + extent[0] > properties.viewport_bounds_range[1] {
return Err(Box::new(ValidationError {
problem: "`offset[0] + extent[0]` is greater than the \
`viewport_bounds_range[1]` property"
.into(),
vuids: &["VUID-VkViewport-x-01232"],
..Default::default()
}));
}
if offset[1] < properties.viewport_bounds_range[0] {
return Err(Box::new(ValidationError {
problem: "`offset[1]` is less than the `viewport_bounds_range[0]` property".into(),
vuids: &["VUID-VkViewport-y-01775"],
..Default::default()
}));
}
if offset[1] > properties.viewport_bounds_range[1] {
return Err(Box::new(ValidationError {
problem: "`offset[1]` is greater than the `viewport_bounds_range[1]` property"
.into(),
vuids: &["VUID-VkViewport-y-01776"],
..Default::default()
}));
}
if offset[1] + extent[1] < properties.viewport_bounds_range[0] {
return Err(Box::new(ValidationError {
problem: "`offset[1] + extent[1]` is less than the \
`viewport_bounds_range[0]` property"
.into(),
vuids: &["VUID-VkViewport-y-01777"],
..Default::default()
}));
}
if offset[1] + extent[1] > properties.viewport_bounds_range[1] {
return Err(Box::new(ValidationError {
problem: "`offset[1] + extent[1]` is greater than the \
`viewport_bounds_range[1]` property"
.into(),
vuids: &["VUID-VkViewport-y-01233"],
..Default::default()
}));
}
if !device.enabled_extensions().ext_depth_range_unrestricted {
if *depth_range.start() < 0.0 || *depth_range.start() > 1.0 {
return Err(Box::new(ValidationError {
problem: "`depth_range.start` is not between 0.0 and 1.0 inclusive".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_depth_range_unrestricted",
)])]),
vuids: &["VUID-VkViewport-minDepth-01234"],
..Default::default()
}));
}
if *depth_range.end() < 0.0 || *depth_range.end() > 1.0 {
return Err(Box::new(ValidationError {
problem: "`depth_range.end` is not between 0.0 and 1.0 inclusive".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_depth_range_unrestricted",
)])]),
vuids: &["VUID-VkViewport-maxDepth-01235"],
..Default::default()
}));
}
}
Ok(())
}
}
impl From<&Viewport> for ash::vk::Viewport {
#[inline]
fn from(val: &Viewport) -> Self {
ash::vk::Viewport {
x: val.offset[0],
y: val.offset[1],
width: val.extent[0],
height: val.extent[1],
min_depth: *val.depth_range.start(),
max_depth: *val.depth_range.end(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Scissor {
pub offset: [u32; 2],
pub extent: [u32; 2],
}
impl Default for Scissor {
#[inline]
fn default() -> Scissor {
Self {
offset: [0; 2],
extent: [i32::MAX as u32; 2],
}
}
}
impl Scissor {
#[deprecated(since = "0.34.0", note = "use `Scissor::default` instead")]
#[inline]
pub fn irrelevant() -> Scissor {
Self::default()
}
}
impl From<&Scissor> for ash::vk::Rect2D {
#[inline]
fn from(val: &Scissor) -> Self {
ash::vk::Rect2D {
offset: ash::vk::Offset2D {
x: val.offset[0] as i32,
y: val.offset[1] as i32,
},
extent: ash::vk::Extent2D {
width: val.extent[0],
height: val.extent[1],
},
}
}
}
impl From<ash::vk::Rect2D> for Scissor {
#[inline]
fn from(val: ash::vk::Rect2D) -> Self {
Scissor {
offset: [val.offset.x as u32, val.offset.y as u32],
extent: [val.extent.width, val.extent.height],
}
}
}