blob: 6ead696a24ec4d5a9aeb505200cd7d8b5cf34adc [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 super::{AccessCheckError, FlushError, GpuFuture, SubmitAnyBuilder};
use crate::{
buffer::Buffer,
device::{Device, DeviceOwned, Queue},
image::{sys::Image, ImageLayout},
swapchain::Swapchain,
DeviceSize, VulkanObject,
};
use std::{ops::Range, sync::Arc};
/// Joins two futures together.
// TODO: handle errors
pub fn join<F, S>(first: F, second: S) -> JoinFuture<F, S>
where
F: GpuFuture,
S: GpuFuture,
{
assert_eq!(first.device().handle(), second.device().handle());
if !first.queue_change_allowed() && !second.queue_change_allowed() {
assert!(first.queue().unwrap() == second.queue().unwrap());
}
JoinFuture { first, second }
}
/// Two futures joined into one.
#[must_use]
pub struct JoinFuture<A, B> {
first: A,
second: B,
}
unsafe impl<A, B> DeviceOwned for JoinFuture<A, B>
where
A: DeviceOwned,
B: DeviceOwned,
{
fn device(&self) -> &Arc<Device> {
let device = self.first.device();
debug_assert_eq!(self.second.device().handle(), device.handle());
device
}
}
unsafe impl<A, B> GpuFuture for JoinFuture<A, B>
where
A: GpuFuture,
B: GpuFuture,
{
fn cleanup_finished(&mut self) {
self.first.cleanup_finished();
self.second.cleanup_finished();
}
fn flush(&self) -> Result<(), FlushError> {
// Since each future remembers whether it has been flushed, there's no safety issue here
// if we call this function multiple times.
self.first.flush()?;
self.second.flush()?;
Ok(())
}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
// TODO: review this function
let first = self.first.build_submission()?;
let second = self.second.build_submission()?;
// In some cases below we have to submit previous command buffers already, this s done by flushing previous.
// Since the implementation should remember being flushed it's safe to call build_submission multiple times
Ok(match (first, second) {
(SubmitAnyBuilder::Empty, b) => b,
(a, SubmitAnyBuilder::Empty) => a,
(SubmitAnyBuilder::SemaphoresWait(mut a), SubmitAnyBuilder::SemaphoresWait(b)) => {
a.extend(b);
SubmitAnyBuilder::SemaphoresWait(a)
}
(SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::CommandBuffer(_, _)) => {
self.second.flush()?;
SubmitAnyBuilder::SemaphoresWait(a)
}
(SubmitAnyBuilder::CommandBuffer(_, _), SubmitAnyBuilder::SemaphoresWait(b)) => {
self.first.flush()?;
SubmitAnyBuilder::SemaphoresWait(b)
}
(SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::QueuePresent(_)) => {
self.second.flush()?;
SubmitAnyBuilder::SemaphoresWait(a)
}
(SubmitAnyBuilder::QueuePresent(_), SubmitAnyBuilder::SemaphoresWait(b)) => {
self.first.flush()?;
SubmitAnyBuilder::SemaphoresWait(b)
}
(SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::BindSparse(_, _)) => {
self.second.flush()?;
SubmitAnyBuilder::SemaphoresWait(a)
}
(SubmitAnyBuilder::BindSparse(_, _), SubmitAnyBuilder::SemaphoresWait(b)) => {
self.first.flush()?;
SubmitAnyBuilder::SemaphoresWait(b)
}
(
SubmitAnyBuilder::CommandBuffer(mut submit_info_a, fence_a),
SubmitAnyBuilder::CommandBuffer(submit_info_b, fence_b),
) => {
assert!(
fence_a.is_none() || fence_b.is_none(),
"Can't merge two queue submits that both have a fence"
);
submit_info_a
.wait_semaphores
.extend(submit_info_b.wait_semaphores);
submit_info_a
.command_buffers
.extend(submit_info_b.command_buffers);
submit_info_a
.signal_semaphores
.extend(submit_info_b.signal_semaphores);
SubmitAnyBuilder::CommandBuffer(submit_info_a, fence_a.or(fence_b))
}
(SubmitAnyBuilder::QueuePresent(_), SubmitAnyBuilder::QueuePresent(_)) => {
self.first.flush()?;
self.second.flush()?;
SubmitAnyBuilder::Empty
}
(SubmitAnyBuilder::CommandBuffer(_, _), SubmitAnyBuilder::QueuePresent(_)) => {
unimplemented!()
}
(SubmitAnyBuilder::QueuePresent(_), SubmitAnyBuilder::CommandBuffer(_, _)) => {
unimplemented!()
}
(SubmitAnyBuilder::BindSparse(_, _), SubmitAnyBuilder::QueuePresent(_)) => {
unimplemented!()
}
(SubmitAnyBuilder::QueuePresent(_), SubmitAnyBuilder::BindSparse(_, _)) => {
unimplemented!()
}
(SubmitAnyBuilder::BindSparse(_, _), SubmitAnyBuilder::CommandBuffer(_, _)) => {
unimplemented!()
}
(SubmitAnyBuilder::CommandBuffer(_, _), SubmitAnyBuilder::BindSparse(_, _)) => {
unimplemented!()
}
(
SubmitAnyBuilder::BindSparse(mut bind_infos_a, fence_a),
SubmitAnyBuilder::BindSparse(bind_infos_b, fence_b),
) => {
if fence_a.is_some() && fence_b.is_some() {
// TODO: this happens if both bind sparse have been given a fence already
// annoying, but not impossible, to handle
unimplemented!()
}
bind_infos_a.extend(bind_infos_b);
SubmitAnyBuilder::BindSparse(bind_infos_a, fence_a)
}
})
}
unsafe fn signal_finished(&self) {
self.first.signal_finished();
self.second.signal_finished();
}
fn queue_change_allowed(&self) -> bool {
self.first.queue_change_allowed() && self.second.queue_change_allowed()
}
fn queue(&self) -> Option<Arc<Queue>> {
match (self.first.queue(), self.second.queue()) {
(Some(q1), Some(q2)) => {
if q1 == q2 {
Some(q1)
} else if self.first.queue_change_allowed() {
Some(q2)
} else if self.second.queue_change_allowed() {
Some(q1)
} else {
None
}
}
(Some(q), None) => Some(q),
(None, Some(q)) => Some(q),
(None, None) => None,
}
}
fn check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<(), AccessCheckError> {
let first = self
.first
.check_buffer_access(buffer, range.clone(), exclusive, queue);
let second = self
.second
.check_buffer_access(buffer, range, exclusive, queue);
debug_assert!(
!(exclusive && first.is_ok() && second.is_ok()),
"Two futures gave exclusive access to the same resource"
);
match (first, second) {
(v, Err(AccessCheckError::Unknown)) => v,
(Err(AccessCheckError::Unknown), v) => v,
(Err(AccessCheckError::Denied(e1)), Err(AccessCheckError::Denied(_))) => {
Err(AccessCheckError::Denied(e1))
} // TODO: which one?
(Ok(()), Err(AccessCheckError::Denied(_)))
| (Err(AccessCheckError::Denied(_)), Ok(())) => panic!(
"Contradictory information \
between two futures"
),
(Ok(()), Ok(())) => Ok(()),
}
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<(), AccessCheckError> {
let first =
self.first
.check_image_access(image, range.clone(), exclusive, expected_layout, queue);
let second =
self.second
.check_image_access(image, range, exclusive, expected_layout, queue);
debug_assert!(
!(exclusive && first.is_ok() && second.is_ok()),
"Two futures gave exclusive access to the same resource"
);
match (first, second) {
(v, Err(AccessCheckError::Unknown)) => v,
(Err(AccessCheckError::Unknown), v) => v,
(Err(AccessCheckError::Denied(e1)), Err(AccessCheckError::Denied(_))) => {
Err(AccessCheckError::Denied(e1))
} // TODO: which one?
(Ok(()), Err(AccessCheckError::Denied(_)))
| (Err(AccessCheckError::Denied(_)), Ok(())) => {
panic!("Contradictory information between two futures")
}
(Ok(()), Ok(())) => Ok(()),
}
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
_before: bool,
) -> Result<(), AccessCheckError> {
let first = self
.first
.check_swapchain_image_acquired(swapchain, image_index, false);
let second = self
.second
.check_swapchain_image_acquired(swapchain, image_index, false);
match (first, second) {
(v, Err(AccessCheckError::Unknown)) => v,
(Err(AccessCheckError::Unknown), v) => v,
(Err(AccessCheckError::Denied(e1)), Err(AccessCheckError::Denied(_))) => {
Err(AccessCheckError::Denied(e1))
} // TODO: which one?
(Ok(()), Err(AccessCheckError::Denied(_)))
| (Err(AccessCheckError::Denied(_)), Ok(())) => Ok(()),
(Ok(()), Ok(())) => Ok(()), // TODO: Double Acquired?
}
}
}