use smallvec::SmallVec;
use std::error;
use std::fmt;
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;
use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use check_errors;
use vk;
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 ()>,
}
impl<'a> SubmitPresentBuilder<'a> {
#[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,
}
}
#[inline]
pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
self.wait_semaphores.push(semaphore.internal_object());
}
#[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()
.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,
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);
}
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().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()];
let vk = queue.device().pointers();
let queue = queue.internal_object_guard();
let infos = vk::PresentInfoKHR {
sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
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,
pSwapchains: self.swapchains.as_ptr(),
pImageIndices: self.image_indices.as_ptr(),
pResults: results.as_mut_ptr(),
};
check_errors(vk.QueuePresentKHR(*queue, &infos))?;
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()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SubmitPresentError {
OomError(OomError),
DeviceLost,
SurfaceLost,
OutOfDate,
}
impl error::Error for SubmitPresentError {
#[inline]
fn description(&self) -> &str {
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",
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
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, "{}", error::Error::description(self))
}
}
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,
_ => 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);
});
}
}