• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 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 use smallvec::SmallVec;
11 use std::error;
12 use std::fmt;
13 use std::marker::PhantomData;
14 use std::ptr;
15 
16 use crate::device::DeviceOwned;
17 use crate::device::Queue;
18 use crate::swapchain::PresentRegion;
19 use crate::swapchain::Swapchain;
20 use crate::sync::Semaphore;
21 
22 use crate::check_errors;
23 use crate::Error;
24 use crate::OomError;
25 use crate::SynchronizedVulkanObject;
26 use crate::VulkanObject;
27 
28 /// Prototype for a submission that presents a swapchain on the screen.
29 // TODO: example here
30 pub struct SubmitPresentBuilder<'a> {
31     wait_semaphores: SmallVec<[ash::vk::Semaphore; 8]>,
32     swapchains: SmallVec<[ash::vk::SwapchainKHR; 4]>,
33     image_indices: SmallVec<[u32; 4]>,
34     present_regions: SmallVec<[ash::vk::PresentRegionKHR; 4]>,
35     rect_layers: SmallVec<[ash::vk::RectLayerKHR; 4]>,
36     marker: PhantomData<&'a ()>,
37 }
38 
39 impl<'a> SubmitPresentBuilder<'a> {
40     /// Builds a new empty `SubmitPresentBuilder`.
41     #[inline]
new() -> SubmitPresentBuilder<'a>42     pub fn new() -> SubmitPresentBuilder<'a> {
43         SubmitPresentBuilder {
44             wait_semaphores: SmallVec::new(),
45             swapchains: SmallVec::new(),
46             image_indices: SmallVec::new(),
47             present_regions: SmallVec::new(),
48             rect_layers: SmallVec::new(),
49             marker: PhantomData,
50         }
51     }
52 
53     /// Adds a semaphore to be waited upon before the presents are executed.
54     ///
55     /// # Safety
56     ///
57     /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed
58     ///   that the GPU has presented the swapchains.
59     ///
60     /// - If you submit this builder, no other queue must be waiting on these semaphores. In other
61     ///   words, each semaphore signal can only correspond to one semaphore wait.
62     ///
63     /// - If you submit this builder, the semaphores must be signaled when the queue execution
64     ///   reaches this submission, or there must be one or more submissions in queues that are
65     ///   going to signal these semaphores. In other words, you must not block the queue with
66     ///   semaphores that can't get signaled.
67     ///
68     /// - The swapchains and semaphores must all belong to the same device.
69     ///
70     #[inline]
add_wait_semaphore(&mut self, semaphore: &'a Semaphore)71     pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
72         self.wait_semaphores.push(semaphore.internal_object());
73     }
74 
75     /// Adds an image of a swapchain to be presented.
76     ///
77     /// Allows to specify a present region.
78     /// Areas outside the present region *can* be ignored by the Vulkan implementation for
79     /// optimizations purposes.
80     ///
81     /// If `VK_KHR_incremental_present` is not enabled, the `present_region` parameter is ignored.
82     ///
83     /// # Safety
84     ///
85     /// - If you submit this builder, the swapchain must be kept alive until you are
86     ///   guaranteed that the GPU has finished presenting.
87     ///
88     /// - The swapchains and semaphores must all belong to the same device.
89     ///
90     #[inline]
add_swapchain<W>( &mut self, swapchain: &'a Swapchain<W>, image_num: u32, present_region: Option<&'a PresentRegion>, )91     pub unsafe fn add_swapchain<W>(
92         &mut self,
93         swapchain: &'a Swapchain<W>,
94         image_num: u32,
95         present_region: Option<&'a PresentRegion>,
96     ) {
97         debug_assert!(image_num < swapchain.num_images());
98 
99         if swapchain
100             .device()
101             .enabled_extensions()
102             .khr_incremental_present
103         {
104             let vk_present_region = match present_region {
105                 Some(present_region) => {
106                     assert!(present_region.is_compatible_with(swapchain));
107                     for rectangle in &present_region.rectangles {
108                         self.rect_layers.push(rectangle.into());
109                     }
110                     ash::vk::PresentRegionKHR {
111                         rectangle_count: present_region.rectangles.len() as u32,
112                         // Set this to null for now; in submit fill it with self.rect_layers
113                         p_rectangles: ptr::null(),
114                     }
115                 }
116                 None => ash::vk::PresentRegionKHR {
117                     rectangle_count: 0,
118                     p_rectangles: ptr::null(),
119                 },
120             };
121             self.present_regions.push(vk_present_region);
122         }
123 
124         self.swapchains.push(swapchain.internal_object());
125         self.image_indices.push(image_num);
126     }
127 
128     /// Submits the command. Calls `vkQueuePresentKHR`.
129     ///
130     /// # Panic
131     ///
132     /// Panics if no swapchain image has been added to the builder.
133     ///
submit(mut self, queue: &Queue) -> Result<(), SubmitPresentError>134     pub fn submit(mut self, queue: &Queue) -> Result<(), SubmitPresentError> {
135         unsafe {
136             debug_assert_eq!(self.swapchains.len(), self.image_indices.len());
137             assert!(
138                 !self.swapchains.is_empty(),
139                 "Tried to submit a present command without any swapchain"
140             );
141 
142             let present_regions = {
143                 if !self.present_regions.is_empty() {
144                     debug_assert!(queue.device().enabled_extensions().khr_incremental_present);
145                     debug_assert_eq!(self.swapchains.len(), self.present_regions.len());
146                     let mut current_index = 0;
147                     for present_region in &mut self.present_regions {
148                         present_region.p_rectangles = self.rect_layers[current_index..].as_ptr();
149                         current_index += present_region.rectangle_count as usize;
150                     }
151                     Some(ash::vk::PresentRegionsKHR {
152                         swapchain_count: self.present_regions.len() as u32,
153                         p_regions: self.present_regions.as_ptr(),
154                         ..Default::default()
155                     })
156                 } else {
157                     None
158                 }
159             };
160 
161             let mut results = vec![ash::vk::Result::SUCCESS; self.swapchains.len()];
162 
163             let fns = queue.device().fns();
164             let queue = queue.internal_object_guard();
165 
166             let infos = ash::vk::PresentInfoKHR {
167                 p_next: present_regions
168                     .as_ref()
169                     .map(|pr| pr as *const ash::vk::PresentRegionsKHR as *const _)
170                     .unwrap_or(ptr::null()),
171                 wait_semaphore_count: self.wait_semaphores.len() as u32,
172                 p_wait_semaphores: self.wait_semaphores.as_ptr(),
173                 swapchain_count: self.swapchains.len() as u32,
174                 p_swapchains: self.swapchains.as_ptr(),
175                 p_image_indices: self.image_indices.as_ptr(),
176                 p_results: results.as_mut_ptr(),
177                 ..Default::default()
178             };
179 
180             check_errors(fns.khr_swapchain.queue_present_khr(*queue, &infos))?;
181 
182             for result in results {
183                 check_errors(result)?;
184             }
185 
186             Ok(())
187         }
188     }
189 }
190 
191 impl<'a> fmt::Debug for SubmitPresentBuilder<'a> {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>192     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
193         fmt.debug_struct("SubmitPresentBuilder")
194             .field("wait_semaphores", &self.wait_semaphores)
195             .field("swapchains", &self.swapchains)
196             .field("image_indices", &self.image_indices)
197             .finish()
198     }
199 }
200 
201 /// Error that can happen when submitting the present prototype.
202 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
203 #[repr(u32)]
204 pub enum SubmitPresentError {
205     /// Not enough memory.
206     OomError(OomError),
207 
208     /// The connection to the device has been lost.
209     DeviceLost,
210 
211     /// The surface is no longer accessible and must be recreated.
212     SurfaceLost,
213 
214     /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for
215     /// implementation-specific reasons outside of the application’s control.
216     FullscreenExclusiveLost,
217 
218     /// The surface has changed in a way that makes the swapchain unusable. You must query the
219     /// surface's new properties and recreate a new swapchain if you want to continue drawing.
220     OutOfDate,
221 }
222 
223 impl error::Error for SubmitPresentError {
224     #[inline]
source(&self) -> Option<&(dyn error::Error + 'static)>225     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
226         match *self {
227             SubmitPresentError::OomError(ref err) => Some(err),
228             _ => None,
229         }
230     }
231 }
232 
233 impl fmt::Display for SubmitPresentError {
234     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>235     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
236         write!(
237             fmt,
238             "{}",
239             match *self {
240                 SubmitPresentError::OomError(_) => "not enough memory",
241                 SubmitPresentError::DeviceLost => "the connection to the device has been lost",
242                 SubmitPresentError::SurfaceLost =>
243                     "the surface of this swapchain is no longer valid",
244                 SubmitPresentError::OutOfDate => "the swapchain needs to be recreated",
245                 SubmitPresentError::FullscreenExclusiveLost => {
246                     "the swapchain no longer has fullscreen exclusivity"
247                 }
248             }
249         )
250     }
251 }
252 
253 impl From<Error> for SubmitPresentError {
254     #[inline]
from(err: Error) -> SubmitPresentError255     fn from(err: Error) -> SubmitPresentError {
256         match err {
257             err @ Error::OutOfHostMemory => SubmitPresentError::OomError(OomError::from(err)),
258             err @ Error::OutOfDeviceMemory => SubmitPresentError::OomError(OomError::from(err)),
259             Error::DeviceLost => SubmitPresentError::DeviceLost,
260             Error::SurfaceLost => SubmitPresentError::SurfaceLost,
261             Error::OutOfDate => SubmitPresentError::OutOfDate,
262             Error::FullscreenExclusiveLost => SubmitPresentError::FullscreenExclusiveLost,
263             _ => panic!("unexpected error: {:?}", err),
264         }
265     }
266 }
267 
268 #[cfg(test)]
269 mod tests {
270     use super::*;
271 
272     #[test]
no_swapchain_added()273     fn no_swapchain_added() {
274         let (_, queue) = gfx_dev_and_queue!();
275         assert_should_panic!("Tried to submit a present command without any swapchain", {
276             let _ = SubmitPresentBuilder::new().submit(&queue);
277         });
278     }
279 }
280