New command buffers system

This commit is contained in:
Pierre Krieger 2016-08-17 11:28:23 +02:00
parent 71b641594e
commit 94b22ab82f
10 changed files with 988 additions and 37 deletions

View File

@ -37,10 +37,17 @@ use buffer::sys::UnsafeBuffer;
use buffer::sys::Usage;
use buffer::traits::AccessRange;
use buffer::traits::Buffer;
use buffer::traits::CommandBufferState;
use buffer::traits::CommandListState;
use buffer::traits::GpuAccessResult;
use buffer::traits::SubmitInfos;
use buffer::traits::TrackedBuffer;
use buffer::traits::TypedBuffer;
use buffer::traits::PipelineBarrierRequest;
use buffer::traits::PipelineMemoryBarrierRequest;
use command_buffer::Submission;
use device::Device;
use device::Queue;
use instance::QueueFamily;
use memory::Content;
use memory::CpuAccess as MemCpuAccess;
@ -50,6 +57,9 @@ use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPool;
use sync::FenceWaitError;
use sync::Sharing;
use sync::Fence;
use sync::AccessFlagBits;
use sync::PipelineStages;
use OomError;
@ -392,6 +402,173 @@ unsafe impl<T: ?Sized, A> TypedBuffer for CpuAccessibleBuffer<T, A>
type Content = T;
}
unsafe impl<T: ?Sized, A> TrackedBuffer for CpuAccessibleBuffer<T, A>
where T: 'static + Send + Sync, A: MemoryPool
{
type CommandListState = CpuAccessibleBufferClState;
type FinishedState = CpuAccessibleBufferFinished;
#[inline]
fn initial_state(&self) -> Self::CommandListState {
// We don't know when the user is going to write to the buffer, so we just assume that it's
// all the time.
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,
}
}
}
pub struct CpuAccessibleBufferClState {
size: usize,
stages: PipelineStages,
access: AccessFlagBits,
first_stages: Option<PipelineStages>,
write: bool,
earliest_previous_transition: usize,
needs_flush_at_the_end: bool,
}
impl CommandListState for CpuAccessibleBufferClState {
type FinishedState = CpuAccessibleBufferFinished;
fn transition(self, num_command: usize, _: &UnsafeBuffer, _: usize, _: usize, write: bool,
stage: PipelineStages, access: AccessFlagBits)
-> (Self, Option<PipelineBarrierRequest>)
{
debug_assert!(!stage.host);
debug_assert!(!access.host_read);
debug_assert!(!access.host_write);
if write {
// Write after read or write after write.
let new_state = CpuAccessibleBufferClState {
size: self.size,
stages: stage,
access: access,
first_stages: Some(self.first_stages.clone().unwrap_or(stage)),
write: true,
earliest_previous_transition: num_command,
needs_flush_at_the_end: true,
};
let barrier = PipelineBarrierRequest {
after_command_num: self.earliest_previous_transition,
source_stage: self.stages,
destination_stages: stage,
by_region: true,
memory_barrier: if self.write {
Some(PipelineMemoryBarrierRequest {
offset: 0,
size: self.size,
source_access: self.access,
destination_access: access,
})
} else {
None
},
};
(new_state, Some(barrier))
} else if self.write {
// Read after write.
let new_state = CpuAccessibleBufferClState {
size: self.size,
stages: stage,
access: access,
first_stages: Some(self.first_stages.clone().unwrap_or(stage)),
write: false,
earliest_previous_transition: num_command,
needs_flush_at_the_end: self.needs_flush_at_the_end,
};
let barrier = PipelineBarrierRequest {
after_command_num: self.earliest_previous_transition,
source_stage: self.stages,
destination_stages: stage,
by_region: true,
memory_barrier: Some(PipelineMemoryBarrierRequest {
offset: 0,
size: self.size,
source_access: self.access,
destination_access: access,
}),
};
(new_state, Some(barrier))
} else {
// Read after read.
let new_state = CpuAccessibleBufferClState {
size: self.size,
stages: self.stages | stage,
access: self.access | access,
first_stages: Some(self.first_stages.clone().unwrap_or(stage)),
write: false,
earliest_previous_transition: self.earliest_previous_transition,
needs_flush_at_the_end: self.needs_flush_at_the_end,
};
(new_state, None)
}
}
fn finish(self) -> (Self::FinishedState, Option<PipelineBarrierRequest>) {
let barrier = if self.needs_flush_at_the_end {
let barrier = PipelineBarrierRequest {
after_command_num: self.earliest_previous_transition,
source_stage: self.stages,
destination_stages: PipelineStages { host: true, .. PipelineStages::none() },
by_region: true,
memory_barrier: Some(PipelineMemoryBarrierRequest {
offset: 0,
size: self.size,
source_access: self.access,
destination_access: AccessFlagBits { host_read: true,
.. AccessFlagBits::none() },
}),
};
Some(barrier)
} else {
None
};
let finished = CpuAccessibleBufferFinished {
first_stages: self.first_stages.unwrap_or(PipelineStages::none()),
write: self.needs_flush_at_the_end,
};
(finished, barrier)
}
}
pub struct CpuAccessibleBufferFinished {
first_stages: PipelineStages,
write: bool,
}
impl CommandBufferState for CpuAccessibleBufferFinished {
fn on_submit<B, F>(&self, buffer: &B, queue: &Arc<Queue>, fence: F) -> SubmitInfos
where B: Buffer, F: FnOnce() -> Arc<Fence>
{
// FIXME: implement correctly
SubmitInfos {
pre_semaphore: None,
post_semaphore: None,
pre_barrier: None,
post_barrier: None,
}
}
}
/// Object that can be used to read or write the content of a `CpuAccessBuffer`.
///
/// Note that this object holds a rwlock read guard on the chunk. If another thread tries to access

View File

@ -348,6 +348,15 @@ impl Usage {
}
}
/// Builds a `Usage` with `transfer_dest` set to true and the rest to false.
#[inline]
pub fn transfer_dest() -> Usage {
Usage {
transfer_dest: true,
.. Usage::none()
}
}
/// Builds a `Usage` with `vertex_buffer` set to true and the rest to false.
#[inline]
pub fn vertex_buffer() -> Usage {

View File

@ -7,14 +7,24 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::any::Any;
use std::ops::Range;
use std::sync::Arc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::Sender;
use buffer::sys::UnsafeBuffer;
use command_buffer::Submission;
use device::Queue;
use memory::Content;
use sync::AccessFlagBits;
use sync::Fence;
use sync::PipelineStages;
use sync::Semaphore;
use VulkanObject;
pub unsafe trait Buffer: 'static + Send + Sync {
/// Returns the inner buffer.
fn inner(&self) -> &UnsafeBuffer;
@ -58,6 +68,147 @@ pub unsafe trait Buffer: 'static + Send + Sync {
}
}
/// Extension trait for `Buffer`. Types that implement this can used in a `StdCommandBuffer`.
///
/// Each buffer and image used in a `StdCommandBuffer` have an associated state which is
/// represented by the `CommandListState` associated type of this trait. You can make multiple
/// buffers or images share the same state by making `is_same` return true.
pub unsafe trait TrackedBuffer: Buffer {
/// State of the buffer in a list of commands.
///
/// The `Any` bound is here for stupid reasons, sorry.
// TODO: remove Any bound
type CommandListState: Any + CommandListState<FinishedState = Self::FinishedState>;
/// State of the buffer in a finished list of commands.
type FinishedState: CommandBufferState;
/// Returns true if TODO.
///
/// If `is_same` returns true, then the type of `CommandListState` must be the same as for the
/// other buffer. Otherwise a panic will occur.
#[inline]
fn is_same<B>(&self, other: &B) -> bool where B: Buffer {
self.inner().internal_object() == other.inner().internal_object()
}
/// Returns the state of the buffer when it has not yet been used.
fn initial_state(&self) -> Self::CommandListState;
}
/// Trait for objects that represent the state of a slice of the buffer in a list of commands.
pub trait CommandListState {
type FinishedState: CommandBufferState;
/// Returns a new state that corresponds to the moment after a slice of the buffer has been
/// used in the pipeline. The parameters indicate in which way it has been used.
///
/// If the transition should result in a pipeline barrier, then it must be returned by this
/// function.
fn transition(self, num_command: usize, buffer: &UnsafeBuffer, offset: usize, size: usize,
write: bool, stage: PipelineStages, access: AccessFlagBits)
-> (Self, Option<PipelineBarrierRequest>)
where Self: Sized;
/// Function called when the command buffer builder is turned into a real command buffer.
///
/// This function can return an additional pipeline barrier that will be applied at the end
/// of the command buffer.
fn finish(self) -> (Self::FinishedState, Option<PipelineBarrierRequest>);
}
/// Requests that a pipeline barrier is created.
pub struct PipelineBarrierRequest {
/// The number of the command after which the barrier should be placed. Must usually match
/// the number that was passed to the previous call to `transition`, or 0 if the buffer hasn't
/// been used yet.
pub after_command_num: usize,
/// The source pipeline stages of the transition.
pub source_stage: PipelineStages,
/// The destination pipeline stages of the transition.
pub destination_stages: PipelineStages,
/// If true, the pipeliner barrier is by region. There is literaly no reason to pass `false`
/// here, but it is included just in case.
pub by_region: bool,
/// An optional memory barrier. See the docs of `PipelineMemoryBarrierRequest`.
pub memory_barrier: Option<PipelineMemoryBarrierRequest>,
}
/// Requests that a memory barrier is created as part of the pipeline barrier.
///
/// By default, a pipeline barrier only guarantees that the source operations are executed before
/// the destination operations, but it doesn't make memory writes made by source operations visible
/// to the destination operations. In order to make so, you have to add a memory barrier.
///
/// The memory barrier always concerns the buffer that is currently being processed. You can't add
/// a memory barrier that concerns another resource.
pub struct PipelineMemoryBarrierRequest {
/// Offset of start of the range to flush.
pub offset: usize,
/// Size of the range to flush.
pub size: usize,
/// Source accesses.
pub source_access: AccessFlagBits,
/// Destination accesses.
pub destination_access: AccessFlagBits,
}
/// Trait for objects that represent the state of a slice of the buffer in a command buffer.
pub trait CommandBufferState {
/// Called right before the command buffer is submitted.
// TODO: function should be unsafe because it must be guaranteed that a cb is submitted
fn on_submit<B, F>(&self, buffer: &B, queue: &Arc<Queue>, fence: F) -> SubmitInfos
where B: Buffer, F: FnOnce() -> Arc<Fence>;
}
pub struct SubmitInfos {
pub pre_semaphore: Option<(Receiver<Arc<Semaphore>>, PipelineStages)>,
pub post_semaphore: Option<Sender<Arc<Semaphore>>>,
pub pre_barrier: Option<PipelineBarrierRequest>,
pub post_barrier: Option<PipelineBarrierRequest>,
}
unsafe impl<B> Buffer for Arc<B> where B: Buffer {
#[inline]
fn inner(&self) -> &UnsafeBuffer {
(**self).inner()
}
fn needs_fence(&self, _: bool, _: Range<usize>) -> Option<bool> { unimplemented!() }
fn host_accesses(&self, _: usize) -> bool { unimplemented!() }
fn blocks(&self, _: Range<usize>) -> Vec<usize> { unimplemented!() }
fn block_memory_range(&self, _: usize) -> Range<usize> { unimplemented!() }
unsafe fn gpu_access(&self, _: &mut Iterator<Item = AccessRange>,
_: &Arc<Submission>) -> GpuAccessResult { unimplemented!() }
#[inline]
fn size(&self) -> usize {
(**self).size()
}
}
unsafe impl<B> TrackedBuffer for Arc<B> where B: TrackedBuffer, Arc<B>: Buffer {
type CommandListState = B::CommandListState;
type FinishedState = B::FinishedState;
#[inline]
fn is_same<Bo>(&self, other: &Bo) -> bool where Bo: Buffer {
(**self).is_same(other)
}
#[inline]
fn initial_state(&self) -> Self::CommandListState {
(**self).initial_state()
}
}
pub unsafe trait TypedBuffer: Buffer {
type Content: ?Sized + 'static;

View File

@ -59,6 +59,7 @@ mod inner;
mod outer;
pub mod pool;
pub mod std;
pub mod submit;
pub mod sys;

View File

@ -0,0 +1,76 @@
// 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::any::Any;
pub struct CopyCommand<L, B> where B: Buffer, L: CommandsList {
previous: L,
buffer: B,
buffer_state: B::CommandBufferState,
transition: Option<>,
}
impl<L, B> CommandsList for CopyCommand<L, B> where B: Buffer, L: CommandsList {
pub fn new(previous: L, buffer: B) -> CopyCommand<L, B> {
let (state, transition) = previous.current_buffer_state(buffer)
.transition(false, self.num_commands(), ShaderStages, AccessFlagBits);
assert!(transition.after_command_num < self.num_commands());
}
}
unsafe impl<L, B> CommandsList for CopyCommand<L, B> where B: Buffer, L: CommandsList {
fn num_commands(&self) -> usize {
self.previous.num_commands() + 1
}
#[inline]
fn requires_graphics_queue(&self) -> bool {
self.previous.requires_graphics_queue()
}
#[inline]
fn requires_compute_queue(&self) -> bool {
self.previous.requires_compute_queue()
}
fn current_buffer_state<Ob>(&self, other: &Ob) -> Ob::CommandBufferState
where Ob: Buffer
{
if self.buffer.is_same(b) {
let s: &Ob::CommandBufferState = (&self.buffer_state as &Any).downcast_ref().unwrap();
s.clone()
} else {
self.previous.current_buffer_state(b)
}
}
unsafe fn build_unsafe_command_buffer<P, I>(&self, pool: P, transitions: I) -> UnsafeCommandBufferBuilder<P>
where P: CommandPool
{
let my_command_num = self.num_commands();
let mut transitions_to_apply = PipelineBarrierBuilder::new();
let transitions = transitions.filter_map(|transition| {
if transition.after_command_num >= my_command_num {
transitions_to_apply.push(transition);
None
} else {
Some(transition)
}
}).chain(self.transition.clone().into_iter());
let mut parent_cb = self.previous.build_unsafe_command_buffer(pool, transitions.clone().chain());
parent_cb.copy_buffer();
parent_cb.pipeline_barrier(transitions_to_apply);
parent_cb
}
}
unsafe impl StdPrimaryCommandsList

View File

@ -0,0 +1,143 @@
// 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::iter;
use std::iter::Empty;
use std::sync::Arc;
use buffer::traits::TrackedBuffer;
use command_buffer::pool::CommandPool;
use command_buffer::pool::StandardCommandPool;
use command_buffer::std::StdCommandsList;
use command_buffer::submit::CommandBuffer;
use command_buffer::submit::SubmitInfo;
use command_buffer::sys::PipelineBarrierBuilder;
use command_buffer::sys::UnsafeCommandBuffer;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use command_buffer::sys::Flags;
use command_buffer::sys::Kind;
use device::Device;
use device::Queue;
use framebuffer::EmptySinglePassRenderPass;
use instance::QueueFamily;
use sync::Fence;
use sync::PipelineStages;
use sync::Semaphore;
pub struct PrimaryCbBuilder<P = Arc<StandardCommandPool>> where P: CommandPool {
pool: P,
flags: Flags,
}
impl PrimaryCbBuilder<Arc<StandardCommandPool>> {
/// Builds a new primary command buffer builder.
#[inline]
pub fn new(device: &Arc<Device>, family: QueueFamily)
-> PrimaryCbBuilder<Arc<StandardCommandPool>>
{
PrimaryCbBuilder::with_pool(Device::standard_command_pool(device, family))
}
}
impl<P> PrimaryCbBuilder<P> where P: CommandPool {
/// Builds a new primary command buffer builder that uses a specific pool.
pub fn with_pool(pool: P) -> PrimaryCbBuilder<P> {
PrimaryCbBuilder {
pool: pool,
flags: Flags::SimultaneousUse, // TODO: allow customization
}
}
}
unsafe impl<P> StdCommandsList for PrimaryCbBuilder<P> where P: CommandPool {
type Pool = P;
type Output = PrimaryCb<P>;
#[inline]
fn num_commands(&self) -> usize {
0
}
#[inline]
fn check_queue_validity(&self, queue: QueueFamily) -> Result<(), ()> {
Ok(())
}
#[inline]
unsafe fn extract_current_buffer_state<B>(&mut self, buffer: &B) -> Option<B::CommandListState>
where B: TrackedBuffer
{
None
}
unsafe fn raw_build<I, F>(self, additional_elements: F, transitions: I,
final_transitions: PipelineBarrierBuilder) -> Self::Output
where F: FnOnce(&mut UnsafeCommandBufferBuilder<Self::Pool>),
I: Iterator<Item = (usize, PipelineBarrierBuilder)>
{
let mut pipeline_barrier = PipelineBarrierBuilder::new();
for (_, transition) in transitions {
pipeline_barrier.merge(transition);
}
let kind = Kind::Primary::<EmptySinglePassRenderPass, EmptySinglePassRenderPass>;
let mut cb = UnsafeCommandBufferBuilder::new(self.pool, kind,
self.flags).unwrap(); // TODO: handle
cb.pipeline_barrier(pipeline_barrier);
additional_elements(&mut cb);
cb.pipeline_barrier(final_transitions);
PrimaryCb {
cb: cb.build().unwrap(), // TODO: handle error
}
}
}
pub struct PrimaryCb<P = Arc<StandardCommandPool>> where P: CommandPool {
cb: UnsafeCommandBuffer<P>,
}
unsafe impl<P> CommandBuffer for PrimaryCb<P> where P: CommandPool {
type Pool = P;
type SemaphoresWaitIterator = Empty<(Arc<Semaphore>, PipelineStages)>;
type SemaphoresSignalIterator = Empty<Arc<Semaphore>>;
#[inline]
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> {
&self.cb
}
unsafe fn on_submit<F>(&self, queue: &Arc<Queue>, fence: F)
-> SubmitInfo<Self::SemaphoresWaitIterator,
Self::SemaphoresSignalIterator>
where F: FnMut() -> Arc<Fence>
{
// TODO: must handle SimultaneousUse and Once flags
SubmitInfo {
semaphores_wait: iter::empty(),
semaphores_signal: iter::empty(),
pre_pipeline_barrier: PipelineBarrierBuilder::new(),
post_pipeline_barrier: PipelineBarrierBuilder::new(),
}
}
}
#[cfg(test)]
mod tests {
use command_buffer::std::PrimaryCbBuilder;
use command_buffer::std::StdCommandsList;
use command_buffer::submit::CommandBuffer;
#[test]
fn basic_submit() {
let (device, queue) = gfx_dev_and_queue!();
let _ = PrimaryCbBuilder::new(&device, queue.family()).build().submit(&queue);
}
}

View File

@ -0,0 +1,96 @@
// 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::iter;
use buffer::traits::TrackedBuffer;
use command_buffer::pool::CommandPool;
use command_buffer::submit::CommandBuffer;
use command_buffer::sys::PipelineBarrierBuilder;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use instance::QueueFamily;
pub use self::empty::PrimaryCb;
pub use self::empty::PrimaryCbBuilder;
pub use self::update_buffer::UpdateCommand;
mod empty;
mod update_buffer;
/// A list of commands that can be turned into a command buffer.
pub unsafe trait StdCommandsList {
/// The type of the pool that will be used to create the command buffer.
type Pool: CommandPool;
/// The type of the command buffer that will be generated.
type Output: CommandBuffer<Pool = Self::Pool>;
/// Adds a command that writes the content of a buffer.
///
/// After this command is executed, the content of `buffer` will become `data`.
#[inline]
fn update_buffer<'a, B, D: ?Sized>(self, buffer: B, data: &'a D)
-> UpdateCommand<'a, Self, B, D>
where Self: Sized, B: TrackedBuffer, D: Copy + 'static
{
UpdateCommand::new(self, buffer, data)
}
/// Turns the commands list into a command buffer that can be submitted.
fn build(self) -> Self::Output where Self: Sized {
unsafe {
self.raw_build(|_| {}, iter::empty(), PipelineBarrierBuilder::new())
}
}
/// Returns the number of commands in the commands list.
///
/// Note that multiple actual commands may count for just 1.
fn num_commands(&self) -> usize;
/// Checks whether the command can be executed on the given queue family.
// TODO: error type?
fn check_queue_validity(&self, queue: QueueFamily) -> Result<(), ()>;
/// Returns the current status of a buffer, or `None` if the buffer hasn't been used yet.
///
/// Whether the buffer passed as parameter is the same as the one in the commands list must be
/// determined with the `is_same` method of `TrackedBuffer`.
///
/// Calling this function tells the commands list that you are going to manage the
/// synchronization that buffer yourself. Hence why the function is unsafe.
///
/// This function is not meant to be called, except when writing a wrapper around a
/// commands list.
///
/// # Panic
///
/// - Panics if the state of that buffer has already been previously extracted.
///
unsafe fn extract_current_buffer_state<B>(&mut self, buffer: &B) -> Option<B::CommandListState>
where B: TrackedBuffer;
/// Turns the commands list into a command buffer.
///
/// This function accepts additional arguments that will customize the output:
///
/// - `additional_elements` is a closure that must be called on the command buffer builder
/// after it has finished building and before `final_transitions` are added.
/// - `transitions` is a list of pipeline barriers accompanied by a command number. The
/// pipeline barrier must happen after the given command number. Usually you want all the
/// the command numbers to be inferior to `num_commands`.
/// - `final_transitions` is a pipeline barrier that must be added at the end of the
/// command buffer builder.
unsafe fn raw_build<I, F>(self, additional_elements: F, transitions: I,
final_transitions: PipelineBarrierBuilder) -> Self::Output
where F: FnOnce(&mut UnsafeCommandBufferBuilder<Self::Pool>),
I: Iterator<Item = (usize, PipelineBarrierBuilder)>;
}
/// Extension trait for `StdCommandsList` that indicates that we're outside a render pass.
pub unsafe trait OutsideRenderPass: StdCommandsList {}

View File

@ -0,0 +1,240 @@
// 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::any::Any;
use std::iter;
use std::iter::Empty;
use std::sync::Arc;
use smallvec::SmallVec;
use buffer::traits::CommandBufferState;
use buffer::traits::CommandListState;
use buffer::traits::PipelineBarrierRequest;
use buffer::traits::TrackedBuffer;
use command_buffer::std::StdCommandsList;
use command_buffer::submit::CommandBuffer;
use command_buffer::submit::SubmitInfo;
use command_buffer::sys::PipelineBarrierBuilder;
use command_buffer::sys::UnsafeCommandBuffer;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use device::Queue;
use instance::QueueFamily;
use sync::AccessFlagBits;
use sync::Fence;
use sync::PipelineStages;
use sync::Semaphore;
pub struct UpdateCommand<'a, L, B, D: ?Sized> where B: TrackedBuffer, L: StdCommandsList, D: 'static {
previous: L,
buffer: B,
buffer_state: Option<B::CommandListState>,
data: &'a D,
transition: Option<PipelineBarrierRequest>,
}
impl<'a, L, B, D: ?Sized> UpdateCommand<'a, L, B, D>
where B: TrackedBuffer,
L: StdCommandsList,
D: Copy + 'static,
{
pub fn new(mut previous: L, buffer: B, data: &'a D) -> UpdateCommand<'a, L, B, D> {
let stage = PipelineStages {
transfer: true,
.. PipelineStages::none()
};
let access = AccessFlagBits {
transfer_write: true,
.. AccessFlagBits::none()
};
let (state, transition) = unsafe {
previous.extract_current_buffer_state(&buffer)
.unwrap_or(buffer.initial_state())
.transition(previous.num_commands() + 1, buffer.inner(),
0, buffer.size(), true, stage, access)
};
if let Some(ref transition) = transition {
assert!(transition.after_command_num <= previous.num_commands());
}
UpdateCommand {
previous: previous,
buffer: buffer,
buffer_state: Some(state),
data: data,
transition: transition,
}
}
}
unsafe impl<'a, L, B, D: ?Sized> StdCommandsList for UpdateCommand<'a, L, B, D>
where B: TrackedBuffer,
L: StdCommandsList,
D: Copy + 'static,
{
type Pool = L::Pool;
type Output = UpdateCommandCb<L, B>;
#[inline]
fn num_commands(&self) -> usize {
self.previous.num_commands() + 1
}
#[inline]
fn check_queue_validity(&self, queue: QueueFamily) -> Result<(), ()> {
// No restriction
self.previous.check_queue_validity(queue)
}
unsafe fn extract_current_buffer_state<Ob>(&mut self, buffer: &Ob)
-> Option<Ob::CommandListState>
where Ob: TrackedBuffer
{
if self.buffer.is_same(buffer) {
let s: &mut Option<Ob::CommandListState> = (&mut self.buffer_state as &mut Any)
.downcast_mut().unwrap();
Some(s.take().unwrap())
} else {
self.previous.extract_current_buffer_state(buffer)
}
}
unsafe fn raw_build<I, F>(mut self, additional_elements: F, transitions: I,
mut final_transitions: PipelineBarrierBuilder) -> Self::Output
where F: FnOnce(&mut UnsafeCommandBufferBuilder<L::Pool>),
I: Iterator<Item = (usize, PipelineBarrierBuilder)>
{
let finished_state = match self.buffer_state.take().map(|s| s.finish()) {
Some((s, t)) => {
if let Some(t) = t {
final_transitions.add_buffer_barrier_request(self.buffer.inner(), t);
}
Some(s)
},
None => None,
};
// We split the transitions in two: those to apply after the actual command, and those to
// transfer to the parent so that they are applied before the actual command.
let my_command_num = self.num_commands();
let mut transitions_to_apply = PipelineBarrierBuilder::new();
let mut transitions = transitions.filter_map(|(after_command_num, transition)| {
if after_command_num >= my_command_num || !transitions_to_apply.is_empty() {
transitions_to_apply.merge(transition);
None
} else {
Some((after_command_num, transition))
}
}).collect::<SmallVec<[_; 8]>>();
let my_transition = if let Some(my_transition) = self.transition.take() {
let mut t = PipelineBarrierBuilder::new();
let c_num = my_transition.after_command_num;
t.add_buffer_barrier_request(self.buffer.inner(), my_transition);
Some((c_num, t))
} else {
None
};
let transitions = my_transition.into_iter().chain(transitions.into_iter());
let my_buffer = self.buffer;
let my_data = self.data;
let parent = self.previous.raw_build(|cb| {
cb.update_buffer(my_buffer.inner(), 0, my_buffer.size(), my_data);
cb.pipeline_barrier(transitions_to_apply);
additional_elements(cb);
}, transitions, final_transitions);
UpdateCommandCb {
previous: parent,
buffer: my_buffer,
buffer_state: finished_state,
}
}
}
pub struct UpdateCommandCb<L, B> where B: TrackedBuffer, L: StdCommandsList {
previous: L::Output,
buffer: B,
buffer_state: Option<B::FinishedState>,
}
unsafe impl<L, B> CommandBuffer for UpdateCommandCb<L, B>
where B: TrackedBuffer, L: StdCommandsList
{
type Pool = L::Pool;
type SemaphoresWaitIterator = Empty<(Arc<Semaphore>, PipelineStages)>;
type SemaphoresSignalIterator = Empty<Arc<Semaphore>>;
#[inline]
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> {
self.previous.inner()
}
unsafe fn on_submit<F>(&self, queue: &Arc<Queue>, mut fence: F)
-> SubmitInfo<Self::SemaphoresWaitIterator,
Self::SemaphoresSignalIterator>
where F: FnMut() -> Arc<Fence>
{
let parent = self.previous.on_submit(queue, &mut fence);
let mut my_output = SubmitInfo {
semaphores_wait: iter::empty(), // FIXME:
semaphores_signal: iter::empty(), // FIXME:
pre_pipeline_barrier: parent.pre_pipeline_barrier,
post_pipeline_barrier: parent.post_pipeline_barrier,
};
if let Some(ref buffer_state) = self.buffer_state {
let submit_infos = buffer_state.on_submit(&self.buffer, queue, fence);
if let Some(pre) = submit_infos.pre_barrier {
my_output.pre_pipeline_barrier.add_buffer_barrier_request(self.buffer.inner(), pre);
}
if let Some(post) = submit_infos.post_barrier {
my_output.post_pipeline_barrier.add_buffer_barrier_request(self.buffer.inner(), post);
}
}
my_output
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use buffer::BufferUsage;
use buffer::CpuAccessibleBuffer;
use command_buffer::std::PrimaryCbBuilder;
use command_buffer::std::StdCommandsList;
use command_buffer::submit::CommandBuffer;
#[test]
fn basic_submit() {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_data(&device, &BufferUsage::transfer_dest(),
Some(queue.family()), 0u32).unwrap();
let _ = PrimaryCbBuilder::new(&device, queue.family())
.update_buffer(buffer.clone(), &128u32)
.build()
.submit(&queue);
let content = buffer.read(Duration::from_secs(0)).unwrap();
assert_eq!(*content, 128);
}
}

View File

@ -39,8 +39,9 @@ pub unsafe trait CommandBuffer {
/// multiple command buffers at once instead.
///
/// This is a simple shortcut for creating a `Submit` object.
// TODO: remove 'static
#[inline]
fn submit(self, queue: &Arc<Queue>) -> Submission where Self: Sized {
fn submit(self, queue: &Arc<Queue>) -> Submission where Self: Sized + 'static {
Submit::new().add(self).submit(queue)
}
@ -80,10 +81,10 @@ pub unsafe trait CommandBuffer {
/// The implementation must ensure that the command buffer doesn't get destroyed before the
/// fence is signaled, or before a fence of a later submission to the same queue is signaled.
///
unsafe fn on_submit<F>(&self, queue_family: u32, queue_within_family: u32, fence: F)
unsafe fn on_submit<F>(&self, queue: &Arc<Queue>, fence: F)
-> SubmitInfo<Self::SemaphoresWaitIterator,
Self::SemaphoresSignalIterator>
where F: FnOnce() -> Arc<Fence>;
where F: FnMut() -> Arc<Fence>;
}
/// Information about how the submitting function should synchronize the submission.
@ -163,8 +164,9 @@ impl<L> Submit<L> where L: SubmitList {
/// In the Vulkan API, a submission is divided into batches that each contain one or more
/// command buffers. Vulkano will automatically determine which command buffers can be grouped
/// into the same batch.
// TODO: remove 'static
#[inline]
pub fn add<C>(self, command_buffer: C) -> Submit<(C, L)> where C: CommandBuffer {
pub fn add<C>(self, command_buffer: C) -> Submit<(C, L)> where C: CommandBuffer + 'static {
Submit { list: (command_buffer, self.list) }
}
@ -172,7 +174,7 @@ impl<L> Submit<L> where L: SubmitList {
pub fn submit(self, queue: &Arc<Queue>) -> Submission {
let SubmitListOpaque { fence, wait_semaphores, wait_stages, command_buffers,
signal_semaphores, mut submits, keep_alive }
= self.list.infos(queue.family().id(), queue.id_within_family());
= self.list.infos(queue);
// TODO: for now we always create a Fence in order to put it in the submission
let fence = fence.unwrap_or_else(|| Fence::new(queue.device().clone()));
@ -238,11 +240,11 @@ pub struct SubmitListOpaque {
}
pub unsafe trait SubmitList {
fn infos(self, queue_family: u32, queue_within_family: u32) -> SubmitListOpaque;
fn infos(self, queue: &Arc<Queue>) -> SubmitListOpaque;
}
unsafe impl SubmitList for () {
fn infos(self, _: u32, _: u32) -> SubmitListOpaque {
fn infos(self, queue: &Arc<Queue>) -> SubmitListOpaque {
SubmitListOpaque {
fence: None,
wait_semaphores: SmallVec::new(),
@ -255,16 +257,16 @@ unsafe impl SubmitList for () {
}
}
unsafe impl<C, R> SubmitList for (C, R) where C: CommandBuffer, R: SubmitList {
fn infos(self, queue_family: u32, queue_within_family: u32) -> SubmitListOpaque {
// TODO: remove 'static
unsafe impl<C, R> SubmitList for (C, R) where C: CommandBuffer + 'static, R: SubmitList {
fn infos(self, queue: &Arc<Queue>) -> SubmitListOpaque {
// TODO: attempt to group multiple submits into one when possible
let (current, rest) = self;
let mut infos = rest.infos(queue_family, queue_within_family);
let mut infos = rest.infos(queue);
let device = current.inner().device().clone();
let qf = device.physical_device().queue_family_by_id(queue_family).unwrap();
let current_infos = unsafe { current.on_submit(queue_family, queue_within_family, || {
let current_infos = unsafe { current.on_submit(queue, || {
if let Some(fence) = infos.fence.as_ref() {
return fence.clone();
}
@ -287,7 +289,7 @@ unsafe impl<C, R> SubmitList for (C, R) where C: CommandBuffer, R: SubmitList {
};
if !current_infos.pre_pipeline_barrier.is_empty() {
let mut cb = UnsafeCommandBufferBuilder::new(Device::standard_command_pool(&device, qf),
let mut cb = UnsafeCommandBufferBuilder::new(Device::standard_command_pool(&device, queue.family()),
Kind::Primary::<EmptySinglePassRenderPass,
EmptySinglePassRenderPass>,
Flags::OneTimeSubmit).unwrap();
@ -300,7 +302,7 @@ unsafe impl<C, R> SubmitList for (C, R) where C: CommandBuffer, R: SubmitList {
infos.command_buffers.push(current.inner().internal_object());
if !current_infos.post_pipeline_barrier.is_empty() {
let mut cb = UnsafeCommandBufferBuilder::new(Device::standard_command_pool(&device, qf),
let mut cb = UnsafeCommandBufferBuilder::new(Device::standard_command_pool(&device, queue.family()),
Kind::Primary::<EmptySinglePassRenderPass,
EmptySinglePassRenderPass>,
Flags::OneTimeSubmit).unwrap();
@ -324,6 +326,7 @@ unsafe impl<C, R> SubmitList for (C, R) where C: CommandBuffer, R: SubmitList {
}
infos.submits.push(new_submit);
infos.keep_alive.push(Arc::new(current) as Arc<_>);
infos
}
@ -344,6 +347,7 @@ mod tests {
use command_buffer::sys::UnsafeCommandBuffer;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use device::Device;
use device::Queue;
use framebuffer::EmptySinglePassRenderPass;
use sync::Fence;
use sync::PipelineStages;
@ -359,7 +363,7 @@ mod tests {
fn inner(&self) -> &UnsafeCommandBuffer<Self::Pool> { &self.inner }
unsafe fn on_submit<F>(&self, queue_family: u32, queue_within_family: u32, fence: F)
unsafe fn on_submit<F>(&self, _: &Arc<Queue>, fence: F)
-> SubmitInfo<Self::SemaphoresWaitIterator,
Self::SemaphoresSignalIterator>
where F: FnOnce() -> Arc<Fence>

View File

@ -39,8 +39,10 @@ use smallvec::SmallVec;
use buffer::Buffer;
use buffer::BufferSlice;
use buffer::sys::UnsafeBuffer;
use buffer::traits::PipelineBarrierRequest as BufferPipelineBarrierRequest;
use command_buffer::pool::AllocatedCommandBuffer;
use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolFinished;
use descriptor::pipeline_layout::PipelineLayout;
use descriptor::descriptor_set::UnsafeDescriptorSet;
use descriptor::descriptor::ShaderStages;
@ -302,7 +304,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
else { vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL };
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdClearColorImage(cmd, image.internal_object(), layout, &clear_value,
ranges.len() as u32, ranges.as_ptr());
}
@ -399,7 +401,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
else { vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL };
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdClearDepthStencilImage(cmd, image.internal_object(), layout, &clear_value,
ranges.len() as u32, ranges.as_ptr());
}
@ -503,7 +505,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
}
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdClearAttachments(cmd, attachments.len() as u32, attachments.as_ptr(),
rects.len() as u32, rects.as_ptr());
}
@ -543,7 +545,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
};
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdFillBuffer(cmd, buffer.internal_object(), offset as vk::DeviceSize,
size as vk::DeviceSize, data);
}
@ -580,7 +582,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
debug_assert!(size <= 65536);
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdUpdateBuffer(cmd, buffer.internal_object(), offset as vk::DeviceSize,
size as vk::DeviceSize, data as *const D as *const _);
}
@ -637,7 +639,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
}
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdCopyBuffer(cmd, src.internal_object(), dest.internal_object(), regions.len() as u32,
regions.as_ptr());
}
@ -656,7 +658,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
}
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
unsafe {
vk.CmdPipelineBarrier(cmd, barrier.src_stage_mask, barrier.dst_stage_mask,
@ -751,7 +753,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
};
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBeginRenderPass(cmd, &infos,
if secondary { vk::SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS }
else { vk::SUBPASS_CONTENTS_INLINE });
@ -767,7 +769,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
#[inline]
pub unsafe fn next_subpass(&mut self, secondary: bool) {
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdNextSubpass(cmd, if secondary { vk::SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS }
else { vk::SUBPASS_CONTENTS_INLINE });
}
@ -782,7 +784,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
#[inline]
pub unsafe fn end_render_pass(&mut self) {
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdEndRenderPass(cmd);
}
@ -799,7 +801,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
#[inline]
pub unsafe fn bind_pipeline_graphics<V, L, R>(&mut self, pipeline: &GraphicsPipeline<V, L, R>) {
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBindPipeline(cmd, vk::PIPELINE_BIND_POINT_GRAPHICS, pipeline.internal_object());
}
@ -812,7 +814,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
#[inline]
pub unsafe fn bind_pipeline_compute<L>(&mut self, pipeline: &ComputePipeline<L>) {
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBindPipeline(cmd, vk::PIPELINE_BIND_POINT_COMPUTE, pipeline.internal_object());
}
@ -822,7 +824,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
first_instance: u32)
{
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDraw(cmd, vertex_count, instance_count, first_vertex, first_instance);
}
@ -832,7 +834,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
first_vertex: u32, vertex_offset: i32, first_instance: u32)
{
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDrawIndexed(cmd, vertex_count, instance_count, first_vertex, vertex_offset,
first_instance);
}
@ -850,7 +852,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
assert_eq!(buffer.device().internal_object(), self.device.internal_object());
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDrawIndirect(cmd, buffer.internal_object(), offset as vk::DeviceSize, draw_count,
stride);
}
@ -868,7 +870,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
assert_eq!(buffer.device().internal_object(), self.device.internal_object());
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDrawIndexedIndirect(cmd, buffer.internal_object(), offset as vk::DeviceSize,
draw_count, stride);
}
@ -877,7 +879,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
#[inline]
pub unsafe fn dispatch(&mut self, x: u32, y: u32, z: u32) {
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDispatch(cmd, x, y, z);
}
@ -892,7 +894,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
assert_eq!(buffer.device().internal_object(), self.device.internal_object());
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdDispatchIndirect(cmd, buffer.internal_object(), offset as vk::DeviceSize);
}
@ -919,7 +921,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
}
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBindVertexBuffers(cmd, first_binding, raw_buffers.len() as u32, raw_buffers.as_ptr(),
raw_offsets.as_ptr());
}
@ -937,7 +939,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
assert_eq!(buffer.device().internal_object(), self.device.internal_object());
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBindIndexBuffer(cmd, buffer.internal_object(), offset as vk::DeviceSize,
index_ty as u32);
}
@ -972,7 +974,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
let dynamic_offsets: SmallVec<[_; 64]> = dynamic_offsets.into_iter().collect();
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdBindDescriptorSets(cmd, bind_point, layout.inner().internal_object(), first_set,
descriptor_sets.len() as u32, descriptor_sets.as_ptr(),
dynamic_offsets.len() as u32, dynamic_offsets.as_ptr());
@ -995,7 +997,7 @@ impl<P> UnsafeCommandBufferBuilder<P> where P: CommandPool {
debug_assert!(mem::size_of_val(data) <= u32::MAX as usize);
let vk = self.device.pointers();
let cmd = self.cmd.take().unwrap();
let cmd = self.cmd.clone().take().unwrap();
vk.CmdPushConstants(cmd, layout.inner().internal_object(), stages.into(), offset as u32,
mem::size_of_val(data) as u32, data as *const D as *const _);
}
@ -1113,6 +1115,18 @@ impl PipelineBarrierBuilder {
self.src_stage_mask == 0 || self.dst_stage_mask == 0
}
/// Merges another pipeline builder into this one.
#[inline]
pub fn merge(&mut self, mut other: PipelineBarrierBuilder) {
self.src_stage_mask |= other.src_stage_mask;
self.dst_stage_mask |= other.dst_stage_mask;
self.dependency_flags &= other.dependency_flags;
self.memory_barriers.extend(other.memory_barriers.into_iter());
self.buffer_barriers.extend(other.buffer_barriers.into_iter());
self.image_barriers.extend(other.image_barriers.into_iter());
}
/// Adds an execution dependency. This means that all the stages in `source` of the previous
/// commands must finish before any of the stages in `dest` of the following commands can start.
///
@ -1160,6 +1174,37 @@ impl PipelineBarrierBuilder {
});
}
pub unsafe fn add_buffer_barrier_request(&mut self, buffer: &UnsafeBuffer,
request: BufferPipelineBarrierRequest)
{
if !request.by_region {
self.dependency_flags = 0;
}
self.src_stage_mask |= request.source_stage.into();
self.dst_stage_mask |= request.destination_stages.into();
if let Some(memory_barrier) = request.memory_barrier {
let (src_queue, dest_queue) = /*if let Some((src_queue, dest_queue)) = queue_transfer {
(src_queue, dest_queue)
} else {*/
(vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)
/*}*/;
self.buffer_barriers.push(vk::BufferMemoryBarrier {
sType: vk::STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
pNext: ptr::null(),
srcAccessMask: memory_barrier.source_access.into(),
dstAccessMask: memory_barrier.destination_access.into(),
srcQueueFamilyIndex: src_queue,
dstQueueFamilyIndex: dest_queue,
buffer: buffer.internal_object(),
offset: memory_barrier.offset as vk::DeviceSize,
size: memory_barrier.size as vk::DeviceSize,
});
}
}
/// Adds a buffer memory barrier. This means that all the memory writes to the given buffer by
/// the given source stages for the given source accesses must be visible by the given dest
/// stages for the given dest accesses.
@ -1306,3 +1351,12 @@ unsafe impl<P> VulkanObject for UnsafeCommandBuffer<P> where P: CommandPool {
self.cmd
}
}
impl<P> Drop for UnsafeCommandBuffer<P> where P: CommandPool {
#[inline]
fn drop(&mut self) {
unsafe {
self.pool.free(self.secondary_cb, Some(self.cmd.into()).into_iter());
}
}
}