mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 16:25:31 +00:00
Implement #385
This commit is contained in:
parent
3125a73628
commit
dd13c015cb
@ -37,8 +37,6 @@ use vulkano::command_buffer;
|
||||
use vulkano::command_buffer::AutoCommandBufferBuilder;
|
||||
use vulkano::command_buffer::CommandBufferBuilder;
|
||||
use vulkano::command_buffer::DynamicState;
|
||||
use vulkano::command_buffer::Submit;
|
||||
use vulkano::command_buffer::Submission;
|
||||
use vulkano::descriptor::pipeline_layout::PipelineLayout;
|
||||
use vulkano::descriptor::pipeline_layout::EmptyPipelineDesc;
|
||||
use vulkano::device::Device;
|
||||
@ -57,6 +55,7 @@ use vulkano::pipeline::viewport::Viewport;
|
||||
use vulkano::pipeline::viewport::Scissor;
|
||||
use vulkano::swapchain::SurfaceTransform;
|
||||
use vulkano::swapchain::Swapchain;
|
||||
use vulkano::sync::GpuFuture;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -343,15 +342,16 @@ fn main() {
|
||||
// Initialization is finally finished!
|
||||
|
||||
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
|
||||
// a `Submission` object which holds the resources for as long as they are in use by the GPU.
|
||||
// an object that implements the `GpuFuture` trait, which holds the resources for as long as
|
||||
// they are in use by the GPU.
|
||||
//
|
||||
// Destroying a `Submission` blocks until the GPU is finished executing it. In order to avoid
|
||||
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
|
||||
// that, we store them in a `Vec` and clean them from time to time.
|
||||
let mut submissions: Vec<Submission> = Vec::new();
|
||||
let mut submissions: Vec<Box<GpuFuture>> = Vec::new();
|
||||
|
||||
loop {
|
||||
// Clearing the old submissions by keeping alive only the ones whose destructor would block.
|
||||
submissions.retain(|s| s.destroying_would_block());
|
||||
// Clearing the old submissions by keeping alive only the ones which aren't finished.
|
||||
submissions.retain(|s| !s.is_finished());
|
||||
|
||||
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
||||
// no image is available (which happens if you submit draw commands too quickly), then the
|
||||
@ -360,7 +360,7 @@ fn main() {
|
||||
//
|
||||
// This function can block if no image is available. The parameter is a timeout after
|
||||
// which the function call will return an error.
|
||||
let image_num = swapchain.acquire_next_image(Duration::new(1, 0)).unwrap();
|
||||
let (image_num, future) = swapchain.acquire_next_image(Duration::new(1, 0)).unwrap();
|
||||
|
||||
// In order to draw, we have to build a *command buffer*. The command buffer object holds
|
||||
// the list of commands that are going to be executed.
|
||||
@ -396,16 +396,19 @@ fn main() {
|
||||
// Finish building the command buffer by calling `build`.
|
||||
.build();
|
||||
|
||||
// Now all we need to do is submit the command buffer to the queue.
|
||||
submissions.push(command_buffer.submit(&queue).unwrap());
|
||||
let future = future
|
||||
.then_execute(queue.clone(), command_buffer)
|
||||
|
||||
// The color output is now expected to contain our triangle. But in order to show it on
|
||||
// the screen, we have to *present* the image by calling `present`.
|
||||
//
|
||||
// This function does not actually present the image immediately. Instead it submits a
|
||||
// present command at the end of the queue. This means that it will only be presented once
|
||||
// the GPU has finished executing the command buffer that draws the triangle.
|
||||
swapchain.present(&queue, image_num).unwrap();
|
||||
// The color output is now expected to contain our triangle. But in order to show it on
|
||||
// the screen, we have to *present* the image by calling `present`.
|
||||
//
|
||||
// This function does not actually present the image immediately. Instead it submits a
|
||||
// present command at the end of the queue. This means that it will only be presented once
|
||||
// the GPU has finished executing the command buffer that draws the triangle.
|
||||
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
|
||||
.then_signal_fence();
|
||||
future.flush().unwrap();
|
||||
submissions.push(Box::new(future) as Box<_>);
|
||||
|
||||
// Note that in more complex programs it is likely that one of `acquire_next_image`,
|
||||
// `command_buffer::submit`, or `present` will block for some time. This happens when the
|
||||
|
@ -26,6 +26,7 @@ use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::RwLockReadGuard;
|
||||
use std::sync::RwLockWriteGuard;
|
||||
use std::sync::TryLockError;
|
||||
use std::sync::Weak;
|
||||
use std::time::Duration;
|
||||
use smallvec::SmallVec;
|
||||
@ -37,8 +38,8 @@ use buffer::sys::Usage;
|
||||
use buffer::traits::Buffer;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::TypedBuffer;
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use instance::QueueFamily;
|
||||
use memory::Content;
|
||||
use memory::CpuAccess as MemCpuAccess;
|
||||
@ -62,23 +63,17 @@ pub struct CpuAccessibleBuffer<T: ?Sized, A = Arc<StdMemoryPool>> where A: Memor
|
||||
// The memory held by the buffer.
|
||||
memory: A::Alloc,
|
||||
|
||||
// Access pattern of the buffer. Can be read-locked for a shared CPU access, or write-locked
|
||||
// for either a write CPU access or a GPU access.
|
||||
access: RwLock<()>,
|
||||
|
||||
// Queue families allowed to access this buffer.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
|
||||
// Latest submission that uses this buffer.
|
||||
// Also used to block any attempt to submit this buffer while it is accessed by the CPU.
|
||||
latest_submission: RwLock<LatestSubmission>,
|
||||
|
||||
// Necessary to make it compile.
|
||||
marker: PhantomData<Box<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LatestSubmission {
|
||||
read_submissions: Mutex<Vec<Weak<Submission>>>,
|
||||
write_submission: Option<Weak<Submission>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
}
|
||||
|
||||
impl<T> CpuAccessibleBuffer<T> {
|
||||
/// Deprecated. Use `from_data` instead.
|
||||
#[deprecated]
|
||||
@ -108,7 +103,7 @@ impl<T> CpuAccessibleBuffer<T> {
|
||||
// TODO: check whether that's true ^
|
||||
|
||||
{
|
||||
let mut mapping = uninitialized.write(Duration::new(0, 0)).unwrap();
|
||||
let mut mapping = uninitialized.write().unwrap();
|
||||
ptr::write::<T>(&mut *mapping, data)
|
||||
}
|
||||
|
||||
@ -145,7 +140,7 @@ impl<T> CpuAccessibleBuffer<[T]> {
|
||||
// TODO: check whether that's true ^
|
||||
|
||||
{
|
||||
let mut mapping = uninitialized.write(Duration::new(0, 0)).unwrap();
|
||||
let mut mapping = uninitialized.write().unwrap();
|
||||
|
||||
for (i, o) in data.zip(mapping.iter_mut()) {
|
||||
ptr::write(o, i);
|
||||
@ -223,11 +218,8 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
|
||||
Ok(Arc::new(CpuAccessibleBuffer {
|
||||
inner: buffer,
|
||||
memory: mem,
|
||||
access: RwLock::new(()),
|
||||
queue_families: queue_families,
|
||||
latest_submission: RwLock::new(LatestSubmission {
|
||||
read_submissions: Mutex::new(vec![]),
|
||||
write_submission: None,
|
||||
}),
|
||||
marker: PhantomData,
|
||||
}))
|
||||
}
|
||||
@ -259,22 +251,16 @@ impl<T: ?Sized, A> CpuAccessibleBuffer<T, A> where T: Content + 'static, A: Memo
|
||||
///
|
||||
/// After this function successfully locks the buffer, any attempt to submit a command buffer
|
||||
/// that uses it will block until you unlock it.
|
||||
// TODO: remove timeout parameter since CPU-side locking can't use it
|
||||
#[inline]
|
||||
pub fn read(&self, timeout: Duration) -> Result<ReadLock<T>, FenceWaitError> {
|
||||
let submission = self.latest_submission.read().unwrap();
|
||||
|
||||
// TODO: should that set the write_submission to None?
|
||||
if let Some(submission) = submission.write_submission.clone().and_then(|s| s.upgrade()) {
|
||||
try!(submission.wait(timeout));
|
||||
}
|
||||
pub fn read(&self) -> Result<ReadLock<T>, TryLockError<RwLockReadGuard<()>>> {
|
||||
let lock = try!(self.access.try_read());
|
||||
|
||||
let offset = self.memory.offset();
|
||||
let range = offset .. offset + self.inner.size();
|
||||
|
||||
Ok(ReadLock {
|
||||
inner: unsafe { self.memory.mapped_memory().unwrap().read_write(range) },
|
||||
lock: submission,
|
||||
lock: lock,
|
||||
})
|
||||
}
|
||||
|
||||
@ -286,30 +272,16 @@ impl<T: ?Sized, A> CpuAccessibleBuffer<T, A> where T: Content + 'static, A: Memo
|
||||
///
|
||||
/// After this function successfully locks the buffer, any attempt to submit a command buffer
|
||||
/// that uses it will block until you unlock it.
|
||||
// TODO: remove timeout parameter since CPU-side locking can't use it
|
||||
#[inline]
|
||||
pub fn write(&self, timeout: Duration) -> Result<WriteLock<T>, FenceWaitError> {
|
||||
let mut submission = self.latest_submission.write().unwrap();
|
||||
|
||||
{
|
||||
let mut read_submissions = submission.read_submissions.get_mut().unwrap();
|
||||
for submission in read_submissions.drain(..) {
|
||||
if let Some(submission) = submission.upgrade() {
|
||||
try!(submission.wait(timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(submission) = submission.write_submission.take().and_then(|s| s.upgrade()) {
|
||||
try!(submission.wait(timeout));
|
||||
}
|
||||
pub fn write(&self) -> Result<WriteLock<T>, TryLockError<RwLockWriteGuard<()>>> {
|
||||
let lock = try!(self.access.try_write());
|
||||
|
||||
let offset = self.memory.offset();
|
||||
let range = offset .. offset + self.inner.size();
|
||||
|
||||
Ok(WriteLock {
|
||||
inner: unsafe { self.memory.mapped_memory().unwrap().read_write(range) },
|
||||
lock: submission,
|
||||
lock: lock,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -330,134 +302,10 @@ unsafe impl<T: ?Sized, A> Buffer for CpuAccessibleBuffer<T, A>
|
||||
self.inner.key()
|
||||
}
|
||||
|
||||
/*fn transition(&self, states: &mut StatesManager, num_command: usize, _: usize, _: usize,
|
||||
write: bool, stage: PipelineStages, access: AccessFlagBits)
|
||||
-> Option<TrackedBufferPipelineBarrierRequest>
|
||||
{
|
||||
debug_assert!(!stage.host);
|
||||
debug_assert!(!access.host_read);
|
||||
debug_assert!(!access.host_write);
|
||||
|
||||
// We don't know when the user is going to write to the buffer from the CPU, so we just
|
||||
// assume that it's happened all the time.
|
||||
let mut state = states.buffer_or(self.inner().buffer, 0, || CpuAccessibleBufferClState {
|
||||
size: self.size(),
|
||||
stages: PipelineStages { host: true, .. PipelineStages::none() },
|
||||
access: AccessFlagBits { host_write: true, .. AccessFlagBits::none() },
|
||||
first_stages: None,
|
||||
write: true,
|
||||
earliest_previous_transition: 0,
|
||||
needs_flush_at_the_end: false,
|
||||
});
|
||||
|
||||
if write {
|
||||
// Write after read or write after write.
|
||||
let new_state = CpuAccessibleBufferClState {
|
||||
size: state.size,
|
||||
stages: stage,
|
||||
access: access,
|
||||
first_stages: Some(state.first_stages.clone().unwrap_or(stage)),
|
||||
write: true,
|
||||
earliest_previous_transition: num_command,
|
||||
needs_flush_at_the_end: true,
|
||||
};
|
||||
|
||||
let barrier = TrackedBufferPipelineBarrierRequest {
|
||||
after_command_num: state.earliest_previous_transition,
|
||||
source_stage: state.stages,
|
||||
destination_stages: stage,
|
||||
by_region: true,
|
||||
memory_barrier: if state.write {
|
||||
Some(TrackedBufferPipelineMemoryBarrierRequest {
|
||||
offset: 0,
|
||||
size: state.size,
|
||||
source_access: state.access,
|
||||
destination_access: access,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
};
|
||||
|
||||
*state = new_state;
|
||||
Some(barrier)
|
||||
|
||||
} else if state.write {
|
||||
// Read after write.
|
||||
let new_state = CpuAccessibleBufferClState {
|
||||
size: state.size,
|
||||
stages: stage,
|
||||
access: access,
|
||||
first_stages: Some(state.first_stages.clone().unwrap_or(stage)),
|
||||
write: false,
|
||||
earliest_previous_transition: num_command,
|
||||
needs_flush_at_the_end: state.needs_flush_at_the_end,
|
||||
};
|
||||
|
||||
let barrier = TrackedBufferPipelineBarrierRequest {
|
||||
after_command_num: state.earliest_previous_transition,
|
||||
source_stage: state.stages,
|
||||
destination_stages: stage,
|
||||
by_region: true,
|
||||
memory_barrier: Some(TrackedBufferPipelineMemoryBarrierRequest {
|
||||
offset: 0,
|
||||
size: state.size,
|
||||
source_access: state.access,
|
||||
destination_access: access,
|
||||
}),
|
||||
};
|
||||
|
||||
*state = new_state;
|
||||
Some(barrier)
|
||||
|
||||
} else {
|
||||
// Read after read.
|
||||
*state = CpuAccessibleBufferClState {
|
||||
size: state.size,
|
||||
stages: state.stages | stage,
|
||||
access: state.access | access,
|
||||
first_stages: Some(state.first_stages.clone().unwrap_or(stage)),
|
||||
write: false,
|
||||
earliest_previous_transition: state.earliest_previous_transition,
|
||||
needs_flush_at_the_end: state.needs_flush_at_the_end,
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
true // FIXME:
|
||||
}
|
||||
|
||||
fn finish(&self, in_s: &mut StatesManager, out: &mut StatesManager)
|
||||
-> Option<TrackedBufferPipelineBarrierRequest>
|
||||
{
|
||||
let state: CpuAccessibleBufferClState = in_s.remove_buffer(self.inner().buffer, 0).unwrap();
|
||||
|
||||
let barrier = if state.needs_flush_at_the_end {
|
||||
let barrier = TrackedBufferPipelineBarrierRequest {
|
||||
after_command_num: state.earliest_previous_transition,
|
||||
source_stage: state.stages,
|
||||
destination_stages: PipelineStages { host: true, .. PipelineStages::none() },
|
||||
by_region: true,
|
||||
memory_barrier: Some(TrackedBufferPipelineMemoryBarrierRequest {
|
||||
offset: 0,
|
||||
size: state.size,
|
||||
source_access: state.access,
|
||||
destination_access: AccessFlagBits { host_read: true,
|
||||
.. AccessFlagBits::none() },
|
||||
}),
|
||||
};
|
||||
|
||||
Some(barrier)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
out.buffer_or(self.inner().buffer, 0, || CpuAccessibleBufferFinished {
|
||||
first_stages: state.first_stages.unwrap_or(PipelineStages::none()),
|
||||
write: state.needs_flush_at_the_end,
|
||||
});
|
||||
|
||||
barrier
|
||||
}*/
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized, A> TypedBuffer for CpuAccessibleBuffer<T, A>
|
||||
@ -487,7 +335,7 @@ pub struct CpuAccessibleBufferFinished {
|
||||
/// this buffer's content or tries to submit a GPU command that uses this buffer, it will block.
|
||||
pub struct ReadLock<'a, T: ?Sized + 'a> {
|
||||
inner: MemCpuAccess<'a, T>,
|
||||
lock: RwLockReadGuard<'a, LatestSubmission>,
|
||||
lock: RwLockReadGuard<'a, ()>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> ReadLock<'a, T> {
|
||||
@ -518,7 +366,7 @@ impl<'a, T: ?Sized + 'a> Deref for ReadLock<'a, T> {
|
||||
/// this buffer's content or tries to submit a GPU command that uses this buffer, it will block.
|
||||
pub struct WriteLock<'a, T: ?Sized + 'a> {
|
||||
inner: MemCpuAccess<'a, T>,
|
||||
lock: RwLockWriteGuard<'a, LatestSubmission>,
|
||||
lock: RwLockWriteGuard<'a, ()>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + 'a> WriteLock<'a, T> {
|
||||
|
@ -27,8 +27,8 @@ use buffer::sys::Usage;
|
||||
use buffer::traits::Buffer;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::TypedBuffer;
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use instance::QueueFamily;
|
||||
use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
@ -50,20 +50,10 @@ pub struct DeviceLocalBuffer<T: ?Sized, A = Arc<StdMemoryPool>> where A: MemoryP
|
||||
// Queue families allowed to access this buffer.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
|
||||
// Latest submission that uses this buffer.
|
||||
// Also used to block any attempt to submit this buffer while it is accessed by the CPU.
|
||||
latest_submission: Mutex<LatestSubmission>,
|
||||
|
||||
// Necessary to make it compile.
|
||||
marker: PhantomData<Box<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LatestSubmission {
|
||||
read_submissions: SmallVec<[Weak<Submission>; 4]>,
|
||||
write_submission: Option<Weak<Submission>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
}
|
||||
|
||||
impl<T> DeviceLocalBuffer<T> {
|
||||
/// Builds a new buffer. Only allowed for sized data.
|
||||
#[inline]
|
||||
@ -137,10 +127,6 @@ impl<T: ?Sized> DeviceLocalBuffer<T> {
|
||||
inner: buffer,
|
||||
memory: mem,
|
||||
queue_families: queue_families,
|
||||
latest_submission: Mutex::new(LatestSubmission {
|
||||
read_submissions: SmallVec::new(),
|
||||
write_submission: None,
|
||||
}),
|
||||
marker: PhantomData,
|
||||
}))
|
||||
}
|
||||
@ -173,6 +159,11 @@ unsafe impl<T: ?Sized, A> Buffer for DeviceLocalBuffer<T, A>
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
false // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized, A> TypedBuffer for DeviceLocalBuffer<T, A>
|
||||
|
@ -33,8 +33,8 @@ use buffer::sys::Usage;
|
||||
use buffer::traits::Buffer;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::TypedBuffer;
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use instance::QueueFamily;
|
||||
use memory::pool::AllocLayout;
|
||||
use memory::pool::MemoryPool;
|
||||
@ -54,8 +54,6 @@ pub struct ImmutableBuffer<T: ?Sized, A = Arc<StdMemoryPool>> where A: MemoryPoo
|
||||
// Queue families allowed to access this buffer.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
|
||||
latest_write_submission: Mutex<Option<Weak<Submission>>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
|
||||
started_reading: AtomicBool,
|
||||
|
||||
marker: PhantomData<Box<T>>,
|
||||
@ -134,7 +132,6 @@ impl<T: ?Sized> ImmutableBuffer<T> {
|
||||
inner: buffer,
|
||||
memory: mem,
|
||||
queue_families: queue_families,
|
||||
latest_write_submission: Mutex::new(None),
|
||||
started_reading: AtomicBool::new(false),
|
||||
marker: PhantomData,
|
||||
}))
|
||||
@ -168,6 +165,11 @@ unsafe impl<T: ?Sized, A> Buffer for ImmutableBuffer<T, A>
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
false // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized, A> TypedBuffer for ImmutableBuffer<T, A>
|
||||
|
@ -14,6 +14,7 @@ use std::ops::Range;
|
||||
use buffer::traits::Buffer;
|
||||
use buffer::traits::BufferInner;
|
||||
use buffer::traits::TypedBuffer;
|
||||
use device::Queue;
|
||||
|
||||
/// A subpart of a buffer.
|
||||
///
|
||||
@ -172,6 +173,11 @@ unsafe impl<T: ?Sized, B> Buffer for BufferSlice<T, B> where B: Buffer {
|
||||
debug_assert!(self_size + self_offset <= self.size);
|
||||
self.resource.conflict_key(self_offset, self_size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
self.resource.gpu_access(exclusive_access, queue)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized, B> TypedBuffer for BufferSlice<T, B> where B: Buffer, T: 'static {
|
||||
|
@ -11,6 +11,7 @@ use std::ops::Range;
|
||||
|
||||
use buffer::BufferSlice;
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use device::Queue;
|
||||
use image::Image;
|
||||
use memory::Content;
|
||||
|
||||
@ -130,6 +131,16 @@ pub unsafe trait Buffer {
|
||||
// FIXME: remove implementation
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Returns true if the buffer can be given access on the given queue.
|
||||
///
|
||||
/// This function implementation should remember that it has been called and return `false` if
|
||||
/// it gets called a second time.
|
||||
///
|
||||
/// The only way to know that the GPU has stopped accessing a queue is when the buffer object
|
||||
/// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar
|
||||
/// to a lock) in order to represent a GPU access.
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool;
|
||||
}
|
||||
|
||||
/// Inner information about a buffer.
|
||||
@ -164,6 +175,11 @@ unsafe impl<T> Buffer for T where T: SafeDeref, T::Target: Buffer {
|
||||
fn conflict_key(&self, self_offset: usize, self_size: usize) -> u64 {
|
||||
(**self).conflict_key(self_offset, self_size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
(**self).gpu_access(exclusive_access, queue)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait TypedBuffer: Buffer {
|
||||
|
@ -7,21 +7,19 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
use command_buffer::cb;
|
||||
use command_buffer::cmd;
|
||||
use command_buffer::cb::AddCommand;
|
||||
use command_buffer::cb::CommandBufferBuild;
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferBuilder;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use command_buffer::pool::StandardCommandPool;
|
||||
use command_buffer::Submit;
|
||||
use command_buffer::SubmitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use instance::QueueFamily;
|
||||
use OomError;
|
||||
|
||||
@ -68,15 +66,15 @@ unsafe impl<L, P, O> CommandBufferBuild for AutoCommandBufferBuilder<L, P>
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<L, P> Submit for AutoCommandBufferBuilder<L, P>
|
||||
where Cb<L, P>: Submit,
|
||||
unsafe impl<L, P> CommandBuffer for AutoCommandBufferBuilder<L, P>
|
||||
where Cb<L, P>: CommandBuffer,
|
||||
P: CommandPool
|
||||
{
|
||||
type Pool = <Cb<L, P> as CommandBuffer>::Pool;
|
||||
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
self.inner.append_submission(base, queue)
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> {
|
||||
self.inner.inner()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,11 @@ use std::sync::Arc;
|
||||
use command_buffer::cb::AddCommand;
|
||||
use command_buffer::cb::CommandBufferBuild;
|
||||
use command_buffer::cb::CommandsList;
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferBuilder;
|
||||
use command_buffer::CommandBufferBuilderBuffered;
|
||||
use command_buffer::cmd;
|
||||
use command_buffer::Submit;
|
||||
use command_buffer::SubmitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
@ -141,12 +141,12 @@ unsafe impl<I, L, O> CommandBufferBuild for BufferedCommandsListLayer<I, L>
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<I, L> Submit for BufferedCommandsListLayer<I, L> where I: Submit {
|
||||
unsafe impl<I, L> CommandBuffer for BufferedCommandsListLayer<I, L> where I: CommandBuffer {
|
||||
type Pool = I::Pool;
|
||||
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
self.inner.as_ref().unwrap().append_submission(base, queue)
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<I::Pool> {
|
||||
self.inner.as_ref().unwrap().inner()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,10 @@ use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
use command_buffer::cb::AddCommand;
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::cmd;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferBuilder;
|
||||
use command_buffer::Submit;
|
||||
use command_buffer::SubmitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
@ -59,12 +59,12 @@ impl<I> CommandsListLayer<I, ()> {
|
||||
|
||||
// TODO: implement CommandBufferBuild
|
||||
|
||||
unsafe impl<I, L> Submit for CommandsListLayer<I, L> where I: Submit {
|
||||
unsafe impl<I, L> CommandBuffer for CommandsListLayer<I, L> where I: CommandBuffer {
|
||||
type Pool = I::Pool;
|
||||
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
self.inner.append_submission(base, queue)
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<I::Pool> {
|
||||
self.inner.inner()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,9 @@
|
||||
//! The `CommandsList` trait is implemented on any command buffer or command buffer builder that
|
||||
//! exposes a list of commands. It is required by some of the layers.
|
||||
|
||||
use command_buffer::pool::CommandPool;
|
||||
use device::DeviceOwned;
|
||||
|
||||
pub use self::auto_barriers::AutoPipelineBarriersLayer;
|
||||
pub use self::buffered::BufferedCommandsListLayer;
|
||||
pub use self::buffered::BufferedCommandsListLayerCommands;
|
||||
|
@ -12,10 +12,10 @@ use std::sync::Arc;
|
||||
|
||||
use command_buffer::cb::AddCommand;
|
||||
use command_buffer::cb::CommandBufferBuild;
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferBuilder;
|
||||
use command_buffer::cmd;
|
||||
use command_buffer::Submit;
|
||||
use command_buffer::SubmitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
@ -102,13 +102,12 @@ pub struct SubmitSyncLayer<I> {
|
||||
inner: I,
|
||||
}
|
||||
|
||||
unsafe impl<I> Submit for SubmitSyncLayer<I> where I: Submit {
|
||||
unsafe impl<I> CommandBuffer for SubmitSyncLayer<I> where I: CommandBuffer {
|
||||
type Pool = I::Pool;
|
||||
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
// FIXME:
|
||||
self.inner.append_submission(base, queue)
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<I::Pool> {
|
||||
self.inner.inner()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,10 @@ use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::cb::CommandBufferBuild;
|
||||
use command_buffer::pool::AllocatedCommandBuffer;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use command_buffer::Submit;
|
||||
use command_buffer::SubmitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
@ -287,12 +286,12 @@ pub struct UnsafeCommandBuffer<P> where P: CommandPool {
|
||||
already_submitted: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl<P> Submit for UnsafeCommandBuffer<P> where P: CommandPool {
|
||||
unsafe impl<P> CommandBuffer for UnsafeCommandBuffer<P> where P: CommandPool {
|
||||
type Pool = P;
|
||||
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, _queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
Ok(base.add_command_buffer(self))
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<P> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,8 @@
|
||||
pub use self::auto::AutoCommandBufferBuilder;
|
||||
pub use self::builder::CommandBufferBuilder;
|
||||
pub use self::builder::CommandBufferBuilderBuffered;
|
||||
pub use self::submit::Submission;
|
||||
pub use self::submit::Submit;
|
||||
pub use self::submit::SubmitBuilder;
|
||||
pub use self::submit::SubmitChain;
|
||||
pub use self::traits::CommandBuffer;
|
||||
pub use self::traits::CommandBufferExecFuture;
|
||||
|
||||
use pipeline::viewport::Viewport;
|
||||
use pipeline::viewport::Scissor;
|
||||
@ -55,10 +53,11 @@ use pipeline::viewport::Scissor;
|
||||
pub mod cb;
|
||||
pub mod cmd;
|
||||
pub mod pool;
|
||||
pub mod submit;
|
||||
|
||||
mod auto;
|
||||
mod builder;
|
||||
mod submit;
|
||||
mod traits;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
|
@ -1,533 +0,0 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 std::error::Error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use sync::Fence;
|
||||
use sync::FenceWaitError;
|
||||
use sync::PipelineStages;
|
||||
use sync::Semaphore;
|
||||
|
||||
use check_errors;
|
||||
use vk;
|
||||
use VulkanObject;
|
||||
use VulkanPointers;
|
||||
use SynchronizedVulkanObject;
|
||||
|
||||
/// Trait for objects that can be submitted to the GPU.
|
||||
// TODO: is Box<Error> appropriate? maybe something else?
|
||||
pub unsafe trait Submit: DeviceOwned {
|
||||
/// Submits the object to the queue.
|
||||
///
|
||||
/// Since submitting has a fixed overhead, you should try, if possible, to submit multiple
|
||||
/// command buffers at once instead. To do so, you can use the `chain` method.
|
||||
///
|
||||
/// `s.submit(queue)` is a shortcut for `s.submit_precise(queue).boxed()`.
|
||||
// TODO: add example
|
||||
#[inline]
|
||||
fn submit(self, queue: &Arc<Queue>) -> Result<Submission, Box<Error>>
|
||||
where Self: Sized + 'static
|
||||
{
|
||||
Ok(try!(self.submit_precise(queue)).boxed())
|
||||
}
|
||||
|
||||
/// Submits the object to the queue.
|
||||
///
|
||||
/// Since submitting has a fixed overhead, you should try, if possible, to submit multiple
|
||||
/// command buffers at once instead. To do so, you can use the `chain` method.
|
||||
///
|
||||
/// Contrary to `submit`, this method preserves strong typing in the submission. This means
|
||||
/// that it has a lower overhead but it is less convenient to store in a container.
|
||||
// TODO: add example
|
||||
#[inline]
|
||||
fn submit_precise(self, queue: &Arc<Queue>) -> Result<Submission<Self>, Box<Error>>
|
||||
where Self: Sized + 'static
|
||||
{
|
||||
submit(self, queue)
|
||||
}
|
||||
|
||||
/// Consumes this object and another one to return a `SubmitChain` object that will submit both
|
||||
/// at once.
|
||||
///
|
||||
/// `self` will be executed first, and then `other` afterwards.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the two objects don't belong to the same `Device`.
|
||||
///
|
||||
// TODO: add test for panic
|
||||
// TODO: add example
|
||||
#[inline]
|
||||
fn chain<S>(self, other: S) -> SubmitChain<Self, S> where Self: Sized, S: Submit {
|
||||
assert_eq!(self.device().internal_object(),
|
||||
other.device().internal_object());
|
||||
SubmitChain { first: self, second: other }
|
||||
}
|
||||
|
||||
/// Called slightly before the object is submitted. The function must modify an existing
|
||||
/// `SubmitBuilder` object to append the list of things to submit to it.
|
||||
///
|
||||
/// # Safety for the caller
|
||||
///
|
||||
/// This function must only be called if there's actually a submission with the returned
|
||||
/// parameters that follows.
|
||||
///
|
||||
/// This function is supposed to be called only by vulkano's internals. It is recommended
|
||||
/// that you never call it.
|
||||
///
|
||||
/// # Safety for the implementation
|
||||
///
|
||||
/// TODO: To write.
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>;
|
||||
}
|
||||
|
||||
unsafe impl<T> Submit for T where T: Deref, T::Target: Submit {
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
(**self).append_submission(base, queue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows building a submission.
|
||||
///
|
||||
/// This object contains a list of operations that the GPU should perform in order. You can add new
|
||||
/// operations to the list with the various `add_*` methods.
|
||||
///
|
||||
/// > **Note**: Command buffers submitted one after another are not executed in order. Instead they
|
||||
/// > are only guarateed to *start* in the order they were added. The object that implements
|
||||
/// > `Submit` should be aware of that fact and add appropriate pipeline barriers to the command
|
||||
/// > buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// While it is safe to build a `SubmitBuilder` from scratch, the only way to actually submit
|
||||
/// something is through the `Submit` trait which is unsafe to implement.
|
||||
// TODO: can be optimized by storing all the semaphores in a single vec and all command buffers
|
||||
// in a single vec
|
||||
// TODO: add sparse bindings and swapchain presents
|
||||
pub struct SubmitBuilder<'a> {
|
||||
semaphores_storage: SmallVec<[vk::Semaphore; 16]>,
|
||||
dest_stages_storage: SmallVec<[vk::PipelineStageFlags; 8]>,
|
||||
command_buffers_storage: SmallVec<[vk::CommandBuffer; 4]>,
|
||||
submits: SmallVec<[SubmitBuilderSubmit; 2]>,
|
||||
keep_alive_semaphores: SmallVec<[Arc<Semaphore>; 8]>,
|
||||
keep_alive_fences: SmallVec<[Arc<Fence>; 2]>,
|
||||
marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SubmitBuilderSubmit {
|
||||
batches: SmallVec<[vk::SubmitInfo; 4]>,
|
||||
fence: Option<Arc<Fence>>,
|
||||
}
|
||||
|
||||
impl<'a> SubmitBuilder<'a> {
|
||||
/// Builds a new empty `SubmitBuilder`.
|
||||
#[inline]
|
||||
pub fn new() -> SubmitBuilder<'a> {
|
||||
SubmitBuilder {
|
||||
semaphores_storage: SmallVec::new(),
|
||||
dest_stages_storage: SmallVec::new(),
|
||||
command_buffers_storage: SmallVec::new(),
|
||||
submits: SmallVec::new(),
|
||||
keep_alive_semaphores: SmallVec::new(),
|
||||
keep_alive_fences: SmallVec::new(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an operation that signals a fence.
|
||||
///
|
||||
/// > **Note**: Due to the way the Vulkan API is designed, you are strongly encouraged to use
|
||||
/// > only one fence and signal at the very end of the submission.
|
||||
///
|
||||
/// The fence is signalled after all previous operations of the `SubmitBuilder` are finished.
|
||||
#[inline]
|
||||
pub fn add_fence_signal(mut self, fence: Arc<Fence>) -> SubmitBuilder<'a> {
|
||||
if self.submits.last().map(|b| b.fence.is_some()).unwrap_or(true) {
|
||||
self.submits.push(Default::default());
|
||||
}
|
||||
|
||||
{
|
||||
let mut last = self.submits.last_mut().unwrap();
|
||||
debug_assert!(last.fence.is_none());
|
||||
self.keep_alive_fences.push(fence.clone());
|
||||
last.fence = Some(fence);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an operation that waits on a semaphore.
|
||||
///
|
||||
/// Only the given `stages` of the command buffers added afterwards will wait upon
|
||||
/// the semaphore. Other stages not included in `stages` can execute before waiting.
|
||||
///
|
||||
/// The semaphore must be signalled by a previous submission.
|
||||
#[inline]
|
||||
pub fn add_wait_semaphore(mut self, semaphore: Arc<Semaphore>, stages: PipelineStages)
|
||||
-> SubmitBuilder<'a>
|
||||
{
|
||||
// TODO: check device of the semaphore with a debug_assert
|
||||
// TODO: if stages contains tessellation or geometry stages, make sure the corresponding
|
||||
// feature is active with a debug_assert
|
||||
debug_assert!({ let f: vk::PipelineStageFlagBits = stages.into(); f != 0 });
|
||||
|
||||
if self.submits.last().map(|b| b.fence.is_some()).unwrap_or(true) {
|
||||
self.submits.push(Default::default());
|
||||
}
|
||||
|
||||
{
|
||||
let mut submit = self.submits.last_mut().unwrap();
|
||||
if submit.batches.last().map(|b| b.signalSemaphoreCount != 0 ||
|
||||
b.commandBufferCount != 0)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
submit.batches.push(SubmitBuilder::empty_vk_submit_info());
|
||||
}
|
||||
|
||||
submit.batches.last_mut().unwrap().waitSemaphoreCount += 1;
|
||||
self.dest_stages_storage.push(stages.into());
|
||||
self.semaphores_storage.push(semaphore.internal_object());
|
||||
self.keep_alive_semaphores.push(semaphore);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an operation that executes a command buffer.
|
||||
///
|
||||
/// > **Note**: Command buffers submitted one after another are not executed in order. Instead
|
||||
/// > they are only guarateed to *start* in the order they were added. The object that
|
||||
/// > implements `Submit` should be aware of that fact and add appropriate pipeline barriers
|
||||
/// > to the command buffers.
|
||||
///
|
||||
/// Thanks to the lifetime requirement, the command buffer must outlive the `Submit` object
|
||||
/// that builds this `SubmitBuilder`. Consequently keeping the `Submit` object alive is enough
|
||||
/// to guarantee that the command buffer is kept alive as well.
|
||||
#[inline]
|
||||
pub fn add_command_buffer<P>(self, command_buffer: &'a UnsafeCommandBuffer<P>)
|
||||
-> SubmitBuilder<'a>
|
||||
where P: CommandPool
|
||||
{
|
||||
self.add_command_buffer_raw(command_buffer.internal_object())
|
||||
}
|
||||
|
||||
// TODO: remove in favor of `add_command_buffer`?
|
||||
#[inline]
|
||||
pub fn add_command_buffer_raw(mut self, command_buffer: vk::CommandBuffer)
|
||||
-> SubmitBuilder<'a>
|
||||
{
|
||||
if self.submits.last().map(|b| b.fence.is_some()).unwrap_or(true) {
|
||||
self.submits.push(Default::default());
|
||||
}
|
||||
|
||||
{
|
||||
let mut submit = self.submits.last_mut().unwrap();
|
||||
if submit.batches.last().map(|b| b.signalSemaphoreCount != 0).unwrap_or(true) {
|
||||
submit.batches.push(SubmitBuilder::empty_vk_submit_info());
|
||||
}
|
||||
|
||||
self.command_buffers_storage.push(command_buffer);
|
||||
submit.batches.last_mut().unwrap().commandBufferCount += 1;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an operation that signals a semaphore.
|
||||
///
|
||||
/// The semaphore is signalled after all previous operations of the `SubmitBuilder` are
|
||||
/// finished.
|
||||
///
|
||||
/// The semaphore must be unsignalled.
|
||||
#[inline]
|
||||
pub fn add_signal_semaphore(mut self, semaphore: Arc<Semaphore>) -> SubmitBuilder<'a> {
|
||||
// TODO: check device of the semaphore with a debug_assert
|
||||
|
||||
if self.submits.last().map(|b| b.fence.is_some()).unwrap_or(true) {
|
||||
self.submits.push(Default::default());
|
||||
}
|
||||
|
||||
{
|
||||
let mut submit = self.submits.last_mut().unwrap();
|
||||
if submit.batches.is_empty() {
|
||||
submit.batches.push(SubmitBuilder::empty_vk_submit_info());
|
||||
}
|
||||
|
||||
submit.batches.last_mut().unwrap().signalSemaphoreCount += 1;
|
||||
self.semaphores_storage.push(semaphore.internal_object());
|
||||
self.keep_alive_semaphores.push(semaphore);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn empty_vk_submit_info() -> vk::SubmitInfo {
|
||||
vk::SubmitInfo {
|
||||
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
pNext: ptr::null(),
|
||||
waitSemaphoreCount: 0,
|
||||
pWaitSemaphores: ptr::null(),
|
||||
pWaitDstStageMask: ptr::null(),
|
||||
commandBufferCount: 0,
|
||||
pCommandBuffers: ptr::null(),
|
||||
signalSemaphoreCount: 0,
|
||||
pSignalSemaphores: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for `Submit::submit`.
|
||||
fn submit<S>(submit: S, queue: &Arc<Queue>) -> Result<Submission<S>, Box<Error>>
|
||||
where S: Submit + 'static
|
||||
{
|
||||
let last_fence;
|
||||
let keep_alive_semaphores;
|
||||
let keep_alive_fences;
|
||||
|
||||
unsafe {
|
||||
let mut builder = try!(submit.append_submission(SubmitBuilder::new(), queue));
|
||||
keep_alive_semaphores = builder.keep_alive_semaphores;
|
||||
keep_alive_fences = builder.keep_alive_fences;
|
||||
|
||||
last_fence = if let Some(last) = builder.submits.last_mut() {
|
||||
if last.fence.is_none() {
|
||||
last.fence = Some(Fence::new(submit.device().clone()));
|
||||
}
|
||||
|
||||
last.fence.as_ref().unwrap().clone()
|
||||
|
||||
} else {
|
||||
Fence::new(submit.device().clone()) // TODO: meh
|
||||
};
|
||||
|
||||
{
|
||||
let vk = queue.device().pointers();
|
||||
let queue = queue.internal_object_guard();
|
||||
|
||||
let mut next_semaphore = 0;
|
||||
let mut next_wait_stage = 0;
|
||||
let mut next_command_buffer = 0;
|
||||
|
||||
for submit in builder.submits.iter_mut() {
|
||||
for batch in submit.batches.iter_mut() {
|
||||
batch.pWaitSemaphores = builder.semaphores_storage
|
||||
.as_ptr().offset(next_semaphore);
|
||||
batch.pWaitDstStageMask = builder.dest_stages_storage
|
||||
.as_ptr().offset(next_wait_stage);
|
||||
next_semaphore += batch.waitSemaphoreCount as isize;
|
||||
next_wait_stage += batch.waitSemaphoreCount as isize;
|
||||
batch.pCommandBuffers = builder.command_buffers_storage
|
||||
.as_ptr().offset(next_command_buffer);
|
||||
next_command_buffer += batch.commandBufferCount as isize;
|
||||
batch.pSignalSemaphores = builder.semaphores_storage
|
||||
.as_ptr().offset(next_semaphore);
|
||||
next_semaphore += batch.signalSemaphoreCount as isize;
|
||||
}
|
||||
|
||||
let fence = submit.fence.as_ref().map(|f| f.internal_object()).unwrap_or(0);
|
||||
check_errors(vk.QueueSubmit(*queue, submit.batches.len() as u32,
|
||||
submit.batches.as_ptr(), fence)).unwrap(); // TODO: handle errors (trickier than it looks)
|
||||
|
||||
}
|
||||
|
||||
debug_assert_eq!(next_semaphore as usize, builder.semaphores_storage.len());
|
||||
debug_assert_eq!(next_wait_stage as usize, builder.dest_stages_storage.len());
|
||||
debug_assert_eq!(next_command_buffer as usize, builder.command_buffers_storage.len());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Submission {
|
||||
queue: queue.clone(),
|
||||
fence: FenceWithWaiting(last_fence),
|
||||
keep_alive_semaphores: keep_alive_semaphores,
|
||||
keep_alive_fences: keep_alive_fences,
|
||||
submit: submit,
|
||||
})
|
||||
}
|
||||
|
||||
/// Chain of two `Submit` objects. See `Submit::chain`.
|
||||
pub struct SubmitChain<A, B> {
|
||||
first: A,
|
||||
second: B,
|
||||
}
|
||||
|
||||
unsafe impl<A, B> Submit for SubmitChain<A, B> where A: Submit, B: Submit {
|
||||
#[inline]
|
||||
unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
|
||||
-> Result<SubmitBuilder<'a>, Box<Error>>
|
||||
{
|
||||
// FIXME: huge problem here ; if the second one returns an error, the first one has been
|
||||
// called without any actual following submission
|
||||
let builder = try!(self.first.append_submission(base, queue));
|
||||
self.second.append_submission(builder, queue)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A, B> DeviceOwned for SubmitChain<A, B> where A: DeviceOwned, B: DeviceOwned {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
debug_assert_eq!(self.first.device().internal_object(),
|
||||
self.second.device().internal_object());
|
||||
|
||||
self.first.device()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned when you submit something to a queue.
|
||||
///
|
||||
/// This object holds the resources that are used by the GPU and that must be kept alive for at
|
||||
/// least as long as the GPU is executing the submission. Therefore destroying a `Submission`
|
||||
/// object will block until the GPU is finished executing.
|
||||
///
|
||||
/// Whenever you submit a command buffer, you are encouraged to store the returned `Submission`
|
||||
/// in a long-living container such as a `Vec`. From time to time, you can clean the obsolete
|
||||
/// objects by checking whether `destroying_would_block()` returns false. For example, if you use
|
||||
/// a `Vec` you can do `vec.retain(|s| s.destroying_would_block())`.
|
||||
///
|
||||
/// # Leak safety
|
||||
///
|
||||
/// One of the roles of the `Submission` object is to keep alive the objects used by the GPU during
|
||||
/// the submission. However if the user calls `std::mem::forget` on the `Submission`, all borrows
|
||||
/// are immediately free'd. This is known as *the leakpocalypse*.
|
||||
///
|
||||
/// In order to avoid this problem, only `'static` objects can be put in a `Submission`.
|
||||
// TODO: ^ decide whether we allow to add an unsafe non-static constructor
|
||||
#[must_use]
|
||||
pub struct Submission<S: 'static = Box<Submit>> {
|
||||
fence: FenceWithWaiting, // TODO: make optional
|
||||
queue: Arc<Queue>,
|
||||
keep_alive_semaphores: SmallVec<[Arc<Semaphore>; 8]>,
|
||||
keep_alive_fences: SmallVec<[Arc<Fence>; 2]>,
|
||||
submit: S,
|
||||
}
|
||||
|
||||
struct FenceWithWaiting(Arc<Fence>);
|
||||
impl Drop for FenceWithWaiting {
|
||||
fn drop(&mut self) {
|
||||
self.0.wait(Duration::from_secs(10)).unwrap(); // TODO: handle some errors
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> fmt::Debug for Submission<S> {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
// TODO: better impl?
|
||||
write!(fmt, "<Vulkan submission>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Submission<S> {
|
||||
/// Returns `true` if destroying this `Submission` object would block the CPU for some time.
|
||||
#[inline]
|
||||
pub fn destroying_would_block(&self) -> bool {
|
||||
!self.finished()
|
||||
}
|
||||
|
||||
/// Returns `true` if the GPU has finished executing this submission.
|
||||
#[inline]
|
||||
pub fn finished(&self) -> bool {
|
||||
self.fence.0.ready().unwrap_or(false) // TODO: what to do in case of error?
|
||||
}
|
||||
|
||||
/// Waits until the submission has finished.
|
||||
#[inline]
|
||||
pub fn wait(&self, timeout: Duration) -> Result<(), FenceWaitError> {
|
||||
self.fence.0.wait(timeout)
|
||||
}
|
||||
|
||||
/// Returns the queue the submission was submitted to.
|
||||
#[inline]
|
||||
pub fn queue(&self) -> &Arc<Queue> {
|
||||
&self.queue
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Submission<S> where S: Submit + 'static {
|
||||
/// Turns this submission into a boxed submission.
|
||||
pub fn boxed(self) -> Submission {
|
||||
Submission {
|
||||
fence: self.fence,
|
||||
queue: self.queue,
|
||||
keep_alive_semaphores: self.keep_alive_semaphores,
|
||||
keep_alive_fences: self.keep_alive_fences,
|
||||
submit: Box::new(self.submit) as Box<_>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: restore
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter;
|
||||
use std::iter::Empty;
|
||||
use std::sync::Arc;
|
||||
|
||||
use command_buffer::pool::StandardCommandPool;
|
||||
use command_buffer::submit::CommandBuffer;
|
||||
use command_buffer::submit::SubmitInfo;
|
||||
use command_buffer::sys::Kind;
|
||||
use command_buffer::sys::Flags;
|
||||
use command_buffer::sys::PipelineBarrierBuilder;
|
||||
use command_buffer::sys::UnsafeCommandBuffer;
|
||||
use command_buffer::sys::UnsafeCommandBufferBuilder;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use framebuffer::framebuffer::EmptyAttachmentsList;
|
||||
use framebuffer::EmptySinglePassRenderPass;
|
||||
use framebuffer::Framebuffer;
|
||||
use sync::Fence;
|
||||
use sync::PipelineStages;
|
||||
use sync::Semaphore;
|
||||
|
||||
#[test]
|
||||
fn basic_submit() {
|
||||
struct Basic { inner: UnsafeCommandBuffer<Arc<StandardCommandPool>> }
|
||||
unsafe impl CommandBuffer for Basic {
|
||||
type Pool = Arc<StandardCommandPool>;
|
||||
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> { &self.inner }
|
||||
|
||||
unsafe fn on_submit<F>(&self, _: &Arc<Queue>, fence: F)
|
||||
-> SubmitInfo<Self::SemaphoresWaitIterator,
|
||||
Self::SemaphoresSignalIterator>
|
||||
where F: FnOnce() -> Arc<Fence>
|
||||
{
|
||||
SubmitInfo::empty()
|
||||
}
|
||||
}
|
||||
|
||||
let (device, queue) = gfx_dev_and_queue!();
|
||||
|
||||
let pool = Device::standard_command_pool(&device, queue.family());
|
||||
let kind = Kind::Primary::<EmptySinglePassRenderPass, Framebuffer<EmptySinglePassRenderPass, EmptyAttachmentsList>>;
|
||||
|
||||
let cb = UnsafeCommandBufferBuilder::new(pool, kind, Flags::OneTimeSubmit).unwrap();
|
||||
let cb = Basic { inner: cb.build().unwrap() };
|
||||
|
||||
let _s = cb.submit(&queue);
|
||||
}
|
||||
}*/
|
40
vulkano/src/command_buffer/submit/mod.rs
Normal file
40
vulkano/src/command_buffer/submit/mod.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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.
|
||||
|
||||
//! Low-level builders that allow submitting an operation to a queue.
|
||||
|
||||
pub use self::queue_present::SubmitPresentBuilder;
|
||||
pub use self::queue_present::SubmitPresentError;
|
||||
pub use self::queue_submit::SubmitCommandBufferBuilder;
|
||||
pub use self::queue_submit::SubmitCommandBufferError;
|
||||
pub use self::semaphores_wait::SubmitSemaphoresWaitBuilder;
|
||||
|
||||
mod queue_present;
|
||||
mod queue_submit;
|
||||
mod semaphores_wait;
|
||||
|
||||
/// Contains all the possible submission builders.
|
||||
#[derive(Debug)]
|
||||
pub enum SubmitAnyBuilder<'a> {
|
||||
Empty,
|
||||
SemaphoresWait(SubmitSemaphoresWaitBuilder<'a>),
|
||||
CommandBuffer(SubmitCommandBufferBuilder<'a>),
|
||||
QueuePresent(SubmitPresentBuilder<'a>),
|
||||
}
|
||||
|
||||
impl<'a> SubmitAnyBuilder<'a> {
|
||||
/// Returns true if equal to `SubmitAnyBuilder::Empty`.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
&SubmitAnyBuilder::Empty => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
176
vulkano/src/command_buffer/submit/queue_present.rs
Normal file
176
vulkano/src/command_buffer/submit/queue_present.rs
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2017 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use device::Queue;
|
||||
use swapchain::Swapchain;
|
||||
use sync::Fence;
|
||||
use sync::PipelineStages;
|
||||
use sync::Semaphore;
|
||||
|
||||
use check_errors;
|
||||
use vk;
|
||||
use Error;
|
||||
use OomError;
|
||||
use VulkanObject;
|
||||
use VulkanPointers;
|
||||
use SynchronizedVulkanObject;
|
||||
|
||||
/// Prototype for a submission that presents a swapchain on the screen.
|
||||
#[derive(Debug)]
|
||||
pub struct SubmitPresentBuilder<'a> {
|
||||
wait_semaphores: SmallVec<[vk::Semaphore; 8]>,
|
||||
swapchains: SmallVec<[vk::SwapchainKHR; 4]>,
|
||||
image_indices: SmallVec<[u32; 4]>,
|
||||
marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> SubmitPresentBuilder<'a> {
|
||||
/// Builds a new empty `SubmitPresentBuilder`.
|
||||
#[inline]
|
||||
pub fn new() -> SubmitPresentBuilder<'a> {
|
||||
SubmitPresentBuilder {
|
||||
wait_semaphores: SmallVec::new(),
|
||||
swapchains: SmallVec::new(),
|
||||
image_indices: SmallVec::new(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a semaphore to be waited upon before the presents are executed.
|
||||
#[inline]
|
||||
pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
|
||||
self.wait_semaphores.push(semaphore.internal_object());
|
||||
}
|
||||
|
||||
/// Adds an image of a swapchain to be presented.
|
||||
#[inline]
|
||||
pub unsafe fn add_swapchain(&mut self, swapchain: &'a Swapchain, image_num: u32) {
|
||||
debug_assert!(image_num < swapchain.num_images());
|
||||
self.swapchains.push(swapchain.internal_object());
|
||||
self.image_indices.push(image_num);
|
||||
}
|
||||
|
||||
/// Submits the command. Calls `vkQueuePresentKHR`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if no swapchain image has been added to the builder.
|
||||
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 vk = queue.device().pointers();
|
||||
let queue = queue.internal_object_guard();
|
||||
|
||||
let mut results = vec![mem::uninitialized(); self.swapchains.len()]; // TODO: alloca
|
||||
|
||||
let infos = vk::PresentInfoKHR {
|
||||
sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
pNext: 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(),
|
||||
};
|
||||
|
||||
try!(check_errors(vk.QueuePresentKHR(*queue, &infos)));
|
||||
|
||||
for result in results {
|
||||
// TODO: AMD driver initially didn't write the results ; check that it's been fixed
|
||||
//try!(check_errors(result));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when submitting the present prototype.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum SubmitPresentError {
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// The connection to the device has been lost.
|
||||
DeviceLost,
|
||||
|
||||
/// The surface is no longer accessible and must be recreated.
|
||||
SurfaceLost,
|
||||
|
||||
/// The surface has changed in a way that makes the swapchain unusable. You must query the
|
||||
/// surface's new properties and recreate a new swapchain if you want to continue drawing.
|
||||
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]
|
||||
#[should_panic(expected = "Tried to submit a present command without any swapchain")]
|
||||
fn no_swapchain_added() {
|
||||
let (_, queue) = gfx_dev_and_queue!();
|
||||
let _ = SubmitPresentBuilder::new().submit(&queue);
|
||||
}
|
||||
}
|
175
vulkano/src/command_buffer/submit/queue_submit.rs
Normal file
175
vulkano/src/command_buffer/submit/queue_submit.rs
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2017 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use device::Queue;
|
||||
use sync::Fence;
|
||||
use sync::PipelineStages;
|
||||
use sync::Semaphore;
|
||||
|
||||
use check_errors;
|
||||
use vk;
|
||||
use Error;
|
||||
use OomError;
|
||||
use VulkanObject;
|
||||
use VulkanPointers;
|
||||
use SynchronizedVulkanObject;
|
||||
|
||||
/// Prototype for a submission that executes command buffers.
|
||||
#[derive(Debug)]
|
||||
pub struct SubmitCommandBufferBuilder<'a> {
|
||||
wait_semaphores: SmallVec<[vk::Semaphore; 16]>,
|
||||
dest_stages: SmallVec<[vk::PipelineStageFlags; 8]>,
|
||||
signal_semaphores: SmallVec<[vk::Semaphore; 16]>,
|
||||
command_buffers: SmallVec<[vk::CommandBuffer; 4]>,
|
||||
fence: vk::Fence,
|
||||
marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> SubmitCommandBufferBuilder<'a> {
|
||||
/// Builds a new empty `SubmitCommandBufferBuilder`.
|
||||
#[inline]
|
||||
pub fn new() -> SubmitCommandBufferBuilder<'a> {
|
||||
SubmitCommandBufferBuilder {
|
||||
wait_semaphores: SmallVec::new(),
|
||||
dest_stages: SmallVec::new(),
|
||||
signal_semaphores: SmallVec::new(),
|
||||
command_buffers: SmallVec::new(),
|
||||
fence: 0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this builder will signal a fence when submitted.
|
||||
#[inline]
|
||||
pub fn has_fence(&self) -> bool {
|
||||
self.fence != 0
|
||||
}
|
||||
|
||||
/// Adds an operation that signals a fence after this submission ends.
|
||||
///
|
||||
/// If a fence was previously set, it will no longer be signaled.
|
||||
#[inline]
|
||||
pub unsafe fn set_fence_signal(&mut self, fence: &'a Fence) {
|
||||
self.fence = fence.internal_object()
|
||||
}
|
||||
|
||||
/// Adds a semaphore to be waited upon before the command buffers are executed.
|
||||
///
|
||||
/// Only the given `stages` of the command buffers added afterwards will wait upon
|
||||
/// the semaphore. Other stages not included in `stages` can execute before waiting.
|
||||
#[inline]
|
||||
pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore, stages: PipelineStages) {
|
||||
debug_assert!(Into::<vk::PipelineStageFlagBits>::into(stages) != 0);
|
||||
self.wait_semaphores.push(semaphore.internal_object());
|
||||
self.dest_stages.push(stages.into());
|
||||
}
|
||||
|
||||
/// Adds a command buffer that is executed as part of this command.
|
||||
///
|
||||
/// The command buffers are submitted in the order in which they are added.
|
||||
#[inline]
|
||||
pub unsafe fn add_command_buffer<P>(&mut self, command_buffer: &'a UnsafeCommandBuffer<P>)
|
||||
where P: CommandPool
|
||||
{
|
||||
self.command_buffers.push(command_buffer.internal_object());
|
||||
}
|
||||
|
||||
/// Returns the number of semaphores to signal.
|
||||
#[inline]
|
||||
pub fn num_signal_semaphores(&self) -> usize {
|
||||
self.signal_semaphores.len()
|
||||
}
|
||||
|
||||
/// Adds a semaphore that is going to be signaled at the end of the submission.
|
||||
#[inline]
|
||||
pub unsafe fn add_signal_semaphore(&mut self, semaphore: &'a Semaphore) {
|
||||
self.signal_semaphores.push(semaphore.internal_object());
|
||||
}
|
||||
|
||||
/// Submits the command buffer.
|
||||
pub fn submit(mut self, queue: &Queue) -> Result<(), SubmitCommandBufferError> {
|
||||
unsafe {
|
||||
let vk = queue.device().pointers();
|
||||
let queue = queue.internal_object_guard();
|
||||
|
||||
debug_assert_eq!(self.wait_semaphores.len(), self.dest_stages.len());
|
||||
|
||||
let batch = vk::SubmitInfo {
|
||||
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
pNext: ptr::null(),
|
||||
waitSemaphoreCount: self.wait_semaphores.len() as u32,
|
||||
pWaitSemaphores: self.wait_semaphores.as_ptr(),
|
||||
pWaitDstStageMask: self.dest_stages.as_ptr(),
|
||||
commandBufferCount: self.command_buffers.len() as u32,
|
||||
pCommandBuffers: self.command_buffers.as_ptr(),
|
||||
signalSemaphoreCount: self.signal_semaphores.len() as u32,
|
||||
pSignalSemaphores: self.signal_semaphores.as_ptr(),
|
||||
};
|
||||
|
||||
try!(check_errors(vk.QueueSubmit(*queue, 1, &batch, self.fence)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when submitting the prototype.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum SubmitCommandBufferError {
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// The connection to the device has been lost.
|
||||
DeviceLost,
|
||||
}
|
||||
|
||||
impl error::Error for SubmitCommandBufferError {
|
||||
#[inline]
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
SubmitCommandBufferError::OomError(_) => "not enough memory",
|
||||
SubmitCommandBufferError::DeviceLost => "the connection to the device has been lost",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
SubmitCommandBufferError::OomError(ref err) => Some(err),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SubmitCommandBufferError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for SubmitCommandBufferError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> SubmitCommandBufferError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => SubmitCommandBufferError::OomError(OomError::from(err)),
|
||||
err @ Error::OutOfDeviceMemory => SubmitCommandBufferError::OomError(OomError::from(err)),
|
||||
Error::DeviceLost => SubmitCommandBufferError::DeviceLost,
|
||||
_ => panic!("unexpected error: {:?}", err)
|
||||
}
|
||||
}
|
||||
}
|
81
vulkano/src/command_buffer/submit/semaphores_wait.rs
Normal file
81
vulkano/src/command_buffer/submit/semaphores_wait.rs
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 smallvec::SmallVec;
|
||||
|
||||
use command_buffer::submit::SubmitCommandBufferBuilder;
|
||||
use command_buffer::submit::SubmitPresentBuilder;
|
||||
use sync::PipelineStages;
|
||||
use sync::Semaphore;
|
||||
|
||||
use vk;
|
||||
use VulkanObject;
|
||||
|
||||
/// Prototype for a submission that waits on semaphores.
|
||||
///
|
||||
/// This prototype can't actually be submitted because it doesn't correspond to anything in Vulkan.
|
||||
/// However you can convert it into another builder prototype through the `Into` trait.
|
||||
#[derive(Debug)]
|
||||
pub struct SubmitSemaphoresWaitBuilder<'a> {
|
||||
semaphores: SmallVec<[&'a Semaphore; 8]>,
|
||||
}
|
||||
|
||||
impl<'a> SubmitSemaphoresWaitBuilder<'a> {
|
||||
/// Builds a new empty `SubmitSemaphoresWaitBuilder`.
|
||||
#[inline]
|
||||
pub fn new() -> SubmitSemaphoresWaitBuilder<'a> {
|
||||
SubmitSemaphoresWaitBuilder {
|
||||
semaphores: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an operation that waits on a semaphore.
|
||||
///
|
||||
/// The semaphore must be signaled by a previous submission.
|
||||
#[inline]
|
||||
pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
|
||||
self.semaphores.push(semaphore);
|
||||
}
|
||||
|
||||
/// Merges this builder with another builder.
|
||||
#[inline]
|
||||
pub fn merge(&mut self, mut other: SubmitSemaphoresWaitBuilder<'a>) {
|
||||
self.semaphores.extend(other.semaphores.drain());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<SubmitCommandBufferBuilder<'a>> for SubmitSemaphoresWaitBuilder<'a> {
|
||||
#[inline]
|
||||
fn into(mut self) -> SubmitCommandBufferBuilder<'a> {
|
||||
unsafe {
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
for sem in self.semaphores.drain() {
|
||||
builder.add_wait_semaphore(sem, PipelineStages {
|
||||
// TODO: correct stages ; hard
|
||||
all_commands: true,
|
||||
.. PipelineStages::none()
|
||||
});
|
||||
}
|
||||
builder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<SubmitPresentBuilder<'a>> for SubmitSemaphoresWaitBuilder<'a> {
|
||||
#[inline]
|
||||
fn into(mut self) -> SubmitPresentBuilder<'a> {
|
||||
unsafe {
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
for sem in self.semaphores.drain() {
|
||||
builder.add_wait_semaphore(sem);
|
||||
}
|
||||
builder
|
||||
}
|
||||
}
|
||||
}
|
214
vulkano/src/command_buffer/traits.rs
Normal file
214
vulkano/src/command_buffer/traits.rs
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 std::error;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use buffer::Buffer;
|
||||
use command_buffer::cb::UnsafeCommandBuffer;
|
||||
use command_buffer::pool::CommandPool;
|
||||
use command_buffer::submit::SubmitAnyBuilder;
|
||||
use command_buffer::submit::SubmitCommandBufferBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::Image;
|
||||
use sync::DummyFuture;
|
||||
use sync::GpuFuture;
|
||||
use SafeDeref;
|
||||
use VulkanObject;
|
||||
|
||||
pub unsafe trait CommandBuffer: DeviceOwned {
|
||||
/// The command pool of the command buffer.
|
||||
type Pool: CommandPool;
|
||||
|
||||
/// Returns the underlying `UnsafeCommandBuffer` of this command buffer.
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool>;
|
||||
|
||||
/// Executes this command buffer on a queue.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if the device of the command buffer is not the same as the device of the future.
|
||||
#[inline]
|
||||
fn execute(self, queue: Arc<Queue>) -> CommandBufferExecFuture<DummyFuture, Self>
|
||||
where Self: Sized
|
||||
{
|
||||
let device = queue.device().clone();
|
||||
self.execute_after(DummyFuture::new(device), queue)
|
||||
}
|
||||
|
||||
/// Executes the command buffer after an existing future.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if the device of the command buffer is not the same as the device of the future.
|
||||
#[inline]
|
||||
fn execute_after<F>(self, future: F, queue: Arc<Queue>) -> CommandBufferExecFuture<F, Self>
|
||||
where Self: Sized, F: GpuFuture
|
||||
{
|
||||
assert_eq!(self.device().internal_object(), future.device().internal_object());
|
||||
|
||||
if !future.queue_change_allowed() {
|
||||
assert!(future.queue().unwrap().is_same(&queue));
|
||||
}
|
||||
|
||||
CommandBufferExecFuture {
|
||||
previous: future,
|
||||
command_buffer: self,
|
||||
queue: queue,
|
||||
submitted: Mutex::new(false),
|
||||
finished: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: lots of other methods
|
||||
}
|
||||
|
||||
unsafe impl<T> CommandBuffer for T where T: SafeDeref, T::Target: CommandBuffer {
|
||||
type Pool = <T::Target as CommandBuffer>::Pool;
|
||||
|
||||
#[inline]
|
||||
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> {
|
||||
(**self).inner()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a command buffer being executed by the GPU and the moment when the execution
|
||||
/// finishes.
|
||||
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
|
||||
pub struct CommandBufferExecFuture<F, Cb> where F: GpuFuture, Cb: CommandBuffer {
|
||||
previous: F,
|
||||
command_buffer: Cb,
|
||||
queue: Arc<Queue>,
|
||||
// True if the command buffer has already been submitted.
|
||||
// If flush is called multiple times, we want to block so that only one flushing is executed.
|
||||
// Therefore we use a `Mutex<bool>` and not an `AtomicBool`.
|
||||
submitted: Mutex<bool>,
|
||||
finished: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl<F, Cb> GpuFuture for CommandBufferExecFuture<F, Cb>
|
||||
where F: GpuFuture, Cb: CommandBuffer
|
||||
{
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
self.finished.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<error::Error>> {
|
||||
Ok(match try!(self.previous.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
builder.add_command_buffer(self.command_buffer.inner());
|
||||
SubmitAnyBuilder::CommandBuffer(builder)
|
||||
},
|
||||
SubmitAnyBuilder::SemaphoresWait(sem) => {
|
||||
let mut builder: SubmitCommandBufferBuilder = sem.into();
|
||||
builder.add_command_buffer(self.command_buffer.inner());
|
||||
SubmitAnyBuilder::CommandBuffer(builder)
|
||||
},
|
||||
SubmitAnyBuilder::CommandBuffer(mut builder) => {
|
||||
// FIXME: add pipeline barrier
|
||||
builder.add_command_buffer(self.command_buffer.inner());
|
||||
SubmitAnyBuilder::CommandBuffer(builder)
|
||||
},
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
unimplemented!() // TODO:
|
||||
/*present.submit(); // TODO: wrong
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
builder.add_command_buffer(self.command_buffer.inner());
|
||||
SubmitAnyBuilder::CommandBuffer(builder)*/
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<error::Error>> {
|
||||
unsafe {
|
||||
let mut submitted = self.submitted.lock().unwrap();
|
||||
if *submitted {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let queue = self.queue.clone();
|
||||
|
||||
match try!(self.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {},
|
||||
SubmitAnyBuilder::CommandBuffer(builder) => {
|
||||
try!(builder.submit(&queue));
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Only write `true` here in order to try again next time if we failed to submit.
|
||||
*submitted = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
self.finished.store(true, Ordering::SeqCst);
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
debug_assert!(match self.previous.queue() {
|
||||
None => true,
|
||||
Some(q) => q.is_same(&self.queue)
|
||||
});
|
||||
|
||||
Some(&self.queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
// FIXME: check the command buffer too
|
||||
self.previous.check_buffer_access(buffer, exclusive, queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
// FIXME: check the command buffer too
|
||||
self.previous.check_image_access(image, exclusive, queue)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F, Cb> DeviceOwned for CommandBufferExecFuture<F, Cb>
|
||||
where F: GpuFuture, Cb: CommandBuffer
|
||||
{
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.command_buffer.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Cb> Drop for CommandBufferExecFuture<F, Cb> where F: GpuFuture, Cb: CommandBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !*self.finished.get_mut() {
|
||||
// TODO: handle errors?
|
||||
self.flush().unwrap();
|
||||
// Block until the queue finished.
|
||||
self.queue.wait().unwrap();
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -553,7 +553,7 @@ impl From<Error> for DeviceCreationError {
|
||||
}
|
||||
|
||||
/// Represents a queue where commands can be submitted.
|
||||
// TODO: should use internal synchronization
|
||||
// TODO: should use internal synchronization?
|
||||
#[derive(Debug)]
|
||||
pub struct Queue {
|
||||
queue: Mutex<vk::Queue>,
|
||||
@ -569,6 +569,14 @@ impl Queue {
|
||||
&self.device
|
||||
}
|
||||
|
||||
/// Returns true if this is the same queue as another one.
|
||||
#[inline]
|
||||
pub fn is_same(&self, other: &Queue) -> bool {
|
||||
self.id == other.id &&
|
||||
self.family == other.family &&
|
||||
self.device.internal_object() == other.device.internal_object()
|
||||
}
|
||||
|
||||
/// Returns the family this queue belongs to.
|
||||
#[inline]
|
||||
pub fn family(&self) -> QueueFamily {
|
||||
@ -581,9 +589,11 @@ impl Queue {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// See the docs of wait().
|
||||
/// Waits until all work on this queue has finished.
|
||||
///
|
||||
/// Just like `Device::wait()`, you shouldn't have to call this function in a typical program.
|
||||
#[inline]
|
||||
pub fn wait_raw(&self) -> Result<(), OomError> {
|
||||
pub fn wait(&self) -> Result<(), OomError> {
|
||||
unsafe {
|
||||
let vk = self.device.pointers();
|
||||
let queue = self.queue.lock().unwrap();
|
||||
@ -591,19 +601,6 @@ impl Queue {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until all work on this queue has finished.
|
||||
///
|
||||
/// Just like `Device::wait()`, you shouldn't have to call this function.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn wait(&self) {
|
||||
self.wait_raw().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl SynchronizedVulkanObject for Queue {
|
||||
|
@ -9,11 +9,9 @@
|
||||
|
||||
use std::iter::Empty;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use format::ClearValue;
|
||||
use format::FormatDesc;
|
||||
use format::FormatTy;
|
||||
@ -81,18 +79,6 @@ pub struct AttachmentImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
// Layout to use when the image is used as a framebuffer attachment.
|
||||
// Must be either "depth-stencil optimal" or "color optimal".
|
||||
attachment_layout: Layout,
|
||||
|
||||
// Additional info behind a mutex.
|
||||
guarded: Mutex<Guarded>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Guarded {
|
||||
// If false, the image is still in the undefined layout.
|
||||
correct_layout: bool,
|
||||
|
||||
// The latest submission that used the image. Used for synchronization purposes.
|
||||
latest_submission: Option<Weak<Submission>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
}
|
||||
|
||||
impl<F> AttachmentImage<F> {
|
||||
@ -182,10 +168,6 @@ impl<F> AttachmentImage<F> {
|
||||
format: format,
|
||||
attachment_layout: if is_depth { Layout::DepthStencilAttachmentOptimal }
|
||||
else { Layout::ColorAttachmentOptimal },
|
||||
guarded: Mutex::new(Guarded {
|
||||
correct_layout: false,
|
||||
latest_submission: None,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -209,6 +191,11 @@ unsafe impl<F, A> Image for AttachmentImage<F, A> where F: 'static + Send + Sync
|
||||
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
|
||||
self.image.key()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
false // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageClearValue<F::ClearValue> for AttachmentImage<F, A>
|
||||
|
@ -9,13 +9,10 @@
|
||||
|
||||
use std::iter::Empty;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use format::FormatDesc;
|
||||
use image::Dimensions;
|
||||
use image::sys::ImageCreationError;
|
||||
@ -43,13 +40,6 @@ pub struct ImmutableImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
dimensions: Dimensions,
|
||||
memory: A::Alloc,
|
||||
format: F,
|
||||
per_layer: SmallVec<[PerLayer; 1]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PerLayer {
|
||||
latest_write_submission: Mutex<Option<Weak<Submission>>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
started_reading: AtomicBool,
|
||||
}
|
||||
|
||||
impl<F> ImmutableImage<F> {
|
||||
@ -104,16 +94,6 @@ impl<F> ImmutableImage<F> {
|
||||
memory: mem,
|
||||
dimensions: dimensions,
|
||||
format: format,
|
||||
per_layer: {
|
||||
let mut v = SmallVec::new();
|
||||
for _ in 0 .. dimensions.array_layers_with_cube() {
|
||||
v.push(PerLayer {
|
||||
latest_write_submission: Mutex::new(None),
|
||||
started_reading: AtomicBool::new(false),
|
||||
});
|
||||
}
|
||||
v
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -136,6 +116,11 @@ unsafe impl<F, A> Image for ImmutableImage<F, A> where F: 'static + Send + Sync,
|
||||
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
|
||||
self.image.key()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
false // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<P, F, A> ImageContent<P> for ImmutableImage<F, A>
|
||||
|
@ -9,12 +9,10 @@
|
||||
|
||||
use std::iter::Empty;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use command_buffer::Submission;
|
||||
use device::Device;
|
||||
use device::Queue;
|
||||
use format::ClearValue;
|
||||
use format::FormatDesc;
|
||||
use format::FormatTy;
|
||||
@ -56,21 +54,6 @@ pub struct StorageImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
|
||||
|
||||
// Queue families allowed to access this image.
|
||||
queue_families: SmallVec<[u32; 4]>,
|
||||
|
||||
// Additional info behind a mutex.
|
||||
guarded: Mutex<Guarded>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Guarded {
|
||||
// If false, the image is still in the undefined layout.
|
||||
correct_layout: bool,
|
||||
|
||||
// The latest submissions that read from this image.
|
||||
read_submissions: SmallVec<[Weak<Submission>; 4]>,
|
||||
|
||||
// The latest submission that writes to this image.
|
||||
write_submission: Option<Weak<Submission>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
}
|
||||
|
||||
impl<F> StorageImage<F> {
|
||||
@ -139,11 +122,6 @@ impl<F> StorageImage<F> {
|
||||
dimensions: dimensions,
|
||||
format: format,
|
||||
queue_families: queue_families,
|
||||
guarded: Mutex::new(Guarded {
|
||||
correct_layout: false,
|
||||
read_submissions: SmallVec::new(),
|
||||
write_submission: None,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -166,6 +144,11 @@ unsafe impl<F, A> Image for StorageImage<F, A> where F: 'static + Send + Sync, A
|
||||
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
|
||||
self.image.key()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
false // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F, A> ImageClearValue<F::ClearValue> for StorageImage<F, A>
|
||||
|
@ -8,10 +8,8 @@
|
||||
// according to those terms.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
|
||||
use command_buffer::Submission;
|
||||
use device::Queue;
|
||||
use format::ClearValue;
|
||||
use format::Format;
|
||||
use format::FormatDesc;
|
||||
@ -50,13 +48,6 @@ pub struct SwapchainImage {
|
||||
format: Format,
|
||||
swapchain: Arc<Swapchain>,
|
||||
id: u32,
|
||||
guarded: Mutex<Guarded>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Guarded {
|
||||
present_layout: bool,
|
||||
latest_submission: Option<Weak<Submission>>, // TODO: can use `Weak::new()` once it's stabilized
|
||||
}
|
||||
|
||||
impl SwapchainImage {
|
||||
@ -74,10 +65,6 @@ impl SwapchainImage {
|
||||
format: format,
|
||||
swapchain: swapchain.clone(),
|
||||
id: id,
|
||||
guarded: Mutex::new(Guarded {
|
||||
present_layout: false,
|
||||
latest_submission: None,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
@ -114,6 +101,12 @@ unsafe impl Image for SwapchainImage {
|
||||
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
|
||||
self.image.key()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, _: bool, _: &Queue) -> bool {
|
||||
// Swapchain image are only accessible after being acquired.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ImageClearValue<<Format as FormatDesc>::ClearValue> for SwapchainImage
|
||||
|
@ -8,6 +8,7 @@
|
||||
// according to those terms.
|
||||
|
||||
use buffer::Buffer;
|
||||
use device::Queue;
|
||||
use format::ClearValue;
|
||||
use format::Format;
|
||||
use format::PossibleFloatFormatDesc;
|
||||
@ -135,6 +136,16 @@ pub unsafe trait Image {
|
||||
/// verify whether they actually overlap.
|
||||
fn conflict_key(&self, first_layer: u32, num_layers: u32, first_mipmap: u32, num_mipmaps: u32)
|
||||
-> u64;
|
||||
|
||||
/// Returns true if the image can be given access on the given queue.
|
||||
///
|
||||
/// This function implementation should remember that it has been called and return `false` if
|
||||
/// it gets called a second time.
|
||||
///
|
||||
/// The only way to know that the GPU has stopped accessing a queue is when the image object
|
||||
/// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar
|
||||
/// to a lock) in order to represent a GPU access.
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool;
|
||||
}
|
||||
|
||||
unsafe impl<T> Image for T where T: SafeDeref, T::Target: Image {
|
||||
@ -149,6 +160,11 @@ unsafe impl<T> Image for T where T: SafeDeref, T::Target: Image {
|
||||
{
|
||||
(**self).conflict_key(first_layer, num_layers, first_mipmap, num_mipmaps)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_access(&self, exclusive_access: bool, queue: &Queue) -> bool {
|
||||
(**self).gpu_access(exclusive_access, queue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for images. Checks whether the value `T` can be used as a clear value for the
|
||||
|
@ -125,12 +125,14 @@
|
||||
//! section), you can draw on it. This is done in three steps:
|
||||
//!
|
||||
//! - Call `Swapchain::acquire_next_image`. This function will return the index of the image
|
||||
//! (within the list returned by `Swapchain::new`) that is available to draw.
|
||||
//! (within the list returned by `Swapchain::new`) that is available to draw, plus a future
|
||||
//! representing the moment when the GPU will gain access to that image.
|
||||
//! - Draw on that image just like you would draw to any other image (see the documentation of
|
||||
//! the `pipeline` module).
|
||||
//! - Call `Swapchain::present` with the same index in order to tell the implementation that you
|
||||
//! are finished drawing to the image and that it can queue a command to present the image on
|
||||
//! the screen after the draw operations are finished.
|
||||
//! the `pipeline` module). You need to chain the draw after the future that was returned by
|
||||
//! `acquire_next_image`.
|
||||
//! - Call `Swapchain::present` with the same index and by chaining the futures, in order to tell
|
||||
//! the implementation that you are finished drawing to the image and that it can queue a
|
||||
//! command to present the image on the screen after the draw operations are finished.
|
||||
//!
|
||||
//! TODO: add example here
|
||||
//! loop {
|
||||
@ -155,7 +157,7 @@
|
||||
//! ```no_run
|
||||
//! # use std::time::Duration;
|
||||
//! use vulkano::swapchain::AcquireError;
|
||||
//! use vulkano::swapchain::PresentError;
|
||||
//! use vulkano::sync::GpuFuture;
|
||||
//!
|
||||
//! // let mut swapchain = Swapchain::new(...);
|
||||
//! # let mut swapchain: (::std::sync::Arc<::vulkano::swapchain::Swapchain>, _) = unsafe { ::std::mem::uninitialized() };
|
||||
@ -170,19 +172,20 @@
|
||||
//!
|
||||
//! let (ref swapchain, ref _images) = swapchain;
|
||||
//!
|
||||
//! let index = match swapchain.acquire_next_image(Duration::from_millis(500)) {
|
||||
//! Ok(img) => img,
|
||||
//! let (index, acq_future) = match swapchain.acquire_next_image(Duration::from_millis(500)) {
|
||||
//! Ok(r) => r,
|
||||
//! Err(AcquireError::OutOfDate) => { recreate_swapchain = true; continue; },
|
||||
//! Err(err) => panic!("{:?}", err)
|
||||
//! };
|
||||
//!
|
||||
//! // ...
|
||||
//!
|
||||
//! match swapchain.present(&queue, index) {
|
||||
//! Ok(()) => (),
|
||||
//! Err(PresentError::OutOfDate) => { recreate_swapchain = true; },
|
||||
//! Err(err) => panic!("{:?}", err),
|
||||
//! }
|
||||
//! let final_future = acq_future
|
||||
//! // .then_execute(...)
|
||||
//! .then_swapchain_present(queue.clone(), swapchain.clone(), index)
|
||||
//! .then_signal_fence();
|
||||
//!
|
||||
//! final_future.flush().unwrap(); // TODO: PresentError?
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@ -201,9 +204,10 @@ pub use self::surface::SupportedCompositeAlpha;
|
||||
pub use self::surface::SupportedCompositeAlphaIter;
|
||||
pub use self::surface::ColorSpace;
|
||||
pub use self::surface::SurfaceCreationError;
|
||||
pub use self::swapchain::Swapchain;
|
||||
pub use self::swapchain::AcquireError;
|
||||
pub use self::swapchain::PresentError;
|
||||
pub use self::swapchain::PresentFuture;
|
||||
pub use self::swapchain::Swapchain;
|
||||
pub use self::swapchain::SwapchainAcquireFuture;
|
||||
|
||||
pub mod display;
|
||||
mod surface;
|
||||
|
@ -13,13 +13,22 @@ use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use buffer::Buffer;
|
||||
use command_buffer::submit::SubmitAnyBuilder;
|
||||
use command_buffer::submit::SubmitCommandBufferBuilder;
|
||||
use command_buffer::submit::SubmitPresentBuilder;
|
||||
use command_buffer::submit::SubmitSemaphoresWaitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use format::Format;
|
||||
use format::FormatDesc;
|
||||
use image::Image;
|
||||
use image::ImageDimensions;
|
||||
use image::sys::UnsafeImage;
|
||||
use image::sys::Usage as ImageUsage;
|
||||
@ -30,6 +39,8 @@ use swapchain::PresentMode;
|
||||
use swapchain::Surface;
|
||||
use swapchain::SurfaceTransform;
|
||||
use swapchain::SurfaceSwapchainLock;
|
||||
use sync::Fence;
|
||||
use sync::GpuFuture;
|
||||
use sync::Semaphore;
|
||||
use sync::SharingMode;
|
||||
|
||||
@ -49,15 +60,6 @@ pub struct Swapchain {
|
||||
surface: Arc<Surface>,
|
||||
swapchain: vk::SwapchainKHR,
|
||||
|
||||
/// Pool of semaphores from which a semaphore is retrieved when acquiring an image.
|
||||
///
|
||||
/// We need to use a queue so that we don't use the same semaphore twice in a row. The length
|
||||
/// of the queue is strictly superior to the number of images, in case the driver lets us
|
||||
/// acquire an image before it is presented.
|
||||
semaphores_pool: Mutex<Vec<Arc<Semaphore>>>,
|
||||
|
||||
images_semaphores: Mutex<Vec<Option<Arc<Semaphore>>>>,
|
||||
|
||||
// If true, that means we have used this swapchain to recreate a new swapchain. The current
|
||||
// swapchain can no longer be used for anything except presenting already-acquired images.
|
||||
//
|
||||
@ -77,6 +79,9 @@ pub struct Swapchain {
|
||||
alpha: CompositeAlpha,
|
||||
mode: PresentMode,
|
||||
clipped: bool,
|
||||
|
||||
// TODO: meh for Mutex
|
||||
images: Mutex<Vec<Weak<SwapchainImage>>>,
|
||||
}
|
||||
|
||||
impl Swapchain {
|
||||
@ -214,8 +219,6 @@ impl Swapchain {
|
||||
device: device.clone(),
|
||||
surface: surface.clone(),
|
||||
swapchain: swapchain,
|
||||
semaphores_pool: Mutex::new(Vec::new()),
|
||||
images_semaphores: Mutex::new(Vec::new()),
|
||||
stale: Mutex::new(false),
|
||||
num_images: num_images,
|
||||
format: format,
|
||||
@ -228,6 +231,7 @@ impl Swapchain {
|
||||
alpha: alpha,
|
||||
mode: mode,
|
||||
clipped: clipped,
|
||||
images: Mutex::new(Vec::new()), // Filled below.
|
||||
});
|
||||
|
||||
let images = unsafe {
|
||||
@ -250,30 +254,22 @@ impl Swapchain {
|
||||
SwapchainImage::from_raw(unsafe_image, format, &swapchain, id as u32).unwrap() // TODO: propagate error
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
{
|
||||
let mut semaphores = swapchain.images_semaphores.lock().unwrap();
|
||||
for _ in 0 .. images.len() {
|
||||
semaphores.push(None);
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0 .. images.len() + 1 {
|
||||
// TODO: check if this change is okay (maybe the Arc can be omitted?) - Mixthos
|
||||
//swapchain.semaphores_pool.push(try!(Semaphore::new(device.clone())));
|
||||
swapchain.semaphores_pool.lock().unwrap().push(Arc::new(try!(Semaphore::raw(device.clone()))));
|
||||
}
|
||||
|
||||
*swapchain.images.lock().unwrap() = images.iter().map(|i| Arc::downgrade(i)).collect();
|
||||
Ok((swapchain, images))
|
||||
}
|
||||
|
||||
/// Tries to take ownership of an image in order to draw on it.
|
||||
///
|
||||
/// The function returns the index of the image in the array of images that was returned
|
||||
/// when creating the swapchain.
|
||||
/// when creating the swapchain, plus a future that represents the moment when the image will
|
||||
/// become available from the GPU (which may not be *immediately*).
|
||||
///
|
||||
/// If you try to draw on an image without acquiring it first, the execution will block. (TODO
|
||||
/// behavior may change).
|
||||
pub fn acquire_next_image(&self, timeout: Duration) -> Result<usize, AcquireError> {
|
||||
// TODO: has to make sure vkQueuePresent is called, because calling acquire_next_image many
|
||||
// times in a row is an error
|
||||
// TODO: swapchain must not have been replaced by being passed as the VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR
|
||||
pub fn acquire_next_image(&self, timeout: Duration) -> Result<(usize, SwapchainAcquireFuture), AcquireError> {
|
||||
unsafe {
|
||||
let stale = self.stale.lock().unwrap();
|
||||
if *stale {
|
||||
@ -282,7 +278,7 @@ impl Swapchain {
|
||||
|
||||
let vk = self.device.pointers();
|
||||
|
||||
let semaphore = self.semaphores_pool.lock().unwrap().remove(0);
|
||||
let semaphore = try!(Semaphore::new(self.device.clone()));
|
||||
|
||||
let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
|
||||
.saturating_add(timeout.subsec_nanos() as u64);
|
||||
@ -290,7 +286,7 @@ impl Swapchain {
|
||||
let mut out = mem::uninitialized();
|
||||
let r = try!(check_errors(vk.AcquireNextImageKHR(self.device.internal_object(),
|
||||
self.swapchain, timeout_ns,
|
||||
semaphore.internal_object(), 0, // TODO: timeout
|
||||
semaphore.internal_object(), 0,
|
||||
&mut out)));
|
||||
|
||||
let id = match r {
|
||||
@ -301,10 +297,12 @@ impl Swapchain {
|
||||
s => panic!("unexpected success value: {:?}", s)
|
||||
};
|
||||
|
||||
let mut images_semaphores = self.images_semaphores.lock().unwrap();
|
||||
images_semaphores[id] = Some(semaphore);
|
||||
|
||||
Ok(id)
|
||||
Ok((id, SwapchainAcquireFuture {
|
||||
semaphore: semaphore,
|
||||
id: id,
|
||||
image: self.images.lock().unwrap().get(id).unwrap().clone(),
|
||||
finished: AtomicBool::new(false),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,40 +313,26 @@ impl Swapchain {
|
||||
///
|
||||
/// The actual behavior depends on the present mode that you passed when creating the
|
||||
/// swapchain.
|
||||
pub fn present(&self, queue: &Arc<Queue>, index: usize) -> Result<(), PresentError> {
|
||||
let vk = self.device.pointers();
|
||||
// TODO: use another API, since taking by Arc is meh
|
||||
pub fn present<F>(me: Arc<Self>, before: F, queue: Arc<Queue>, index: usize)
|
||||
-> PresentFuture<F>
|
||||
where F: GpuFuture
|
||||
{
|
||||
assert!(index < me.num_images as usize);
|
||||
|
||||
let wait_semaphore = {
|
||||
let mut images_semaphores = self.images_semaphores.lock().unwrap();
|
||||
images_semaphores[index].take().expect("Trying to present an image that was \
|
||||
not acquired")
|
||||
};
|
||||
let swapchain_image = me.images.lock().unwrap().get(index).unwrap().upgrade().unwrap(); // TODO: return error instead
|
||||
// Normally if `check_image_access` returns false we're supposed to call the `gpu_access`
|
||||
// function on the image instead. But since we know that this method on `SwapchainImage`
|
||||
// always returns false anyway (by design), we don't need to do it.
|
||||
assert!(before.check_image_access(&swapchain_image, true, &queue)); // TODO: return error isntead
|
||||
|
||||
// FIXME: the semaphore may be destroyed ; need to return it
|
||||
|
||||
unsafe {
|
||||
let mut result = mem::uninitialized();
|
||||
|
||||
let queue = queue.internal_object_guard();
|
||||
let index = index as u32;
|
||||
|
||||
let infos = vk::PresentInfoKHR {
|
||||
sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
pNext: ptr::null(),
|
||||
waitSemaphoreCount: 1,
|
||||
pWaitSemaphores: &wait_semaphore.internal_object(),
|
||||
swapchainCount: 1,
|
||||
pSwapchains: &self.swapchain,
|
||||
pImageIndices: &index,
|
||||
pResults: &mut result,
|
||||
};
|
||||
|
||||
try!(check_errors(vk.QueuePresentKHR(*queue, &infos)));
|
||||
//try!(check_errors(result)); // TODO: AMD driver doesn't seem to write the result
|
||||
PresentFuture {
|
||||
previous: before,
|
||||
queue: queue,
|
||||
swapchain: me,
|
||||
image_id: index as u32,
|
||||
finished: AtomicBool::new(false),
|
||||
}
|
||||
|
||||
self.semaphores_pool.lock().unwrap().push(wait_semaphore);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the number of images of the swapchain.
|
||||
@ -414,23 +398,14 @@ impl Swapchain {
|
||||
pub fn clipped(&self) -> bool {
|
||||
self.clipped
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VulkanObject for Swapchain {
|
||||
type Object = vk::SwapchainKHR;
|
||||
|
||||
/*/// Returns the semaphore that is going to be signalled when the image is going to be ready
|
||||
/// to be drawn upon.
|
||||
///
|
||||
/// Returns `None` if the image was not acquired first, or was already presented.
|
||||
// TODO: racy, as someone could present the image before using the semaphore
|
||||
#[inline]
|
||||
pub fn image_semaphore(&self, id: u32) -> Option<Arc<Semaphore>> {
|
||||
let semaphores = self.images_semaphores.lock().unwrap();
|
||||
semaphores[id as usize].as_ref().map(|s| s.clone())
|
||||
}*/
|
||||
// TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn image_semaphore(&self, id: u32, semaphore: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
|
||||
let mut semaphores = self.images_semaphores.lock().unwrap();
|
||||
mem::replace(&mut semaphores[id as usize], Some(semaphore))
|
||||
fn internal_object(&self) -> vk::SwapchainKHR {
|
||||
self.swapchain
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,6 +420,93 @@ impl Drop for Swapchain {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the moment when the GPU will have access to a swapchain image.
|
||||
#[must_use]
|
||||
pub struct SwapchainAcquireFuture {
|
||||
semaphore: Semaphore,
|
||||
id: usize,
|
||||
image: Weak<SwapchainImage>,
|
||||
finished: AtomicBool,
|
||||
}
|
||||
|
||||
impl SwapchainAcquireFuture {
|
||||
/// Returns the index of the image in the list of images returned when creating the swapchain.
|
||||
#[inline]
|
||||
pub fn image_id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GpuFuture for SwapchainAcquireFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
self.finished.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<error::Error>> {
|
||||
let mut sem = SubmitSemaphoresWaitBuilder::new();
|
||||
sem.add_wait_semaphore(&self.semaphore);
|
||||
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
self.finished.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
if let Some(sc_img) = self.image.upgrade() {
|
||||
sc_img.inner().internal_object() == image.inner().internal_object()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl DeviceOwned for SwapchainAcquireFuture {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.semaphore.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SwapchainAcquireFuture {
|
||||
fn drop(&mut self) {
|
||||
if !*self.finished.get_mut() {
|
||||
panic!() // FIXME: what to do?
|
||||
/*// TODO: handle errors?
|
||||
let fence = Fence::new(self.device().clone()).unwrap();
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
builder.add_wait_semaphore(&self.semaphore);
|
||||
builder.set_signal_fence(&fence);
|
||||
builder.submit(... which queue ? ...).unwrap();
|
||||
fence.wait(Duration::from_secs(600)).unwrap();*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when calling `acquire_next_image`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
@ -494,6 +556,13 @@ impl fmt::Display for AcquireError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OomError> for AcquireError {
|
||||
#[inline]
|
||||
fn from(err: OomError) -> AcquireError {
|
||||
AcquireError::OomError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for AcquireError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> AcquireError {
|
||||
@ -508,61 +577,110 @@ impl From<Error> for AcquireError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when calling `acquire_next_image`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum PresentError {
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// The connection to the device has been lost.
|
||||
DeviceLost,
|
||||
|
||||
/// The surface is no longer accessible and must be recreated.
|
||||
SurfaceLost,
|
||||
|
||||
/// The surface has changed in a way that makes the swapchain unusable. You must query the
|
||||
/// surface's new properties and recreate a new swapchain if you want to continue drawing.
|
||||
OutOfDate,
|
||||
/// Represents a swapchain image being presented on the screen.
|
||||
#[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: Arc<Swapchain>,
|
||||
image_id: u32,
|
||||
finished: AtomicBool,
|
||||
}
|
||||
|
||||
impl error::Error for PresentError {
|
||||
unsafe impl<P> GpuFuture for PresentFuture<P> where P: GpuFuture {
|
||||
#[inline]
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PresentError::OomError(_) => "not enough memory",
|
||||
PresentError::DeviceLost => "the connection to the device has been lost",
|
||||
PresentError::SurfaceLost => "the surface of this swapchain is no longer valid",
|
||||
PresentError::OutOfDate => "the swapchain needs to be recreated",
|
||||
}
|
||||
fn is_finished(&self) -> bool {
|
||||
self.finished.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
PresentError::OomError(ref err) => Some(err),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PresentError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for PresentError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> PresentError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => PresentError::OomError(OomError::from(err)),
|
||||
err @ Error::OutOfDeviceMemory => PresentError::OomError(OomError::from(err)),
|
||||
Error::DeviceLost => PresentError::DeviceLost,
|
||||
Error::SurfaceLost => PresentError::SurfaceLost,
|
||||
Error::OutOfDate => PresentError::OutOfDate,
|
||||
_ => panic!("unexpected error: {:?}", err)
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<error::Error>> {
|
||||
let queue = self.previous.queue().map(|q| q.clone());
|
||||
|
||||
// TODO: if the swapchain image layout is not PRESENT, should add a transition command
|
||||
// buffer
|
||||
|
||||
Ok(match try!(self.previous.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::SemaphoresWait(sem) => {
|
||||
let mut builder: SubmitPresentBuilder = sem.into();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::CommandBuffer(mut cb) => {
|
||||
try!(cb.submit(&queue.unwrap())); // FIXME: wrong because build_submission can be called multiple times
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
builder.add_swapchain(&self.swapchain, self.image_id);
|
||||
SubmitAnyBuilder::QueuePresent(builder)
|
||||
},
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
unimplemented!() // TODO:
|
||||
/*present.submit();
|
||||
let mut builder = SubmitPresentBuilder::new();
|
||||
builder.add_swapchain(self.command_buffer.inner(), self.image_id);
|
||||
SubmitAnyBuilder::CommandBuffer(builder)*/
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<error::Error>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
self.finished.store(true, Ordering::SeqCst);
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
debug_assert!(match self.previous.queue() {
|
||||
None => true,
|
||||
Some(q) => q.is_same(&self.queue)
|
||||
});
|
||||
|
||||
Some(&self.queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
unimplemented!() // TODO: VK specs don't say whether it is legal to do that
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
unimplemented!() // TODO: VK specs don't say whether it is legal to do that
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<P> DeviceOwned for PresentFuture<P> where P: GpuFuture {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.queue.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Drop for PresentFuture<P> where P: GpuFuture {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !*self.finished.get_mut() {
|
||||
// TODO: handle errors?
|
||||
self.flush().unwrap();
|
||||
// Block until the queue finished.
|
||||
self.queue().unwrap().wait().unwrap();
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,40 +46,18 @@ pub struct Fence<D = Arc<Device>> where D: SafeDeref<Target = Device> {
|
||||
}
|
||||
|
||||
impl<D> Fence<D> where D: SafeDeref<Target = Device> {
|
||||
/// See the docs of new().
|
||||
#[inline]
|
||||
pub fn raw(device: D) -> Result<Fence<D>, OomError> {
|
||||
Fence::new_impl(device, false)
|
||||
}
|
||||
|
||||
/// Builds a new fence.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: D) -> Arc<Fence<D>> {
|
||||
Arc::new(Fence::raw(device).unwrap())
|
||||
pub fn new(device: D) -> Result<Fence<D>, OomError> {
|
||||
Fence::new_impl(device, false)
|
||||
}
|
||||
|
||||
/// See the docs of signaled().
|
||||
#[inline]
|
||||
pub fn signaled_raw(device: D) -> Result<Fence<D>, OomError> {
|
||||
pub fn signaled(device: D) -> Result<Fence<D>, OomError> {
|
||||
Fence::new_impl(device, true)
|
||||
}
|
||||
|
||||
/// Builds a new fence already in the "signaled" state.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn signaled(device: D) -> Arc<Fence<D>> {
|
||||
Arc::new(Fence::signaled_raw(device).unwrap())
|
||||
}
|
||||
|
||||
fn new_impl(device: D, signaled: bool) -> Result<Fence<D>, OomError> {
|
||||
let fence = unsafe {
|
||||
let infos = vk::FenceCreateInfo {
|
||||
@ -317,7 +295,6 @@ impl From<Error> for FenceWaitError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use sync::Fence;
|
||||
|
||||
@ -325,7 +302,7 @@ mod tests {
|
||||
fn fence_create() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let fence = Fence::new(device.clone());
|
||||
let fence = Fence::new(device.clone()).unwrap();
|
||||
assert!(!fence.ready().unwrap());
|
||||
}
|
||||
|
||||
@ -333,7 +310,7 @@ mod tests {
|
||||
fn fence_create_signaled() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let fence = Fence::signaled(device.clone());
|
||||
let fence = Fence::signaled(device.clone()).unwrap();
|
||||
assert!(fence.ready().unwrap());
|
||||
}
|
||||
|
||||
@ -341,7 +318,7 @@ mod tests {
|
||||
fn fence_signaled_wait() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let fence = Fence::signaled(device.clone());
|
||||
let fence = Fence::signaled(device.clone()).unwrap();
|
||||
fence.wait(Duration::new(0, 10)).unwrap();
|
||||
}
|
||||
|
||||
@ -349,8 +326,8 @@ mod tests {
|
||||
fn fence_reset() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let mut fence = Fence::signaled(device.clone());
|
||||
Arc::get_mut(&mut fence).unwrap().reset();
|
||||
let mut fence = Fence::signaled(device.clone()).unwrap();
|
||||
fence.reset();
|
||||
assert!(!fence.ready().unwrap());
|
||||
}
|
||||
|
||||
@ -360,22 +337,23 @@ mod tests {
|
||||
let (device1, _) = gfx_dev_and_queue!();
|
||||
let (device2, _) = gfx_dev_and_queue!();
|
||||
|
||||
let fence1 = Fence::signaled(device1.clone());
|
||||
let fence2 = Fence::signaled(device2.clone());
|
||||
let fence1 = Fence::signaled(device1.clone()).unwrap();
|
||||
let fence2 = Fence::signaled(device2.clone()).unwrap();
|
||||
|
||||
let _ = Fence::multi_wait([&*fence1, &*fence2].iter().cloned(), Duration::new(0, 10));
|
||||
let _ = Fence::multi_wait([&fence1, &fence2].iter().cloned(), Duration::new(0, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Tried to reset multiple fences that didn't belong to the same device")]
|
||||
fn multireset_different_devices() {
|
||||
use std::iter::once;
|
||||
|
||||
let (device1, _) = gfx_dev_and_queue!();
|
||||
let (device2, _) = gfx_dev_and_queue!();
|
||||
|
||||
let mut fence1 = Fence::signaled(device1.clone());
|
||||
let mut fence2 = Fence::signaled(device2.clone());
|
||||
let mut fence1 = Fence::signaled(device1.clone()).unwrap();
|
||||
let mut fence2 = Fence::signaled(device2.clone()).unwrap();
|
||||
|
||||
let _ = Fence::multi_reset(Some(Arc::get_mut(&mut fence1).unwrap()).into_iter()
|
||||
.chain(Some(Arc::get_mut(&mut fence2).unwrap()).into_iter()));
|
||||
let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2)));
|
||||
}
|
||||
}
|
||||
|
611
vulkano/src/sync/future.rs
Normal file
611
vulkano/src/sync/future.rs
Normal file
@ -0,0 +1,611 @@
|
||||
// Copyright (c) 2016 The vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or http://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 std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use buffer::Buffer;
|
||||
use command_buffer::CommandBuffer;
|
||||
use command_buffer::CommandBufferExecFuture;
|
||||
use command_buffer::submit::SubmitAnyBuilder;
|
||||
use command_buffer::submit::SubmitCommandBufferBuilder;
|
||||
use command_buffer::submit::SubmitSemaphoresWaitBuilder;
|
||||
use device::Device;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
use image::Image;
|
||||
use swapchain::Swapchain;
|
||||
use swapchain::PresentFuture;
|
||||
use sync::Fence;
|
||||
use sync::FenceWaitError;
|
||||
use sync::Semaphore;
|
||||
|
||||
use SafeDeref;
|
||||
use VulkanObject;
|
||||
|
||||
/// Represents an event that will happen on the GPU in the future.
|
||||
pub unsafe trait GpuFuture: DeviceOwned {
|
||||
/// Returns `true` if the event happened on the GPU.
|
||||
///
|
||||
/// If this returns `false`, then the destuctor of this future will block until it is the case.
|
||||
///
|
||||
/// If you didn't call `flush()` yet, then this function will return `false`.
|
||||
// TODO: what if user submits a cb without fence, calls flush, and then calls is_finished()
|
||||
// expecting it to return true eventually?
|
||||
fn is_finished(&self) -> bool;
|
||||
|
||||
/// Builds a submission that, if submitted, makes sure that the event represented by this
|
||||
/// `GpuFuture` will happen, and possibly contains extra elements (eg. a semaphore wait or an
|
||||
/// event wait) that makes the dependency with subsequent operations work.
|
||||
///
|
||||
/// It is the responsibility of the caller to ensure that the submission is going to be
|
||||
/// submitted only once. However keep in mind that this function can perfectly be called
|
||||
/// multiple times (as long as the returned object is only submitted once).
|
||||
///
|
||||
/// Once the caller has submitted the submission and has determined that the GPU has finished
|
||||
/// executing it, it should call `signal_finished`. Failure to do so will incur a large runtime
|
||||
/// overhead, as the future will have to block to make sure that it is finished.
|
||||
// TODO: better error type
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>>;
|
||||
|
||||
/// Flushes the future and submits to the GPU the actions that will permit this future to
|
||||
/// occur.
|
||||
///
|
||||
/// The implementation must remember that it was flushed. If the function is called multiple
|
||||
/// times, only the first time must result in a flush.
|
||||
// TODO: better error type
|
||||
fn flush(&self) -> Result<(), Box<Error>>;
|
||||
|
||||
/// Sets the future to its "complete" state, meaning that it can safely be destroyed.
|
||||
///
|
||||
/// This must only be done if you called `build_submission()`, submitted the returned
|
||||
/// submission, and determined that it was finished.
|
||||
unsafe fn signal_finished(&self);
|
||||
|
||||
/// Returns the queue that triggers the event. Returns `None` if unknown or irrelevant.
|
||||
///
|
||||
/// If this function returns `None` and `queue_change_allowed` returns `false`, then a panic
|
||||
/// is likely to occur if you use this future. This is only a problem if you implement
|
||||
/// the `GpuFuture` trait yourself for a type outside of vulkano.
|
||||
fn queue(&self) -> Option<&Arc<Queue>>;
|
||||
|
||||
/// Returns `true` if elements submitted after this future can be submitted to a different
|
||||
/// queue than the other returned by `queue()`.
|
||||
fn queue_change_allowed(&self) -> bool;
|
||||
|
||||
/// Checks whether submitting something after this future grants access (exclusive or shared,
|
||||
/// depending on the parameter) to the given buffer on the given queue.
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool;
|
||||
|
||||
/// Checks whether submitting something after this future grants access (exclusive or shared,
|
||||
/// depending on the parameter) to the given image on the given queue.
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool;
|
||||
|
||||
/// Joins this future with another one, representing the moment when both events have happened.
|
||||
fn join<F>(self, other: F) -> JoinFuture<Self, F>
|
||||
where Self: Sized, F: GpuFuture
|
||||
{
|
||||
assert_eq!(self.device().internal_object(), other.device().internal_object());
|
||||
|
||||
if !self.queue_change_allowed() && !other.queue_change_allowed() {
|
||||
assert!(self.queue().unwrap().is_same(other.queue().unwrap()));
|
||||
}
|
||||
|
||||
JoinFuture {
|
||||
first: self,
|
||||
second: other,
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a command buffer after this future.
|
||||
#[inline]
|
||||
fn then_execute<Cb>(self, queue: Arc<Queue>, command_buffer: Cb)
|
||||
-> CommandBufferExecFuture<Self, Cb>
|
||||
where Self: Sized, Cb: CommandBuffer
|
||||
{
|
||||
command_buffer.execute_after(self, queue)
|
||||
}
|
||||
|
||||
/// Executes a command buffer after this future, on the same queue as the future.
|
||||
#[inline]
|
||||
fn then_execute_same_queue<Cb>(self, command_buffer: Cb) -> CommandBufferExecFuture<Self, Cb>
|
||||
where Self: Sized, Cb: CommandBuffer
|
||||
{
|
||||
let queue = self.queue().unwrap().clone();
|
||||
command_buffer.execute_after(self, queue)
|
||||
}
|
||||
|
||||
/// Signals a semaphore after this future. Returns another future that represents the signal.
|
||||
#[inline]
|
||||
fn then_signal_semaphore(self) -> SemaphoreSignalFuture<Self> where Self: Sized {
|
||||
let device = self.device().clone();
|
||||
|
||||
assert!(self.queue().is_some()); // TODO: document
|
||||
|
||||
SemaphoreSignalFuture {
|
||||
previous: self,
|
||||
semaphore: Semaphore::new(device).unwrap(),
|
||||
wait_submitted: Mutex::new(false),
|
||||
finished: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Signals a fence after this future. Returns another future that represents the signal.
|
||||
#[inline]
|
||||
fn then_signal_fence(self) -> FenceSignalFuture<Self> where Self: Sized {
|
||||
let device = self.device().clone();
|
||||
|
||||
assert!(self.queue().is_some()); // TODO: document
|
||||
|
||||
FenceSignalFuture {
|
||||
previous: self,
|
||||
fence: Fence::new(device).unwrap(),
|
||||
flushed: Mutex::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Presents a swapchain image after this future.
|
||||
///
|
||||
/// You should only ever do this indirectly after a `SwapchainAcquireFuture` of the same image,
|
||||
/// otherwise an error will occur when flushing.
|
||||
///
|
||||
/// > **Note**: This is just a shortcut for the `Swapchain::present()` function.
|
||||
#[inline]
|
||||
fn then_swapchain_present(self, queue: Arc<Queue>, swapchain: Arc<Swapchain>,
|
||||
image_index: usize) -> PresentFuture<Self>
|
||||
where Self: Sized
|
||||
{
|
||||
Swapchain::present(swapchain, self, queue, image_index)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> GpuFuture for T where T: SafeDeref, T::Target: GpuFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
(**self).is_finished()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>> {
|
||||
(**self).build_submission()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<Error>> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
(**self).signal_finished()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
(**self).queue_change_allowed()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
(**self).queue()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
(**self).check_buffer_access(buffer, exclusive, queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
(**self).check_image_access(image, exclusive, queue)
|
||||
}
|
||||
}
|
||||
|
||||
/// A dummy future that represents "now".
|
||||
#[must_use]
|
||||
pub struct DummyFuture {
|
||||
device: Arc<Device>,
|
||||
}
|
||||
|
||||
impl DummyFuture {
|
||||
/// Builds a new dummy future.
|
||||
#[inline]
|
||||
pub fn new(device: Arc<Device>) -> DummyFuture {
|
||||
DummyFuture {
|
||||
device: device,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GpuFuture for DummyFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>> {
|
||||
Ok(SubmitAnyBuilder::Empty)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl DeviceOwned for DummyFuture {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
&self.device
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a semaphore being signaled after a previous event.
|
||||
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
|
||||
pub struct SemaphoreSignalFuture<F> where F: GpuFuture {
|
||||
previous: F,
|
||||
semaphore: Semaphore,
|
||||
// True if the signaling command has already been submitted.
|
||||
// If flush is called multiple times, we want to block so that only one flushing is executed.
|
||||
// Therefore we use a `Mutex<bool>` and not an `AtomicBool`.
|
||||
wait_submitted: Mutex<bool>,
|
||||
finished: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl<F> GpuFuture for SemaphoreSignalFuture<F> where F: GpuFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
self.finished.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>> {
|
||||
// Flushing the signaling part, since it must always be submitted before the waiting part.
|
||||
try!(self.flush());
|
||||
|
||||
let mut sem = SubmitSemaphoresWaitBuilder::new();
|
||||
sem.add_wait_semaphore(&self.semaphore);
|
||||
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<(), Box<Error>> {
|
||||
unsafe {
|
||||
let mut wait_submitted = self.wait_submitted.lock().unwrap();
|
||||
|
||||
if *wait_submitted {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let queue = self.previous.queue().unwrap().clone();
|
||||
|
||||
match try!(self.previous.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
builder.add_signal_semaphore(&self.semaphore);
|
||||
try!(builder.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::SemaphoresWait(sem) => {
|
||||
let mut builder: SubmitCommandBufferBuilder = sem.into();
|
||||
builder.add_signal_semaphore(&self.semaphore);
|
||||
try!(builder.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::CommandBuffer(mut builder) => {
|
||||
debug_assert_eq!(builder.num_signal_semaphores(), 0);
|
||||
builder.add_signal_semaphore(&self.semaphore);
|
||||
try!(builder.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
try!(present.submit(&queue));
|
||||
let mut builder = SubmitCommandBufferBuilder::new();
|
||||
builder.add_signal_semaphore(&self.semaphore);
|
||||
try!(builder.submit(&queue)); // FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice
|
||||
},
|
||||
};
|
||||
|
||||
// Only write `true` here in order to try again next time if an error occurs.
|
||||
*wait_submitted = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
debug_assert!(*self.wait_submitted.lock().unwrap());
|
||||
self.finished.store(true, Ordering::SeqCst);
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
self.previous.queue()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
self.previous.check_buffer_access(buffer, exclusive, queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
self.previous.check_image_access(image, exclusive, queue)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F> DeviceOwned for SemaphoreSignalFuture<F> where F: GpuFuture {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.semaphore.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for SemaphoreSignalFuture<F> where F: GpuFuture {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !*self.finished.get_mut() {
|
||||
// TODO: handle errors?
|
||||
self.flush().unwrap();
|
||||
// Block until the queue finished.
|
||||
self.queue().unwrap().wait().unwrap();
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a fence being signaled after a previous event.
|
||||
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
|
||||
pub struct FenceSignalFuture<F> where F: GpuFuture {
|
||||
previous: F,
|
||||
fence: Fence,
|
||||
// True if the signaling command has already been submitted.
|
||||
// If flush is called multiple times, we want to block so that only one flushing is executed.
|
||||
// Therefore we use a `Mutex<bool>` and not an `AtomicBool`.
|
||||
flushed: Mutex<bool>,
|
||||
}
|
||||
|
||||
impl<F> FenceSignalFuture<F> where F: GpuFuture {
|
||||
/// Waits until the fence is signaled, or at least until the number of nanoseconds of the
|
||||
/// timeout has elapsed.
|
||||
pub fn wait(&self, timeout: Duration) -> Result<(), FenceWaitError> {
|
||||
// FIXME: flush?
|
||||
self.fence.wait(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F> GpuFuture for FenceSignalFuture<F> where F: GpuFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
if !*self.flushed.lock().unwrap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.fence.wait(Duration::from_secs(0)).is_ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>> {
|
||||
try!(self.flush());
|
||||
self.fence.wait(Duration::from_secs(600)).unwrap(); // TODO: handle errors
|
||||
Ok(SubmitAnyBuilder::Empty)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<Error>> {
|
||||
unsafe {
|
||||
let mut flushed = self.flushed.lock().unwrap();
|
||||
|
||||
if *flushed {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let queue = self.previous.queue().unwrap().clone();
|
||||
|
||||
match try!(self.previous.build_submission()) {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
let mut b = SubmitCommandBufferBuilder::new();
|
||||
b.set_fence_signal(&self.fence);
|
||||
try!(b.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::SemaphoresWait(sem) => {
|
||||
let b: SubmitCommandBufferBuilder = sem.into();
|
||||
debug_assert!(!b.has_fence());
|
||||
try!(b.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::CommandBuffer(mut cb_builder) => {
|
||||
debug_assert!(!cb_builder.has_fence());
|
||||
cb_builder.set_fence_signal(&self.fence);
|
||||
try!(cb_builder.submit(&queue));
|
||||
},
|
||||
SubmitAnyBuilder::QueuePresent(present) => {
|
||||
try!(present.submit(&queue));
|
||||
let mut b = SubmitCommandBufferBuilder::new();
|
||||
b.set_fence_signal(&self.fence);
|
||||
try!(b.submit(&queue)); // FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice
|
||||
},
|
||||
};
|
||||
|
||||
// Only write `true` here in order to try again next time if an error occurs.
|
||||
*flushed = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn signal_finished(&self) {
|
||||
debug_assert!(*self.flushed.lock().unwrap());
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue_change_allowed(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn queue(&self) -> Option<&Arc<Queue>> {
|
||||
self.previous.queue()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_buffer_access(&self, buffer: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
self.previous.check_buffer_access(buffer, exclusive, queue)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
self.previous.check_image_access(image, exclusive, queue)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<F> DeviceOwned for FenceSignalFuture<F> where F: GpuFuture {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.fence.device()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for FenceSignalFuture<F> where F: GpuFuture {
|
||||
fn drop(&mut self) {
|
||||
self.flush().unwrap(); // TODO: handle error?
|
||||
self.fence.wait(Duration::from_secs(600)).unwrap(); // TODO: handle some errors
|
||||
|
||||
unsafe {
|
||||
self.previous.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
let device = self.first.device();
|
||||
debug_assert_eq!(self.second.device().internal_object(), device.internal_object());
|
||||
device
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A, B> GpuFuture for JoinFuture<A, B> where A: GpuFuture, B: GpuFuture {
|
||||
#[inline]
|
||||
fn is_finished(&self) -> bool {
|
||||
self.first.is_finished() && self.second.is_finished()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) -> Result<(), Box<Error>> {
|
||||
// Since each future remembers whether it has been flushed, there's no safety issue here
|
||||
// if we call this function multiple times.
|
||||
try!(self.first.flush());
|
||||
try!(self.second.flush());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Box<Error>> {
|
||||
let first = try!(self.first.build_submission());
|
||||
let second = try!(self.second.build_submission());
|
||||
|
||||
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)
|
||||
},
|
||||
_ => 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<&Arc<Queue>> {
|
||||
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: &Buffer, exclusive: bool, queue: &Queue) -> bool {
|
||||
let first = self.first.check_buffer_access(buffer, exclusive, queue);
|
||||
let second = self.second.check_buffer_access(buffer, exclusive, queue);
|
||||
debug_assert!(!exclusive || !(first && second), "Two futures gave exclusive access to the \
|
||||
same resource");
|
||||
first || second
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_image_access(&self, image: &Image, exclusive: bool, queue: &Queue) -> bool {
|
||||
let first = self.first.check_image_access(image, exclusive, queue);
|
||||
let second = self.second.check_image_access(image, exclusive, queue);
|
||||
debug_assert!(!exclusive || !(first && second), "Two futures gave exclusive access to the \
|
||||
same resource");
|
||||
first || second
|
||||
}
|
||||
}
|
@ -27,10 +27,16 @@ use vk;
|
||||
pub use self::event::Event;
|
||||
pub use self::fence::Fence;
|
||||
pub use self::fence::FenceWaitError;
|
||||
pub use self::future::DummyFuture;
|
||||
pub use self::future::GpuFuture;
|
||||
pub use self::future::SemaphoreSignalFuture;
|
||||
pub use self::future::FenceSignalFuture;
|
||||
pub use self::future::JoinFuture;
|
||||
pub use self::semaphore::Semaphore;
|
||||
|
||||
mod event;
|
||||
mod fence;
|
||||
mod future;
|
||||
mod semaphore;
|
||||
|
||||
/// Base trait for objects that can be used as resources and must be synchronized.
|
||||
|
@ -31,9 +31,9 @@ pub struct Semaphore<D = Arc<Device>> where D: SafeDeref<Target = Device> {
|
||||
}
|
||||
|
||||
impl<D> Semaphore<D> where D: SafeDeref<Target = Device> {
|
||||
/// See the docs of new().
|
||||
/// Builds a new semaphore.
|
||||
#[inline]
|
||||
pub fn raw(device: D) -> Result<Semaphore<D>, OomError> {
|
||||
pub fn new(device: D) -> Result<Semaphore<D>, OomError> {
|
||||
let semaphore = unsafe {
|
||||
// since the creation is constant, we use a `static` instead of a struct on the stack
|
||||
static mut INFOS: vk::SemaphoreCreateInfo = vk::SemaphoreCreateInfo {
|
||||
@ -54,17 +54,6 @@ impl<D> Semaphore<D> where D: SafeDeref<Target = Device> {
|
||||
semaphore: semaphore,
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a new semaphore.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// - Panics if the device or host ran out of memory.
|
||||
///
|
||||
#[inline]
|
||||
pub fn new(device: D) -> Arc<Semaphore<D>> {
|
||||
Arc::new(Semaphore::raw(device).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl DeviceOwned for Semaphore {
|
||||
|
Loading…
Reference in New Issue
Block a user