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