Support for VK_KHR_incremental_present (#729)

* Implement support for VK_KHR_incremental_present

The public interface is mainly extended by a new function:
swapchain::present_incremental

* Fix nits from PR

* Remove PresentRegion::to_vk

* Fix more nitpicks
This commit is contained in:
Nicolas Koch 2017-08-17 11:27:21 +02:00 committed by tomaka
parent 5e110ae981
commit 3eb2d7a168
7 changed files with 228 additions and 10 deletions

View File

@ -181,6 +181,7 @@ pub const STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR: u32 = 1000059007;
pub const STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR: u32 = 1000059008;
pub const STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN: u32 = 1000062000;
pub const STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: u32 = 1000080000;
pub const STRUCTURE_TYPE_PRESENT_REGIONS_KHR: u32 = 1000084000;
pub const STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR: u32 = 1000085000;
pub const STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR: u32 = 1000127000;
pub const STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR: u32 = 1000127001;
@ -2621,6 +2622,27 @@ pub struct MemoryRequirements2KHR {
pub memoryRequirements: MemoryRequirements,
}
#[repr(C)]
pub struct RectLayerKHR {
pub offset: Offset2D,
pub extent: Extent2D,
pub layer: u32,
}
#[repr(C)]
pub struct PresentRegionKHR {
pub rectangleCount: u32,
pub pRectangles: *const RectLayerKHR,
}
#[repr(C)]
pub struct PresentRegionsKHR {
pub sType: StructureType,
pub pNext: *const c_void,
pub swapchainCount: u32,
pub pRegions: *const PresentRegionKHR,
}
macro_rules! ptrs {
($struct_name:ident, { $($name:ident => ($($param_n:ident: $param_ty:ty),*) -> $ret:ty,)+ }) => (
pub struct $struct_name {

View File

@ -14,7 +14,9 @@ use std::marker::PhantomData;
use std::mem;
use std::ptr;
use device::DeviceOwned;
use device::Queue;
use swapchain::PresentRegion;
use swapchain::Swapchain;
use sync::Semaphore;
@ -27,11 +29,12 @@ use vk;
/// Prototype for a submission that presents a swapchain on the screen.
// TODO: example here
#[derive(Debug)]
pub struct SubmitPresentBuilder<'a> {
wait_semaphores: SmallVec<[vk::Semaphore; 8]>,
swapchains: SmallVec<[vk::SwapchainKHR; 4]>,
image_indices: SmallVec<[u32; 4]>,
present_regions: SmallVec<[vk::PresentRegionKHR; 4]>,
rect_layers: SmallVec<[vk::RectLayerKHR; 4]>,
marker: PhantomData<&'a ()>,
}
@ -43,6 +46,8 @@ impl<'a> SubmitPresentBuilder<'a> {
wait_semaphores: SmallVec::new(),
swapchains: SmallVec::new(),
image_indices: SmallVec::new(),
present_regions: SmallVec::new(),
rect_layers: SmallVec::new(),
marker: PhantomData,
}
}
@ -71,6 +76,12 @@ impl<'a> SubmitPresentBuilder<'a> {
/// 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
@ -79,32 +90,85 @@ impl<'a> SubmitPresentBuilder<'a> {
/// - 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) {
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()
.loaded_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.to_vk());
}
vk::PresentRegionKHR {
rectangleCount: present_region.rectangles.len() as u32,
// Set this to null for now; in submit fill it with self.rect_layers
pRectangles: ptr::null(),
}
},
None => {
vk::PresentRegionKHR {
rectangleCount: 0,
pRectangles: 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(self, queue: &Queue) -> Result<(), SubmitPresentError> {
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 vk = queue.device().pointers();
let queue = queue.internal_object_guard();
let present_regions = {
if !self.present_regions.is_empty() {
debug_assert!(queue.device().loaded_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.pRectangles = self.rect_layers[current_index ..].as_ptr();
current_index += present_region.rectangleCount as usize;
}
Some(vk::PresentRegionsKHR {
sType: vk::STRUCTURE_TYPE_PRESENT_REGIONS_KHR,
pNext: ptr::null(),
swapchainCount: self.present_regions.len() as u32,
pRegions: self.present_regions.as_ptr(),
})
} else {
None
}
};
let mut results = vec![mem::uninitialized(); self.swapchains.len()]; // TODO: alloca
let vk = queue.device().pointers();
let queue = queue.internal_object_guard();
let infos = vk::PresentInfoKHR {
sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
pNext: ptr::null(),
pNext: present_regions
.as_ref()
.map(|pr| pr as *const vk::PresentRegionsKHR as *const _)
.unwrap_or(ptr::null()),
waitSemaphoreCount: self.wait_semaphores.len() as u32,
pWaitSemaphores: self.wait_semaphores.as_ptr(),
swapchainCount: self.swapchains.len() as u32,
@ -125,6 +189,16 @@ impl<'a> SubmitPresentBuilder<'a> {
}
}
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)]

View File

@ -398,6 +398,7 @@ device_extensions! {
khr_maintenance1 => b"VK_KHR_maintenance1",
khr_get_memory_requirements2 => b"VK_KHR_get_memory_requirements2",
khr_dedicated_allocation => b"VK_KHR_dedicated_allocation",
khr_incremental_present => b"VK_KHR_incremental_present",
}
/// Error that can happen when loading the list of layers.

View File

@ -204,6 +204,8 @@ pub use self::capabilities::SupportedPresentModesIter;
pub use self::capabilities::SupportedSurfaceTransforms;
pub use self::capabilities::SupportedSurfaceTransformsIter;
pub use self::capabilities::SurfaceTransform;
pub use self::present_region::PresentRegion;
pub use self::present_region::RectangleLayer;
pub use self::surface::CapabilitiesError;
pub use self::surface::Surface;
pub use self::surface::SurfaceCreationError;
@ -216,9 +218,11 @@ pub use self::swapchain::SwapchainCreationError;
pub use self::swapchain::acquire_next_image;
pub use self::swapchain::acquire_next_image_raw;
pub use self::swapchain::present;
pub use self::swapchain::present_incremental;
mod capabilities;
pub mod display;
mod present_region;
mod surface;
mod swapchain;

View File

@ -0,0 +1,64 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://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 swapchain::Swapchain;
use vk;
/// Represents a region on an image.
///
/// A region consists of an arbitrary amount of rectangles.
#[derive(Debug, Clone)]
pub struct PresentRegion {
pub rectangles: Vec<RectangleLayer>,
}
impl PresentRegion {
/// Returns true if this present region is compatible with swapchain.
pub fn is_compatible_with(&self, swapchain: &Swapchain) -> bool {
self.rectangles.iter().all(|rect| rect.is_compatible_with(swapchain))
}
}
/// Represents a rectangular region on an image layer.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RectangleLayer {
/// Coordinates in pixels of the top-left hand corner of the rectangle.
pub offset: [i32; 2],
/// Dimensions in pixels of the rectangle.
pub extent: [u32; 2],
/// The layer of the image. For images with only one layer, the value of layer must be 0.
pub layer: u32,
}
impl RectangleLayer {
/// Returns true if this rectangle layer is compatible with swapchain.
pub fn is_compatible_with(&self, swapchain: &Swapchain) -> bool {
// FIXME negative offset is not disallowed by spec, but semantically should not be possible
debug_assert!(self.offset[0] >= 0);
debug_assert!(self.offset[1] >= 0);
self.offset[0] as u32 + self.extent[0] <= swapchain.dimensions()[0] &&
self.offset[1] as u32 + self.extent[1] <= swapchain.dimensions()[1] &&
self.layer < swapchain.layers()
}
pub(crate) fn to_vk(&self) -> vk::RectLayerKHR {
vk::RectLayerKHR {
offset: vk::Offset2D {
x: self.offset[0],
y: self.offset[1],
},
extent: vk::Extent2D {
width: self.extent[0],
height: self.extent[1],
},
layer: self.layer,
}
}
}

View File

@ -37,6 +37,7 @@ use swapchain::CapabilitiesError;
use swapchain::ColorSpace;
use swapchain::CompositeAlpha;
use swapchain::PresentMode;
use swapchain::PresentRegion;
use swapchain::Surface;
use swapchain::SurfaceSwapchainLock;
use swapchain::SurfaceTransform;
@ -118,6 +119,38 @@ pub fn present<F>(swapchain: Arc<Swapchain>, before: F, queue: Arc<Queue>, index
queue: queue,
swapchain: swapchain,
image_id: index,
present_region: None,
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
/// Same as `swapchain::present`, except it allows specifying a present region.
/// Areas outside the present region may be ignored by vulkan in order to optimize presentation.
///
/// This is just an optimizaion hint, as the vulkan driver is free to ignore the given present region.
///
/// If `VK_KHR_incremental_present` is not enabled on the device, the parameter will be ignored.
pub fn present_incremental<F>(swapchain: Arc<Swapchain>, before: F, queue: Arc<Queue>, index: usize,
present_region: PresentRegion)
-> PresentFuture<F>
where F: GpuFuture
{
assert!(index < swapchain.images.len());
// TODO: restore this check with a dummy ImageAccess implementation
/*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead
// Normally if `check_image_access` returns false we're supposed to call the `gpu_access`
// function on the image instead. But since we know that this method on `SwapchainImage`
// always returns false anyway (by design), we don't need to do it.
assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/
PresentFuture {
previous: before,
queue: queue,
swapchain: swapchain,
image_id: index,
present_region: Some(present_region),
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
@ -520,6 +553,12 @@ unsafe impl VulkanObject for Swapchain {
}
}
unsafe impl DeviceOwned for Swapchain {
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl fmt::Debug for Swapchain {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
@ -896,6 +935,7 @@ pub struct PresentFuture<P>
queue: Arc<Queue>,
swapchain: Arc<Swapchain>,
image_id: usize,
present_region: Option<PresentRegion>,
// True if `flush()` has been called on the future, which means that the present command has
// been submitted.
flushed: AtomicBool,
@ -942,24 +982,24 @@ unsafe impl<P> GpuFuture for PresentFuture<P>
Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => {
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(&self.swapchain, self.image_id as u32);
builder.add_swapchain(&self.swapchain, self.image_id as u32, self.present_region.as_ref());
SubmitAnyBuilder::QueuePresent(builder)
},
SubmitAnyBuilder::SemaphoresWait(sem) => {
let mut builder: SubmitPresentBuilder = sem.into();
builder.add_swapchain(&self.swapchain, self.image_id as u32);
builder.add_swapchain(&self.swapchain, self.image_id as u32, self.present_region.as_ref());
SubmitAnyBuilder::QueuePresent(builder)
},
SubmitAnyBuilder::CommandBuffer(cb) => {
cb.submit(&queue.unwrap())?; // FIXME: wrong because build_submission can be called multiple times
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(&self.swapchain, self.image_id as u32);
builder.add_swapchain(&self.swapchain, self.image_id as u32, self.present_region.as_ref());
SubmitAnyBuilder::QueuePresent(builder)
},
SubmitAnyBuilder::BindSparse(cb) => {
cb.submit(&queue.unwrap())?; // FIXME: wrong because build_submission can be called multiple times
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(&self.swapchain, self.image_id as u32);
builder.add_swapchain(&self.swapchain, self.image_id as u32, self.present_region.as_ref());
SubmitAnyBuilder::QueuePresent(builder)
},
SubmitAnyBuilder::QueuePresent(present) => {

View File

@ -26,6 +26,7 @@ use image::ImageAccess;
use image::ImageLayout;
use swapchain;
use swapchain::PresentFuture;
use swapchain::PresentRegion;
use swapchain::Swapchain;
use sync::AccessFlagBits;
use sync::FenceWaitError;
@ -242,6 +243,18 @@ pub unsafe trait GpuFuture: DeviceOwned {
{
swapchain::present(swapchain, self, queue, image_index)
}
/// Same as `then_swapchain_present`, except it allows specifying a present region.
///
/// > **Note**: This is just a shortcut for the `Swapchain::present_incremental()` function.
#[inline]
fn then_swapchain_present_incremental(self, queue: Arc<Queue>, swapchain: Arc<Swapchain>,
image_index: usize, present_region: PresentRegion)
-> PresentFuture<Self>
where Self: Sized
{
swapchain::present_incremental(swapchain, self, queue, image_index, present_region)
}
}
unsafe impl<F: ?Sized> GpuFuture for Box<F>