// 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 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(first: F, second: S) -> JoinFuture 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 { first: A, second: B, } unsafe impl DeviceOwned for JoinFuture where A: DeviceOwned, B: DeviceOwned, { fn device(&self) -> &Arc { let device = self.first.device(); debug_assert_eq!(self.second.device().handle(), device.handle()); device } } unsafe impl GpuFuture for JoinFuture 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 { // 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> { 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, 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, 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? } } }