// Copyright (c) 2017 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use smallvec::SmallVec; use std::error; use std::fmt; use std::marker::PhantomData; use std::ptr; use crate::device::DeviceOwned; use crate::device::Queue; use crate::swapchain::PresentRegion; use crate::swapchain::Swapchain; use crate::sync::Semaphore; use crate::check_errors; use crate::Error; use crate::OomError; use crate::SynchronizedVulkanObject; use crate::VulkanObject; /// Prototype for a submission that presents a swapchain on the screen. // TODO: example here pub struct SubmitPresentBuilder<'a> { wait_semaphores: SmallVec<[ash::vk::Semaphore; 8]>, swapchains: SmallVec<[ash::vk::SwapchainKHR; 4]>, image_indices: SmallVec<[u32; 4]>, present_regions: SmallVec<[ash::vk::PresentRegionKHR; 4]>, rect_layers: SmallVec<[ash::vk::RectLayerKHR; 4]>, marker: PhantomData<&'a ()>, } impl<'a> SubmitPresentBuilder<'a> { /// Builds a new empty `SubmitPresentBuilder`. #[inline] pub fn new() -> SubmitPresentBuilder<'a> { SubmitPresentBuilder { wait_semaphores: SmallVec::new(), swapchains: SmallVec::new(), image_indices: SmallVec::new(), present_regions: SmallVec::new(), rect_layers: SmallVec::new(), marker: PhantomData, } } /// Adds a semaphore to be waited upon before the presents are executed. /// /// # Safety /// /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed /// that the GPU has presented the swapchains. /// /// - If you submit this builder, no other queue must be waiting on these semaphores. In other /// words, each semaphore signal can only correspond to one semaphore wait. /// /// - If you submit this builder, the semaphores must be signaled when the queue execution /// reaches this submission, or there must be one or more submissions in queues that are /// going to signal these semaphores. In other words, you must not block the queue with /// semaphores that can't get signaled. /// /// - The swapchains and semaphores must all belong to the same device. /// #[inline] pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) { self.wait_semaphores.push(semaphore.internal_object()); } /// Adds an image of a swapchain to be presented. /// /// Allows to specify a present region. /// Areas outside the present region *can* be ignored by the Vulkan implementation for /// optimizations purposes. /// /// If `VK_KHR_incremental_present` is not enabled, the `present_region` parameter is ignored. /// /// # Safety /// /// - If you submit this builder, the swapchain must be kept alive until you are /// guaranteed that the GPU has finished presenting. /// /// - The swapchains and semaphores must all belong to the same device. /// #[inline] pub unsafe fn add_swapchain( &mut self, swapchain: &'a Swapchain, image_num: u32, present_region: Option<&'a PresentRegion>, ) { debug_assert!(image_num < swapchain.num_images()); if swapchain .device() .enabled_extensions() .khr_incremental_present { let vk_present_region = match present_region { Some(present_region) => { assert!(present_region.is_compatible_with(swapchain)); for rectangle in &present_region.rectangles { self.rect_layers.push(rectangle.into()); } ash::vk::PresentRegionKHR { rectangle_count: present_region.rectangles.len() as u32, // Set this to null for now; in submit fill it with self.rect_layers p_rectangles: ptr::null(), } } None => ash::vk::PresentRegionKHR { rectangle_count: 0, p_rectangles: ptr::null(), }, }; self.present_regions.push(vk_present_region); } self.swapchains.push(swapchain.internal_object()); self.image_indices.push(image_num); } /// Submits the command. Calls `vkQueuePresentKHR`. /// /// # Panic /// /// Panics if no swapchain image has been added to the builder. /// pub fn submit(mut self, queue: &Queue) -> Result<(), SubmitPresentError> { unsafe { debug_assert_eq!(self.swapchains.len(), self.image_indices.len()); assert!( !self.swapchains.is_empty(), "Tried to submit a present command without any swapchain" ); let present_regions = { if !self.present_regions.is_empty() { debug_assert!(queue.device().enabled_extensions().khr_incremental_present); debug_assert_eq!(self.swapchains.len(), self.present_regions.len()); let mut current_index = 0; for present_region in &mut self.present_regions { present_region.p_rectangles = self.rect_layers[current_index..].as_ptr(); current_index += present_region.rectangle_count as usize; } Some(ash::vk::PresentRegionsKHR { swapchain_count: self.present_regions.len() as u32, p_regions: self.present_regions.as_ptr(), ..Default::default() }) } else { None } }; let mut results = vec![ash::vk::Result::SUCCESS; self.swapchains.len()]; let fns = queue.device().fns(); let queue = queue.internal_object_guard(); let infos = ash::vk::PresentInfoKHR { p_next: present_regions .as_ref() .map(|pr| pr as *const ash::vk::PresentRegionsKHR as *const _) .unwrap_or(ptr::null()), wait_semaphore_count: self.wait_semaphores.len() as u32, p_wait_semaphores: self.wait_semaphores.as_ptr(), swapchain_count: self.swapchains.len() as u32, p_swapchains: self.swapchains.as_ptr(), p_image_indices: self.image_indices.as_ptr(), p_results: results.as_mut_ptr(), ..Default::default() }; check_errors(fns.khr_swapchain.queue_present_khr(*queue, &infos))?; for result in results { check_errors(result)?; } Ok(()) } } } impl<'a> fmt::Debug for SubmitPresentBuilder<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_struct("SubmitPresentBuilder") .field("wait_semaphores", &self.wait_semaphores) .field("swapchains", &self.swapchains) .field("image_indices", &self.image_indices) .finish() } } /// Error that can happen when submitting the present prototype. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] pub enum SubmitPresentError { /// Not enough memory. OomError(OomError), /// The connection to the device has been lost. DeviceLost, /// The surface is no longer accessible and must be recreated. SurfaceLost, /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. FullscreenExclusiveLost, /// The surface has changed in a way that makes the swapchain unusable. You must query the /// surface's new properties and recreate a new swapchain if you want to continue drawing. OutOfDate, } impl error::Error for SubmitPresentError { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { SubmitPresentError::OomError(ref err) => Some(err), _ => None, } } } impl fmt::Display for SubmitPresentError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( fmt, "{}", match *self { SubmitPresentError::OomError(_) => "not enough memory", SubmitPresentError::DeviceLost => "the connection to the device has been lost", SubmitPresentError::SurfaceLost => "the surface of this swapchain is no longer valid", SubmitPresentError::OutOfDate => "the swapchain needs to be recreated", SubmitPresentError::FullscreenExclusiveLost => { "the swapchain no longer has fullscreen exclusivity" } } ) } } impl From for SubmitPresentError { #[inline] fn from(err: Error) -> SubmitPresentError { match err { err @ Error::OutOfHostMemory => SubmitPresentError::OomError(OomError::from(err)), err @ Error::OutOfDeviceMemory => SubmitPresentError::OomError(OomError::from(err)), Error::DeviceLost => SubmitPresentError::DeviceLost, Error::SurfaceLost => SubmitPresentError::SurfaceLost, Error::OutOfDate => SubmitPresentError::OutOfDate, Error::FullscreenExclusiveLost => SubmitPresentError::FullscreenExclusiveLost, _ => panic!("unexpected error: {:?}", err), } } } #[cfg(test)] mod tests { use super::*; #[test] fn no_swapchain_added() { let (_, queue) = gfx_dev_and_queue!(); assert_should_panic!("Tried to submit a present command without any swapchain", { let _ = SubmitPresentBuilder::new().submit(&queue); }); } }