Add TrackedImage trait and the equivalent of buffers for images

This commit is contained in:
Pierre Krieger 2016-08-17 15:36:07 +02:00
parent 8d928cba1c
commit b94e62fbde
6 changed files with 231 additions and 6 deletions

View File

@ -16,6 +16,7 @@ use std::sync::mpsc::Sender;
use buffer::sys::UnsafeBuffer;
use command_buffer::Submission;
use device::Queue;
use image::Image;
use memory::Content;
use sync::AccessFlagBits;
@ -68,7 +69,7 @@ pub unsafe trait Buffer: 'static + Send + Sync {
}
}
/// Extension trait for `Buffer`. Types that implement this can used in a `StdCommandBuffer`.
/// Extension trait for `Buffer`. Types that implement this can be 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
@ -87,10 +88,19 @@ pub unsafe trait TrackedBuffer: Buffer {
/// 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 {
fn is_same_buffer<B>(&self, other: &B) -> bool where B: Buffer {
self.inner().internal_object() == other.inner().internal_object()
}
/// Returns true if TODO.
///
/// If `is_same` returns true, then the type of `CommandListState` must be the same as for the
/// other image. Otherwise a panic will occur.
#[inline]
fn is_same_image<I>(&self, other: &I) -> bool where I: Image {
false
}
/// Returns the state of the buffer when it has not yet been used.
fn initial_state(&self) -> Self::CommandListState;
}
@ -156,7 +166,7 @@ pub struct PipelineMemoryBarrierRequest {
pub destination_access: AccessFlagBits,
}
/// Trait for objects that represent the state of a slice of the buffer in a command buffer.
/// Trait for objects that represent the state 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
@ -199,8 +209,13 @@ unsafe impl<B> TrackedBuffer for Arc<B> where B: TrackedBuffer, Arc<B>: Buffer {
type FinishedState = B::FinishedState;
#[inline]
fn is_same<Bo>(&self, other: &Bo) -> bool where Bo: Buffer {
(**self).is_same(other)
fn is_same_buffer<Bo>(&self, other: &Bo) -> bool where Bo: Buffer {
(**self).is_same_buffer(other)
}
#[inline]
fn is_same_image<I>(&self, other: &I) -> bool where I: Image {
(**self).is_same_image(other)
}
#[inline]

View File

@ -26,6 +26,7 @@ use command_buffer::sys::Kind;
use device::Device;
use device::Queue;
use framebuffer::EmptySinglePassRenderPass;
use image::traits::TrackedImage;
use instance::QueueFamily;
use sync::Fence;
use sync::PipelineStages;
@ -77,6 +78,13 @@ unsafe impl<P> StdCommandsList for PrimaryCbBuilder<P> where P: CommandPool {
None
}
#[inline]
unsafe fn extract_current_image_state<I>(&mut self, image: &I) -> Option<I::CommandListState>
where I: TrackedImage
{
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>),

View File

@ -15,6 +15,7 @@ use command_buffer::submit::CommandBuffer;
use command_buffer::sys::PipelineBarrierBuilder;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use framebuffer::RenderPass;
use image::traits::TrackedImage;
use instance::QueueFamily;
pub use self::empty::PrimaryCb;
@ -76,6 +77,17 @@ pub unsafe trait StdCommandsList {
unsafe fn extract_current_buffer_state<B>(&mut self, buffer: &B) -> Option<B::CommandListState>
where B: TrackedBuffer;
/// Returns the current status of an image, or `None` if the image hasn't been used yet.
///
/// See the description of `extract_current_buffer_state`.
///
/// # Panic
///
/// - Panics if the state of that image has already been previously extracted.
///
unsafe fn extract_current_image_state<I>(&mut self, image: &I) -> Option<I::CommandListState>
where I: TrackedImage;
/// Turns the commands list into a command buffer.
///
/// This function accepts additional arguments that will customize the output:

View File

@ -25,6 +25,7 @@ use command_buffer::sys::PipelineBarrierBuilder;
use command_buffer::sys::UnsafeCommandBuffer;
use command_buffer::sys::UnsafeCommandBufferBuilder;
use device::Queue;
use image::traits::TrackedImage;
use instance::QueueFamily;
use sync::AccessFlagBits;
use sync::Fence;
@ -99,7 +100,7 @@ unsafe impl<'a, L, B, D: ?Sized> StdCommandsList for UpdateCommand<'a, L, B, D>
-> Option<Ob::CommandListState>
where Ob: TrackedBuffer
{
if self.buffer.is_same(buffer) {
if self.buffer.is_same_buffer(buffer) {
let s: &mut Option<Ob::CommandListState> = (&mut self.buffer_state as &mut Any)
.downcast_mut().unwrap();
Some(s.take().unwrap())
@ -109,6 +110,19 @@ unsafe impl<'a, L, B, D: ?Sized> StdCommandsList for UpdateCommand<'a, L, B, D>
}
}
unsafe fn extract_current_image_state<I>(&mut self, image: &I) -> Option<I::CommandListState>
where I: TrackedImage
{
if self.buffer.is_same_image(image) {
let s: &mut Option<I::CommandListState> = (&mut self.buffer_state as &mut Any)
.downcast_mut().unwrap();
Some(s.take().unwrap())
} else {
self.previous.extract_current_image_state(image)
}
}
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>),

View File

@ -56,6 +56,7 @@ use framebuffer::UnsafeRenderPass;
use image::Image;
use image::sys::Layout;
use image::sys::UnsafeImage;
use image::traits::PipelineBarrierRequest as ImagePipelineBarrierRequest;
use pipeline::ComputePipeline;
use pipeline::GraphicsPipeline;
use pipeline::input_assembly::IndexType;
@ -1190,6 +1191,10 @@ impl PipelineBarrierBuilder {
} else {*/
(vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)
/*}*/;
// TODO: add more debug asserts
debug_assert!(memory_barrier.offset + memory_barrier.size <= buffer.size());
self.buffer_barriers.push(vk::BufferMemoryBarrier {
sType: vk::STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@ -1205,6 +1210,51 @@ impl PipelineBarrierBuilder {
}
}
pub unsafe fn add_image_barrier_request(&mut self, image: &UnsafeImage,
request: ImagePipelineBarrierRequest)
{
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)
/*}*/;
// TODO: add more debug asserts
debug_assert!(memory_barrier.first_mipmap +
memory_barrier.num_mipmaps <= image.mipmap_levels());
debug_assert!(memory_barrier.first_layer +
memory_barrier.num_layers <= image.dimensions().array_layers());
self.image_barriers.push(vk::ImageMemoryBarrier {
sType: vk::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
pNext: ptr::null(),
srcAccessMask: memory_barrier.source_access.into(),
dstAccessMask: memory_barrier.destination_access.into(),
oldLayout: memory_barrier.old_layout as u32,
newLayout: memory_barrier.new_layout as u32,
srcQueueFamilyIndex: src_queue,
dstQueueFamilyIndex: dest_queue,
image: image.internal_object(),
subresourceRange: vk::ImageSubresourceRange {
aspectMask: 1 | 2 | 4 | 8, // FIXME: wrong
baseMipLevel: memory_barrier.first_mipmap,
levelCount: memory_barrier.num_mipmaps,
baseArrayLayer: memory_barrier.first_layer,
layerCount: memory_barrier.num_layers,
},
});
}
}
/// 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.

View File

@ -7,10 +7,15 @@
// 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::Sender;
use std::sync::mpsc::Receiver;
use buffer::Buffer;
use command_buffer::Submission;
use device::Queue;
use format::ClearValue;
use format::Format;
use image::sys::Dimensions;
@ -18,8 +23,13 @@ use image::sys::Layout;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use sampler::Sampler;
use sync::AccessFlagBits;
use sync::PipelineStages;
use sync::Fence;
use sync::Semaphore;
use VulkanObject;
/// Trait for types that represent images.
pub unsafe trait Image: 'static + Send + Sync {
/// Returns the inner unsafe image object used by this image.
@ -115,6 +125,122 @@ pub unsafe trait Image: 'static + Send + Sync {
}
}
/// Extension trait for `Image`. Types that implement this can be 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 TrackedImage: Image {
/// State of the image 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_buffer<B>(&self, other: &B) -> bool where B: Buffer {
false
}
/// Returns true if TODO.
///
/// If `is_same` returns true, then the type of `CommandListState` must be the same as for the
/// other image. Otherwise a panic will occur.
#[inline]
fn is_same_image<I>(&self, other: &I) -> bool where I: Image {
self.inner().internal_object() == other.inner().internal_object()
}
/// Returns the state of the image 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 image 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 image 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, image: &UnsafeImage, first_mipmap: u32,
num_mipmaps: u32, first_layer: u32, num_layers: u32, write: bool, layout: Layout,
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 image 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.
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 image that is currently being processed. You can't add
/// a memory barrier that concerns another resource.
pub struct PipelineMemoryBarrierRequest {
pub first_mipmap: u32,
pub num_mipmaps: u32,
pub first_layer: u32,
pub num_layers: u32,
pub old_layout: Layout,
pub new_layout: Layout,
/// Source accesses.
pub source_access: AccessFlagBits,
/// Destination accesses.
pub destination_access: AccessFlagBits,
}
/// Trait for objects that represent the state of the image 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<I, F>(&self, image: &I, queue: &Arc<Queue>, fence: F) -> SubmitInfos
where I: Image, 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>,
}
/// Extension trait for images. Checks whether the value `T` can be used as a clear value for the
/// given image.
// TODO: isn't that for image views instead?