Work on renderpass layout

This commit is contained in:
Pierre Krieger 2016-02-21 13:24:12 +01:00
parent e0795b55b7
commit 25be5132cf
2 changed files with 172 additions and 25 deletions

View File

@ -9,5 +9,8 @@
- [Can't cast an `ImageResource` into a `Resource` even though the former depends on the latter](https://github.com/rust-lang/rust/issues/5665). - [Can't cast an `ImageResource` into a `Resource` even though the former depends on the latter](https://github.com/rust-lang/rust/issues/5665).
- This library was designed with specialization in mind. There are several `is_compatible` trait methods that perform deep comparisons between
layouts. With specialization available, these methods could be specialized as `true` for layouts that are known to always be compatible.
- https://github.com/rust-lang/rust/issues/29328 - https://github.com/rust-lang/rust/issues/29328

View File

@ -44,9 +44,6 @@ pub unsafe trait RenderPassLayout {
/// Iterator that produces one clear value per attachment. /// Iterator that produces one clear value per attachment.
type ClearValuesIter: Iterator<Item = ClearValue>; type ClearValuesIter: Iterator<Item = ClearValue>;
/// Iterator that produces attachments.
type AttachmentsIter: Iterator<Item = AttachmentDescription>;
/// Decodes a `ClearValues` into a list of clear values where each element corresponds /// Decodes a `ClearValues` into a list of clear values where each element corresponds
/// to an attachment. The size of the returned array must be the same as the number of /// to an attachment. The size of the returned array must be the same as the number of
/// attachments. /// attachments.
@ -55,8 +52,23 @@ pub unsafe trait RenderPassLayout {
/// that are loaded with `LoadOp::Clear` must have an entry in the array. /// that are loaded with `LoadOp::Clear` must have an entry in the array.
fn convert_clear_values(&self, Self::ClearValues) -> Self::ClearValuesIter; fn convert_clear_values(&self, Self::ClearValues) -> Self::ClearValuesIter;
/// Iterator that produces attachments.
type AttachmentsIter: ExactSizeIterator<Item = AttachmentDescription>;
/// Returns the descriptions of the attachments. /// Returns the descriptions of the attachments.
fn attachments(&self) -> Self::AttachmentsIter; fn attachments(&self) -> Self::AttachmentsIter;
/// Iterator that produces passes.
type PassesIter: ExactSizeIterator<Item = PassDescription>;
/// Returns the descriptions of the passes.
fn passes(&self) -> Self::PassesIter;
/// Iterator that produces pass dependencies.
type PassDependenciesIter: ExactSizeIterator<Item = PassDependencyDescription>;
/// Returns the descriptions of the dependencies between passes.
fn pass_dependencies(&self) -> Self::PassDependenciesIter;
} }
pub unsafe trait RenderPassLayoutExt<'a, M: 'a>: RenderPassLayout { pub unsafe trait RenderPassLayoutExt<'a, M: 'a>: RenderPassLayout {
@ -65,6 +77,35 @@ pub unsafe trait RenderPassLayoutExt<'a, M: 'a>: RenderPassLayout {
fn ids(&self, &Self::AttachmentsList) -> (Vec<Arc<ImageResource>>, Vec<u64>); fn ids(&self, &Self::AttachmentsList) -> (Vec<Arc<ImageResource>>, Vec<u64>);
} }
/// Trait implemented on renderpass layouts to check whether they are compatible
/// with another layout.
///
/// The trait is automatically implemented for all type that implement `RenderPassLayout`.
// TODO: once specialization lands, this trait can be specialized for pairs that are known to
// always be compatible
// TODO: maybe this can be unimplemented on some pairs, to provide compile-time checks?
pub unsafe trait CompatibleLayout<Other>: RenderPassLayout where Other: RenderPassLayout {
/// Returns `true` if this layout is compatible with the other layout, as defined in the
/// `Render Pass Compatibility` section of the Vulkan specs.
fn is_compatible_with(&self, other: &Other) -> bool;
}
unsafe impl<A, B> CompatibleLayout<B> for A
where A: RenderPassLayout, B: RenderPassLayout
{
fn is_compatible_with(&self, other: &B) -> bool {
for (atch1, atch2) in self.attachments().zip(other.attachments()) {
if !atch1.is_compatible_with(&atch2) {
return false;
}
}
return true;
// FIXME: finish
}
}
/// Describes a uniform value that will be used to fill an attachment at the start of the /// Describes a uniform value that will be used to fill an attachment at the start of the
/// renderpass. /// renderpass.
// TODO: should have the same layout as `vk::ClearValue` for performances // TODO: should have the same layout as `vk::ClearValue` for performances
@ -96,6 +137,41 @@ pub struct AttachmentDescription {
pub final_layout: ImageLayout, pub final_layout: ImageLayout,
} }
impl AttachmentDescription {
/// Returns true if this attachment is compatible with another attachment, as defined in the
/// `Render Pass Compatibility` section of the Vulkan specs.
#[inline]
pub fn is_compatible_with(&self, other: &AttachmentDescription) -> bool {
self.format == other.format && self.samples == other.samples
}
}
pub struct PassDescription {
/// Indices of attachments to use as color attachments.
pub color_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow
pub depth_stencil: Option<(usize, ImageLayout)>,
/// Indices of attachments to use as input attachments.
pub input_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow
/// If not empty, each color attachment will be resolved into each corresponding entry of
/// this list.
///
/// If this value is not empty, it **must** be the same length as `color_attachments`.
pub resolve_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow
/// Indices of attachments that will be preserved during this pass.
pub preserve_attachments: Vec<usize>, // TODO: Vec is slow
}
// FIXME: finish
pub struct PassDependencyDescription {
pub source_subpass: usize,
pub destination_subpass: usize,
/*VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;*/
pub by_region: bool,
}
/// Builds a `RenderPass` object. /// Builds a `RenderPass` object.
#[macro_export] #[macro_export]
macro_rules! renderpass { macro_rules! renderpass {
@ -218,6 +294,8 @@ impl<L> RenderPass<L> where L: RenderPassLayout {
pub fn new(device: &Arc<Device>, layout: L) -> Result<Arc<RenderPass<L>>, OomError> { pub fn new(device: &Arc<Device>, layout: L) -> Result<Arc<RenderPass<L>>, OomError> {
let vk = device.pointers(); let vk = device.pointers();
// TODO: check the validity of the renderpass layout with debug_assert!
// TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618) // TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618)
let attachments = layout.attachments().map(|attachment| { let attachments = layout.attachments().map(|attachment| {
vk::AttachmentDescription { vk::AttachmentDescription {
@ -233,28 +311,91 @@ impl<L> RenderPass<L> where L: RenderPassLayout {
} }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
// FIXME: totally hacky
// TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618) // TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618)
let color_attachment_references = layout.attachments().map(|attachment| { let attachment_references = layout.passes().flat_map(|pass| {
vk::AttachmentReference { debug_assert!(pass.resolve_attachments.is_empty() ||
attachment: 0, pass.resolve_attachments.len() == pass.color_attachments.len());
layout: vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, let resolve = pass.resolve_attachments.into_iter().map(|(offset, img_la)| {
} debug_assert!(offset < layout.attachments().len());
vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
});
let color = pass.color_attachments.into_iter().map(|(offset, img_la)| {
debug_assert!(offset < layout.attachments().len());
vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
});
let input = pass.input_attachments.into_iter().map(|(offset, img_la)| {
debug_assert!(offset < layout.attachments().len());
vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
});
let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil {
Some(vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, })
} else {
None
}.into_iter();
color.chain(input).chain(resolve).chain(depthstencil)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
// TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618) // TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618)
let passes = (0 .. 1).map(|_| { let preserve_attachments_references = layout.passes().flat_map(|pass| {
pass.preserve_attachments.into_iter().map(|offset| offset as u32)
}).collect::<Vec<_>>();
// TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618)
let mut ref_index = 0usize;
let mut preserve_ref_index = 0usize;
let passes = layout.passes().map(|pass| {
unsafe {
let color_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.color_attachments.len();
let input_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.input_attachments.len();
let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.resolve_attachments.len();
let depth_stencil = if pass.depth_stencil.is_some() {
let a = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += 1;
a
} else {
ptr::null()
};
let preserve_attachments = preserve_attachments_references.as_ptr().offset(preserve_ref_index as isize);
preserve_ref_index += pass.preserve_attachments.len();
vk::SubpassDescription { vk::SubpassDescription {
flags: 0, // reserved flags: 0, // reserved
pipelineBindPoint: vk::PIPELINE_BIND_POINT_GRAPHICS, pipelineBindPoint: vk::PIPELINE_BIND_POINT_GRAPHICS,
inputAttachmentCount: 0, // FIXME: inputAttachmentCount: pass.input_attachments.len() as u32,
pInputAttachments: ptr::null(), // FIXME: pInputAttachments: input_attachments,
colorAttachmentCount: color_attachment_references.len() as u32, // FIXME: colorAttachmentCount: pass.input_attachments.len() as u32,
pColorAttachments: color_attachment_references.as_ptr(), // FIXME: pColorAttachments: color_attachments,
pResolveAttachments: ptr::null(), // FIXME: pResolveAttachments: if pass.resolve_attachments.len() == 0 { ptr::null() } else { resolve_attachments },
pDepthStencilAttachment: ptr::null(), // FIXME: pDepthStencilAttachment: ptr::null(), // FIXME:
preserveAttachmentCount: 0, // FIXME: preserveAttachmentCount: pass.preserve_attachments.len() as u32,
pPreserveAttachments: ptr::null(), // FIXME: pPreserveAttachments: preserve_attachments,
}
}
}).collect::<Vec<_>>();
debug_assert!(ref_index == attachment_references.len());
debug_assert!(preserve_ref_index == preserve_attachments_references.len());
// TODO: allocate on stack instead (https://github.com/rust-lang/rfcs/issues/618)
let dependencies = layout.pass_dependencies().map(|dependency| {
debug_assert!(dependency.source_subpass < layout.passes().len());
debug_assert!(dependency.destination_subpass < layout.passes().len());
vk::SubpassDependency {
srcSubpass: dependency.source_subpass as u32,
dstSubpass: dependency.destination_subpass as u32,
srcStageMask: vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, // FIXME:
dstStageMask: vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, // FIXME:
srcAccessMask: vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // FIXME:
dstAccessMask: vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // FIXME:
dependencyFlags: if dependency.by_region { vk::DEPENDENCY_BY_REGION_BIT } else { 0 },
} }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
@ -267,8 +408,8 @@ impl<L> RenderPass<L> where L: RenderPassLayout {
pAttachments: attachments.as_ptr(), pAttachments: attachments.as_ptr(),
subpassCount: passes.len() as u32, subpassCount: passes.len() as u32,
pSubpasses: passes.as_ptr(), pSubpasses: passes.as_ptr(),
dependencyCount: 0, // FIXME: dependencyCount: dependencies.len() as u32,
pDependencies: ptr::null(), // FIXME: pDependencies: dependencies.as_ptr(),
}; };
let mut output = mem::uninitialized(); let mut output = mem::uninitialized();
@ -315,8 +456,11 @@ impl<L> RenderPass<L> where L: RenderPassLayout {
/// ///
/// This means that framebuffers created with this renderpass can also be used alongside with /// This means that framebuffers created with this renderpass can also be used alongside with
/// the other renderpass. /// the other renderpass.
pub fn is_compatible_with<R2>(&self, other: &RenderPass<R2>) -> bool { #[inline]
true // FIXME: pub fn is_compatible_with<R2>(&self, other: &RenderPass<R2>) -> bool
where R2: RenderPassLayout
{
self.layout.is_compatible_with(&other.layout)
} }
/// Returns the layout used to create this renderpass. /// Returns the layout used to create this renderpass.