blob: 28f4229a01ad5f3fd0a3f2d7ff2b4ddb016a1739 [file] [log] [blame]
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// 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<W>(
&mut self,
swapchain: &'a Swapchain<W>,
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<Error> 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);
});
}
}