1 // Copyright (c) 2016 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 //! Configures the area of the framebuffer that pixels will be written to. 11 //! 12 //! There are two different concepts to determine where things will be drawn: 13 //! 14 //! - The viewport is the region of the image which corresponds to the vertex coordinates `-1.0` to 15 //! `1.0`. 16 //! - Any pixel outside of the scissor box will be discarded. 17 //! 18 //! In other words, modifying the viewport will stretch the image, while modifying the scissor 19 //! box acts like a filter. 20 //! 21 //! It is legal and sensible to use a viewport that is larger than the target image or that 22 //! only partially overlaps the target image. 23 //! 24 //! # Multiple viewports 25 //! 26 //! In most situations, you only need a single viewport and a single scissor box. 27 //! 28 //! If, however, you use a geometry shader, you can specify multiple viewports and scissor boxes. 29 //! Then in your geometry shader you can specify in which viewport and scissor box the primitive 30 //! should be written to. In GLSL this is done by writing to the special variable 31 //! `gl_ViewportIndex`. 32 //! 33 //! If you don't use a geometry shader or use a geometry shader where don't set which viewport to 34 //! use, then the first viewport and scissor box will be used. 35 //! 36 //! # Dynamic and fixed 37 //! 38 //! Vulkan allows four different setups: 39 //! 40 //! - The state of both the viewports and scissor boxes is known at pipeline creation. 41 //! - The state of viewports is known at pipeline creation, but the state of scissor boxes is 42 //! only known when submitting the draw command. 43 //! - The state of scissor boxes is known at pipeline creation, but the state of viewports is 44 //! only known when submitting the draw command. 45 //! - The state of both the viewports and scissor boxes is only known when submitting the 46 //! draw command. 47 //! 48 //! In all cases the number of viewports and scissor boxes must be the same. 49 //! 50 51 use std::ops::Range; 52 53 /// List of viewports and scissors that are used when creating a graphics pipeline object. 54 /// 55 /// Note that the number of viewports and scissors must be the same. 56 #[derive(Debug, Clone)] 57 pub enum ViewportState { 58 /// The state is known in advance. 59 Fixed { 60 /// State of the viewports and scissors. 61 data: Vec<(Viewport, Scissor)>, 62 }, 63 64 /// The state of viewports is known in advance, but the state of scissors is dynamic and will 65 /// be set when drawing. 66 FixedViewport { 67 /// State of the viewports. 68 viewports: Vec<Viewport>, 69 70 /// Sets whether the scissor count is also dynamic, or only the scissors themselves. 71 /// 72 /// If set to `true`, the device API version must be at least 1.3, or the 73 /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must 74 /// be enabled on the device. 75 scissor_count_dynamic: bool, 76 }, 77 78 /// The state of scissors is known in advance, but the state of viewports is dynamic and will 79 /// be set when drawing. 80 FixedScissor { 81 /// State of the scissors. 82 scissors: Vec<Scissor>, 83 84 /// Sets whether the viewport count is also dynamic, or only the viewports themselves. 85 /// 86 /// If set to `true`, the device API version must be at least 1.3, or the 87 /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must 88 /// be enabled on the device. 89 viewport_count_dynamic: bool, 90 }, 91 92 /// The state of both the viewports and scissors is dynamic and will be set when drawing. 93 Dynamic { 94 /// Number of viewports and scissors. 95 /// 96 /// This is ignored if both `viewport_count_dynamic` and `scissor_count_dynamic` are `true`. 97 count: u32, 98 99 /// Sets whether the viewport count is also dynamic, or only the viewports themselves. 100 /// 101 /// If set to `true`, the device API version must be at least 1.3, or the 102 /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must 103 /// be enabled on the device. 104 viewport_count_dynamic: bool, 105 106 /// Sets whether the scissor count is also dynamic, or only the scissors themselves. 107 /// 108 /// If set to `true`, the device API version must be at least 1.3, or the 109 /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must 110 /// be enabled on the device. 111 scissor_count_dynamic: bool, 112 }, 113 } 114 115 impl ViewportState { 116 /// Creates a `ViewportState` with fixed state and no viewports or scissors. 117 #[inline] new() -> Self118 pub fn new() -> Self { 119 Self::Fixed { data: Vec::new() } 120 } 121 122 /// Creates a `ViewportState` with fixed state from the given viewports and scissors. viewport_fixed_scissor_fixed( data: impl IntoIterator<Item = (Viewport, Scissor)>, ) -> Self123 pub fn viewport_fixed_scissor_fixed( 124 data: impl IntoIterator<Item = (Viewport, Scissor)>, 125 ) -> Self { 126 Self::Fixed { 127 data: data.into_iter().collect(), 128 } 129 } 130 131 /// Creates a `ViewportState` with fixed state from the given viewports, and matching scissors 132 /// that cover the whole viewport. viewport_fixed_scissor_irrelevant(data: impl IntoIterator<Item = Viewport>) -> Self133 pub fn viewport_fixed_scissor_irrelevant(data: impl IntoIterator<Item = Viewport>) -> Self { 134 Self::Fixed { 135 data: data 136 .into_iter() 137 .map(|viewport| (viewport, Scissor::irrelevant())) 138 .collect(), 139 } 140 } 141 142 /// Creates a `ViewportState` with dynamic viewport, and a single scissor that always covers 143 /// the whole viewport. 144 #[inline] viewport_dynamic_scissor_irrelevant() -> Self145 pub fn viewport_dynamic_scissor_irrelevant() -> Self { 146 Self::FixedScissor { 147 scissors: vec![Scissor::irrelevant()], 148 viewport_count_dynamic: false, 149 } 150 } 151 152 /// Creates a `ViewportState` with dynamic viewports and scissors, but a fixed count. 153 #[inline] viewport_dynamic_scissor_dynamic(count: u32) -> Self154 pub fn viewport_dynamic_scissor_dynamic(count: u32) -> Self { 155 Self::Dynamic { 156 count, 157 viewport_count_dynamic: false, 158 scissor_count_dynamic: false, 159 } 160 } 161 162 /// Creates a `ViewportState` with dynamic viewport count and scissor count. 163 #[inline] viewport_count_dynamic_scissor_count_dynamic() -> Self164 pub fn viewport_count_dynamic_scissor_count_dynamic() -> Self { 165 Self::Dynamic { 166 count: 0, 167 viewport_count_dynamic: true, 168 scissor_count_dynamic: true, 169 } 170 } 171 172 /// Returns the number of viewports and scissors. 173 /// 174 /// `None` is returned if both `viewport_count_dynamic` and `scissor_count_dynamic` are `true`. 175 #[inline] count(&self) -> Option<u32>176 pub fn count(&self) -> Option<u32> { 177 Some(match *self { 178 ViewportState::Fixed { ref data } => data.len() as u32, 179 ViewportState::FixedViewport { ref viewports, .. } => viewports.len() as u32, 180 ViewportState::FixedScissor { ref scissors, .. } => scissors.len() as u32, 181 ViewportState::Dynamic { 182 viewport_count_dynamic: true, 183 scissor_count_dynamic: true, 184 .. 185 } => return None, 186 ViewportState::Dynamic { count, .. } => count, 187 }) 188 } 189 } 190 191 impl Default for ViewportState { 192 /// Returns [`ViewportState::new()`]. 193 #[inline] default() -> Self194 fn default() -> Self { 195 Self::new() 196 } 197 } 198 199 /// State of a single viewport. 200 // FIXME: check that: 201 // x + width must be less than or equal to viewportBoundsRange[0] 202 // y + height must be less than or equal to viewportBoundsRange[1] 203 #[derive(Debug, Clone, PartialEq)] 204 pub struct Viewport { 205 /// Coordinates in pixels of the top-left hand corner of the viewport. 206 pub origin: [f32; 2], 207 208 /// Dimensions in pixels of the viewport. 209 pub dimensions: [f32; 2], 210 211 /// Minimum and maximum values of the depth. 212 /// 213 /// The values `0.0` to `1.0` of each vertex's Z coordinate will be mapped to this 214 /// `depth_range` before being compared to the existing depth value. 215 /// 216 /// This is equivalents to `glDepthRange` in OpenGL, except that OpenGL uses the Z coordinate 217 /// range from `-1.0` to `1.0` instead. 218 pub depth_range: Range<f32>, 219 } 220 221 impl From<Viewport> for ash::vk::Viewport { 222 #[inline] from(val: Viewport) -> Self223 fn from(val: Viewport) -> Self { 224 ash::vk::Viewport { 225 x: val.origin[0], 226 y: val.origin[1], 227 width: val.dimensions[0], 228 height: val.dimensions[1], 229 min_depth: val.depth_range.start, 230 max_depth: val.depth_range.end, 231 } 232 } 233 } 234 235 /// State of a single scissor box. 236 // FIXME: add a check: 237 // Evaluation of (offset.x + extent.width) must not cause a signed integer addition overflow 238 // Evaluation of (offset.y + extent.height) must not cause a signed integer addition overflow 239 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 240 pub struct Scissor { 241 /// Coordinates in pixels of the top-left hand corner of the box. 242 pub origin: [u32; 2], 243 244 /// Dimensions in pixels of the box. 245 pub dimensions: [u32; 2], 246 } 247 248 impl Scissor { 249 /// Returns a scissor that, when used, will instruct the pipeline to draw to the entire 250 /// framebuffer no matter its size. 251 #[inline] irrelevant() -> Scissor252 pub fn irrelevant() -> Scissor { 253 Scissor { 254 origin: [0, 0], 255 dimensions: [0x7fffffff, 0x7fffffff], 256 } 257 } 258 } 259 260 impl Default for Scissor { 261 #[inline] default() -> Scissor262 fn default() -> Scissor { 263 Scissor::irrelevant() 264 } 265 } 266 267 impl From<Scissor> for ash::vk::Rect2D { 268 #[inline] from(val: Scissor) -> Self269 fn from(val: Scissor) -> Self { 270 ash::vk::Rect2D { 271 offset: ash::vk::Offset2D { 272 x: val.origin[0] as i32, 273 y: val.origin[1] as i32, 274 }, 275 extent: ash::vk::Extent2D { 276 width: val.dimensions[0], 277 height: val.dimensions[1], 278 }, 279 } 280 } 281 } 282