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