use super::{PresentMode, Swapchain};
use crate::{
buffer::Buffer,
device::{Device, DeviceOwned, Queue},
image::{Image, ImageLayout},
sync::{
fence::Fence,
future::{AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder},
semaphore::Semaphore,
},
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError,
VulkanObject,
};
use smallvec::smallvec;
use std::{
fmt::Debug,
mem::MaybeUninit,
num::NonZeroU64,
ops::Range,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
time::Duration,
};
pub fn acquire_next_image(
swapchain: Arc<Swapchain>,
timeout: Option<Duration>,
) -> Result<(u32, bool, SwapchainAcquireFuture), Validated<VulkanError>> {
let semaphore = Arc::new(Semaphore::from_pool(swapchain.device.clone())?);
let fence = Fence::from_pool(swapchain.device.clone())?;
let AcquiredImage {
image_index,
suboptimal,
} = {
let retired = swapchain.is_retired.lock();
if *retired {
return Err(VulkanError::OutOfDate.into());
}
let acquire_result =
unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) };
if matches!(
acquire_result,
Err(Validated::Error(VulkanError::FullScreenExclusiveModeLost))
) {
swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
acquire_result?
};
Ok((
image_index,
suboptimal,
SwapchainAcquireFuture {
swapchain,
semaphore: Some(semaphore),
fence: Some(fence),
image_index,
finished: AtomicBool::new(false),
},
))
}
pub unsafe fn acquire_next_image_raw(
swapchain: &Swapchain,
timeout: Option<Duration>,
semaphore: Option<&Semaphore>,
fence: Option<&Fence>,
) -> Result<AcquiredImage, Validated<VulkanError>> {
let fns = swapchain.device.fns();
let timeout_ns = if let Some(timeout) = timeout {
timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
} else {
u64::MAX
};
let mut out = MaybeUninit::uninit();
let result = (fns.khr_swapchain.acquire_next_image_khr)(
swapchain.device.handle(),
swapchain.handle,
timeout_ns,
semaphore
.map(|s| s.handle())
.unwrap_or(ash::vk::Semaphore::null()),
fence.map(|f| f.handle()).unwrap_or(ash::vk::Fence::null()),
out.as_mut_ptr(),
);
let suboptimal = match result {
ash::vk::Result::SUCCESS => false,
ash::vk::Result::SUBOPTIMAL_KHR => true,
ash::vk::Result::NOT_READY => return Err(VulkanError::NotReady.into()),
ash::vk::Result::TIMEOUT => return Err(VulkanError::Timeout.into()),
err => return Err(VulkanError::from(err).into()),
};
if let Some(semaphore) = semaphore {
let mut state = semaphore.state();
state.swapchain_acquire();
}
if let Some(fence) = fence {
let mut state = fence.state();
state.import_swapchain_acquire();
}
Ok(AcquiredImage {
image_index: out.assume_init(),
suboptimal,
})
}
pub struct AcquiredImage {
pub image_index: u32,
pub suboptimal: bool,
}
#[must_use]
pub struct SwapchainAcquireFuture {
swapchain: Arc<Swapchain>,
image_index: u32,
semaphore: Option<Arc<Semaphore>>,
fence: Option<Fence>,
finished: AtomicBool,
}
impl SwapchainAcquireFuture {
pub fn image_index(&self) -> u32 {
self.image_index
}
pub fn swapchain(&self) -> &Arc<Swapchain> {
&self.swapchain
}
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), VulkanError> {
match &self.fence {
Some(fence) => fence.wait(timeout),
None => Ok(()),
}
}
}
unsafe impl GpuFuture for SwapchainAcquireFuture {
fn cleanup_finished(&mut self) {}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
if let Some(ref semaphore) = self.semaphore {
let sem = smallvec![semaphore.clone()];
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
} else {
Ok(SubmitAnyBuilder::Empty)
}
}
fn flush(&self) -> Result<(), Validated<VulkanError>> {
Ok(())
}
unsafe fn signal_finished(&self) {
self.finished.store(true, Ordering::SeqCst);
}
fn queue_change_allowed(&self) -> bool {
true
}
fn queue(&self) -> Option<Arc<Queue>> {
None
}
fn check_buffer_access(
&self,
_buffer: &Buffer,
_range: Range<DeviceSize>,
_exclusive: bool,
_queue: &Queue,
) -> Result<(), AccessCheckError> {
Err(AccessCheckError::Unknown)
}
fn check_image_access(
&self,
image: &Image,
_range: Range<DeviceSize>,
_exclusive: bool,
expected_layout: ImageLayout,
_queue: &Queue,
) -> Result<(), AccessCheckError> {
if self.swapchain.index_of_image(image) != Some(self.image_index) {
return Err(AccessCheckError::Unknown);
}
if !self.swapchain.images[self.image_index as usize]
.layout_initialized
.load(Ordering::Relaxed)
&& expected_layout != ImageLayout::Undefined
{
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
requested: expected_layout,
}));
}
if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc {
return Err(AccessCheckError::Denied(
AccessError::UnexpectedImageLayout {
allowed: ImageLayout::PresentSrc,
requested: expected_layout,
},
));
}
Ok(())
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
before: bool,
) -> Result<(), AccessCheckError> {
if before {
Ok(())
} else {
if swapchain == self.swapchain.as_ref() && image_index == self.image_index {
Ok(())
} else {
Err(AccessCheckError::Unknown)
}
}
}
}
impl Drop for SwapchainAcquireFuture {
fn drop(&mut self) {
if thread::panicking() {
return;
}
if let Some(fence) = &self.fence {
fence.wait(None).unwrap(); self.semaphore = None;
}
}
}
unsafe impl DeviceOwned for SwapchainAcquireFuture {
fn device(&self) -> &Arc<Device> {
&self.swapchain.device
}
}
pub fn present<F>(
before: F,
queue: Arc<Queue>,
swapchain_info: SwapchainPresentInfo,
) -> PresentFuture<F>
where
F: GpuFuture,
{
assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count());
PresentFuture {
previous: before,
queue,
swapchain_info,
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
#[derive(Clone, Debug)]
pub struct PresentInfo {
pub wait_semaphores: Vec<Arc<Semaphore>>,
pub swapchain_infos: Vec<SwapchainPresentInfo>,
pub _ne: crate::NonExhaustive,
}
impl Default for PresentInfo {
#[inline]
fn default() -> Self {
Self {
wait_semaphores: Vec::new(),
swapchain_infos: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct SwapchainPresentInfo {
pub swapchain: Arc<Swapchain>,
pub image_index: u32,
pub present_id: Option<NonZeroU64>,
pub present_mode: Option<PresentMode>,
pub present_regions: Vec<RectangleLayer>,
pub _ne: crate::NonExhaustive,
}
impl SwapchainPresentInfo {
#[inline]
pub fn swapchain_image_index(swapchain: Arc<Swapchain>, image_index: u32) -> Self {
Self {
swapchain,
image_index,
present_id: None,
present_mode: None,
present_regions: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RectangleLayer {
pub offset: [u32; 2],
pub extent: [u32; 2],
pub layer: u32,
}
impl RectangleLayer {
#[inline]
pub fn is_compatible_with(&self, swapchain: &Swapchain) -> bool {
self.offset[0] + self.extent[0] <= swapchain.image_extent()[0]
&& self.offset[1] + self.extent[1] <= swapchain.image_extent()[1]
&& self.layer < swapchain.image_array_layers()
}
}
impl From<&RectangleLayer> for ash::vk::RectLayerKHR {
#[inline]
fn from(val: &RectangleLayer) -> Self {
ash::vk::RectLayerKHR {
offset: ash::vk::Offset2D {
x: val.offset[0] as i32,
y: val.offset[1] as i32,
},
extent: ash::vk::Extent2D {
width: val.extent[0],
height: val.extent[1],
},
layer: val.layer,
}
}
}
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
pub struct PresentFuture<P>
where
P: GpuFuture,
{
previous: P,
queue: Arc<Queue>,
swapchain_info: SwapchainPresentInfo,
flushed: AtomicBool,
finished: AtomicBool,
}
impl<P> PresentFuture<P>
where
P: GpuFuture,
{
pub fn image_id(&self) -> u32 {
self.swapchain_info.image_index
}
pub fn swapchain(&self) -> &Arc<Swapchain> {
&self.swapchain_info.swapchain
}
}
unsafe impl<P> GpuFuture for PresentFuture<P>
where
P: GpuFuture,
{
fn cleanup_finished(&mut self) {
self.previous.cleanup_finished();
}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
if self.flushed.load(Ordering::SeqCst) {
return Ok(SubmitAnyBuilder::Empty);
}
let mut swapchain_info = self.swapchain_info.clone();
debug_assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count());
let device = swapchain_info.swapchain.device();
if !device.enabled_features().present_id {
swapchain_info.present_id = None;
}
if device.enabled_extensions().khr_incremental_present {
for rectangle in &swapchain_info.present_regions {
assert!(rectangle.is_compatible_with(swapchain_info.swapchain.as_ref()));
}
} else {
swapchain_info.present_regions = Default::default();
}
let _queue = self.previous.queue();
Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
}),
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
SubmitAnyBuilder::QueuePresent(PresentInfo {
wait_semaphores: semaphores.into_iter().collect(),
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::CommandBuffer(_, _) => {
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::BindSparse(_, _) => {
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::QueuePresent(mut present_info) => {
if present_info.swapchain_infos.first().map_or(false, |prev| {
prev.present_mode.is_some() != self.swapchain_info.present_mode.is_some()
}) {
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
} else {
present_info
.swapchain_infos
.push(self.swapchain_info.clone());
SubmitAnyBuilder::QueuePresent(present_info)
}
}
})
}
fn flush(&self) -> Result<(), Validated<VulkanError>> {
unsafe {
let build_submission_result = self.build_submission();
self.flushed.store(true, Ordering::SeqCst);
match build_submission_result? {
SubmitAnyBuilder::Empty => Ok(()),
SubmitAnyBuilder::QueuePresent(present_info) => {
let PresentInfo {
wait_semaphores: _,
swapchain_infos,
_ne: _,
} = &present_info;
let has_present_mode = swapchain_infos
.first()
.map_or(false, |first| first.present_mode.is_some());
for swapchain_info in swapchain_infos {
let &SwapchainPresentInfo {
ref swapchain,
image_index: _,
present_id,
present_regions: _,
present_mode,
_ne: _,
} = swapchain_info;
if present_id.map_or(false, |present_id| {
!swapchain.try_claim_present_id(present_id)
}) {
return Err(Box::new(ValidationError {
problem: "the provided `present_id` was not greater than any \
`present_id` passed previously for the same swapchain"
.into(),
vuids: &["VUID-VkPresentIdKHR-presentIds-04999"],
..Default::default()
})
.into());
}
if let Some(present_mode) = present_mode {
assert!(has_present_mode);
if !swapchain.present_modes().contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: "the requested present mode is not one of the modes \
in `swapchain.present_modes()`"
.into(),
vuids: &[
"VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761",
],
..Default::default()
})
.into());
}
} else {
assert!(!has_present_mode);
}
}
match self.previous.check_swapchain_image_acquired(
&self.swapchain_info.swapchain,
self.swapchain_info.image_index,
true,
) {
Ok(_) => (),
Err(AccessCheckError::Unknown) => {
return Err(Box::new(ValidationError::from_error(
AccessError::SwapchainImageNotAcquired,
))
.into());
}
Err(AccessCheckError::Denied(err)) => {
return Err(Box::new(ValidationError::from_error(err)).into());
}
}
Ok(self
.queue
.with(|mut q| q.present_unchecked(present_info))?
.map(|r| r.map(|_| ()))
.fold(Ok(()), Result::and)?)
}
_ => unreachable!(),
}
}
}
unsafe fn signal_finished(&self) {
self.flushed.store(true, Ordering::SeqCst);
self.finished.store(true, Ordering::SeqCst);
self.previous.signal_finished();
}
fn queue_change_allowed(&self) -> bool {
false
}
fn queue(&self) -> Option<Arc<Queue>> {
debug_assert!(match self.previous.queue() {
None => true,
Some(q) => q == self.queue,
});
Some(self.queue.clone())
}
fn check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<(), AccessCheckError> {
self.previous
.check_buffer_access(buffer, range, exclusive, queue)
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<(), AccessCheckError> {
if self.swapchain_info.swapchain.index_of_image(image)
== Some(self.swapchain_info.image_index)
{
Err(AccessCheckError::Unknown)
} else {
self.previous
.check_image_access(image, range, exclusive, expected_layout, queue)
}
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
before: bool,
) -> Result<(), AccessCheckError> {
if before {
self.previous
.check_swapchain_image_acquired(swapchain, image_index, false)
} else if swapchain == self.swapchain_info.swapchain.as_ref()
&& image_index == self.swapchain_info.image_index
{
Err(AccessError::SwapchainImageNotAcquired.into())
} else {
self.previous
.check_swapchain_image_acquired(swapchain, image_index, false)
}
}
}
unsafe impl<P> DeviceOwned for PresentFuture<P>
where
P: GpuFuture,
{
fn device(&self) -> &Arc<Device> {
self.queue.device()
}
}
impl<P> Drop for PresentFuture<P>
where
P: GpuFuture,
{
fn drop(&mut self) {
if thread::panicking() {
return;
}
unsafe {
if !*self.flushed.get_mut() {
self.flush().ok();
}
if !*self.finished.get_mut() {
self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap();
self.previous.signal_finished();
}
}
}
}
pub fn wait_for_present(
swapchain: Arc<Swapchain>,
present_id: u64,
timeout: Option<Duration>,
) -> Result<bool, Validated<VulkanError>> {
if !swapchain.device.enabled_features().present_wait {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("present_wait")])]),
vuids: &["VUID-vkWaitForPresentKHR-presentWait-06234"],
..Default::default()
})
.into());
}
if present_id == 0 {
return Err(Box::new(ValidationError {
context: "present_id".into(),
problem: "is 0".into(),
..Default::default()
})
.into());
}
let retired = swapchain.is_retired.lock();
if *retired {
return Err(VulkanError::OutOfDate.into());
}
let timeout_ns = timeout.map(|dur| dur.as_nanos() as u64).unwrap_or(0);
let result = unsafe {
(swapchain.device.fns().khr_present_wait.wait_for_present_khr)(
swapchain.device.handle(),
swapchain.handle,
present_id,
timeout_ns,
)
};
match result {
ash::vk::Result::SUCCESS => Ok(false),
ash::vk::Result::SUBOPTIMAL_KHR => Ok(true),
ash::vk::Result::TIMEOUT => Err(VulkanError::Timeout.into()),
err => {
let err = VulkanError::from(err);
if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
Err(err.into())
}
}
}