// Copyright (c) 2017 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use std::sync::Arc; use crate::buffer::BufferAccess; use crate::command_buffer::submit::SubmitAnyBuilder; use crate::device::Device; use crate::device::DeviceOwned; use crate::device::Queue; use crate::image::ImageAccess; use crate::image::ImageLayout; use crate::sync::AccessCheckError; use crate::sync::AccessFlags; use crate::sync::FlushError; use crate::sync::GpuFuture; use crate::sync::PipelineStages; use crate::VulkanObject; /// Joins two futures together. // TODO: handle errors #[inline] pub fn join(first: F, second: S) -> JoinFuture where F: GpuFuture, S: GpuFuture, { assert_eq!( first.device().internal_object(), second.device().internal_object() ); if !first.queue_change_allowed() && !second.queue_change_allowed() { assert!(first.queue().unwrap().is_same(&second.queue().unwrap())); } JoinFuture { first: first, second: second, } } /// Two futures joined into one. #[must_use] pub struct JoinFuture { first: A, second: B, } unsafe impl DeviceOwned for JoinFuture where A: DeviceOwned, B: DeviceOwned, { #[inline] fn device(&self) -> &Arc { let device = self.first.device(); debug_assert_eq!( self.second.device().internal_object(), device.internal_object() ); device } } unsafe impl GpuFuture for JoinFuture where A: GpuFuture, B: GpuFuture, { #[inline] fn cleanup_finished(&mut self) { self.first.cleanup_finished(); self.second.cleanup_finished(); } #[inline] 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(()) } #[inline] unsafe fn build_submission(&self) -> Result { // 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.merge(b); SubmitAnyBuilder::SemaphoresWait(a) } (SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::CommandBuffer(b)) => { self.second.flush()?; SubmitAnyBuilder::SemaphoresWait(a) } (SubmitAnyBuilder::CommandBuffer(a), SubmitAnyBuilder::SemaphoresWait(b)) => { self.first.flush()?; SubmitAnyBuilder::SemaphoresWait(b) } (SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::QueuePresent(b)) => { self.second.flush()?; SubmitAnyBuilder::SemaphoresWait(a) } (SubmitAnyBuilder::QueuePresent(a), SubmitAnyBuilder::SemaphoresWait(b)) => { self.first.flush()?; SubmitAnyBuilder::SemaphoresWait(b) } (SubmitAnyBuilder::SemaphoresWait(a), SubmitAnyBuilder::BindSparse(b)) => { self.second.flush()?; SubmitAnyBuilder::SemaphoresWait(a) } (SubmitAnyBuilder::BindSparse(a), SubmitAnyBuilder::SemaphoresWait(b)) => { self.first.flush()?; SubmitAnyBuilder::SemaphoresWait(b) } (SubmitAnyBuilder::CommandBuffer(a), SubmitAnyBuilder::CommandBuffer(b)) => { // TODO: we may want to add debug asserts here let new = a.merge(b); SubmitAnyBuilder::CommandBuffer(new) } (SubmitAnyBuilder::QueuePresent(a), SubmitAnyBuilder::QueuePresent(b)) => { self.first.flush()?; self.second.flush()?; SubmitAnyBuilder::Empty } (SubmitAnyBuilder::CommandBuffer(a), SubmitAnyBuilder::QueuePresent(b)) => { unimplemented!() } (SubmitAnyBuilder::QueuePresent(a), SubmitAnyBuilder::CommandBuffer(b)) => { unimplemented!() } (SubmitAnyBuilder::BindSparse(a), SubmitAnyBuilder::QueuePresent(b)) => { unimplemented!() } (SubmitAnyBuilder::QueuePresent(a), SubmitAnyBuilder::BindSparse(b)) => { unimplemented!() } (SubmitAnyBuilder::BindSparse(a), SubmitAnyBuilder::CommandBuffer(b)) => { unimplemented!() } (SubmitAnyBuilder::CommandBuffer(a), SubmitAnyBuilder::BindSparse(b)) => { unimplemented!() } (SubmitAnyBuilder::BindSparse(mut a), SubmitAnyBuilder::BindSparse(b)) => { match a.merge(b) { Ok(()) => SubmitAnyBuilder::BindSparse(a), Err(_) => { // TODO: this happens if both bind sparse have been given a fence already // annoying, but not impossible, to handle unimplemented!() } } } }) } #[inline] unsafe fn signal_finished(&self) { self.first.signal_finished(); self.second.signal_finished(); } #[inline] fn queue_change_allowed(&self) -> bool { self.first.queue_change_allowed() && self.second.queue_change_allowed() } #[inline] fn queue(&self) -> Option> { match (self.first.queue(), self.second.queue()) { (Some(q1), Some(q2)) => { if q1.is_same(&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, } } #[inline] fn check_buffer_access( &self, buffer: &dyn BufferAccess, exclusive: bool, queue: &Queue, ) -> Result, AccessCheckError> { let first = self.first.check_buffer_access(buffer, exclusive, queue); let second = self.second.check_buffer_access(buffer, 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(e2))) => { Err(AccessCheckError::Denied(e1)) } // TODO: which one? (Ok(_), Err(AccessCheckError::Denied(_))) | (Err(AccessCheckError::Denied(_)), Ok(_)) => panic!( "Contradictory information \ between two futures" ), (Ok(None), Ok(None)) => Ok(None), (Ok(Some(a)), Ok(None)) | (Ok(None), Ok(Some(a))) => Ok(Some(a)), (Ok(Some((a1, a2))), Ok(Some((b1, b2)))) => Ok(Some((a1 | b1, a2 | b2))), } } #[inline] fn check_image_access( &self, image: &dyn ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue, ) -> Result, AccessCheckError> { let first = self .first .check_image_access(image, layout, exclusive, queue); let second = self .second .check_image_access(image, layout, 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(e2))) => { Err(AccessCheckError::Denied(e1)) } // TODO: which one? (Ok(_), Err(AccessCheckError::Denied(_))) | (Err(AccessCheckError::Denied(_)), Ok(_)) => panic!( "Contradictory information \ between two futures" ), (Ok(None), Ok(None)) => Ok(None), (Ok(Some(a)), Ok(None)) | (Ok(None), Ok(Some(a))) => Ok(Some(a)), (Ok(Some((a1, a2))), Ok(Some((b1, b2)))) => Ok(Some((a1 | b1, a2 | b2))), } } }