Implement ContextCheckLayer

This commit is contained in:
Pierre Krieger 2017-04-17 11:46:27 +02:00
parent a1b65b3845
commit 097682bf9d
2 changed files with 162 additions and 30 deletions

View File

@ -51,7 +51,7 @@ impl AutoCommandBufferBuilder<Arc<StandardCommandPool>> {
let c = cb::AutoPipelineBarriersLayer::new(c);
let c = cb::SubmitSyncBuilderLayer::new(c);
let c = cb::StateCacheLayer::new(c);
let c = cb::ContextCheckLayer::new(c);
let c = cb::ContextCheckLayer::new(c, false, true);
let c = cb::QueueTyCheckLayer::new(c, supports_graphics, supports_compute);
let c = cb::DeviceCheckLayer::new(c);
c

View File

@ -16,20 +16,45 @@ use device::Device;
use device::DeviceOwned;
/// Layer around a command buffer builder that checks whether the commands can be executed in the
/// given context.
/// given context related to render passes.
///
/// What is checked exactly:
///
/// - When adding a command that can only be executed within a render pass or outside of a render
/// pass, checks that we are within or outside of a render pass.
/// - When leaving the render pass or going to the next subpass, makes sure that the number of
/// subpasses of the current render pass is respected.
/// - When binding a graphics pipeline or drawing, makes sure that the pipeline is valid for the
/// current render pass.
///
/// "The given context" here means being inside/outside a render pass or a secondary command
/// buffer.
pub struct ContextCheckLayer<I> {
// Inner command buffer builder.
inner: I,
// True if we are currently inside a render pass.
inside_render_pass: bool,
// True if entering/leaving a render pass or going to the next subpass is allowed.
allow_render_pass_ops: bool,
}
impl<I> ContextCheckLayer<I> {
/// Builds a new `ContextCheckLayer`.
///
/// If `allow_render_pass_ops` is true, then entering/leaving a render pass or going to the
/// next subpass is allowed by the layer.
///
/// If `inside_render_pass` is true, then the builder is currently inside a render pass.
///
/// Note that this layer will only protect you if you pass correct values in this constructor.
/// It is not unsafe to pass wrong values, but if you do so then the layer will be inefficient
/// as a safety tool.
#[inline]
pub fn new(inner: I) -> ContextCheckLayer<I> {
pub fn new(inner: I, inside_render_pass: bool, allow_render_pass_ops: bool)
-> ContextCheckLayer<I>
{
ContextCheckLayer {
inner: inner,
inside_render_pass: inside_render_pass,
allow_render_pass_ops: allow_render_pass_ops,
}
}
@ -75,9 +100,18 @@ unsafe impl<I> CommandBufferBuilder for ContextCheckLayer<I>
}
}
// TODO: actually implement
// TODO:
// impl!((C), commands_raw::CmdExecuteCommands<C>);
macro_rules! pass_through {
// FIXME: must also check that a pipeline's render pass matches the render pass
// FIXME:
// > If the variable multisample rate feature is not supported, pipeline is a graphics pipeline,
// > the current subpass has no attachments, and this is not the first call to this function with
// > a graphics pipeline after transitioning to the current subpass, then the sample count
// > specified by this pipeline must match that set in the previous pipeline
macro_rules! impl_always {
(($($param:ident),*), $cmd:ty) => {
unsafe impl<'a, I, O $(, $param)*> AddCommand<$cmd> for ContextCheckLayer<I>
where I: AddCommand<$cmd, Out = O>
@ -88,32 +122,130 @@ macro_rules! pass_through {
fn add(self, command: $cmd) -> Self::Out {
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: self.inside_render_pass,
allow_render_pass_ops: self.allow_render_pass_ops,
}
}
}
}
}
pass_through!((Rp, F), commands_raw::CmdBeginRenderPass<Rp, F>);
pass_through!((S, Pl), commands_raw::CmdBindDescriptorSets<S, Pl>);
pass_through!((B), commands_raw::CmdBindIndexBuffer<B>);
pass_through!((Pl), commands_raw::CmdBindPipeline<Pl>);
pass_through!((V), commands_raw::CmdBindVertexBuffers<V>);
pass_through!((S, D), commands_raw::CmdBlitImage<S, D>);
pass_through!((), commands_raw::CmdClearAttachments);
pass_through!((S, D), commands_raw::CmdCopyBuffer<S, D>);
pass_through!((S, D), commands_raw::CmdCopyBufferToImage<S, D>);
pass_through!((S, D), commands_raw::CmdCopyImage<S, D>);
pass_through!((), commands_raw::CmdDispatchRaw);
pass_through!((), commands_raw::CmdDrawIndexedRaw);
pass_through!((B), commands_raw::CmdDrawIndirectRaw<B>);
pass_through!((), commands_raw::CmdDrawRaw);
pass_through!((), commands_raw::CmdEndRenderPass);
pass_through!((C), commands_raw::CmdExecuteCommands<C>);
pass_through!((B), commands_raw::CmdFillBuffer<B>);
pass_through!((), commands_raw::CmdNextSubpass);
pass_through!((Pc, Pl), commands_raw::CmdPushConstants<Pc, Pl>);
pass_through!((S, D), commands_raw::CmdResolveImage<S, D>);
pass_through!((), commands_raw::CmdSetEvent);
pass_through!((), commands_raw::CmdSetState);
pass_through!((B, D), commands_raw::CmdUpdateBuffer<B, D>);
impl_always!((S, Pl), commands_raw::CmdBindDescriptorSets<S, Pl>);
impl_always!((B), commands_raw::CmdBindIndexBuffer<B>);
impl_always!((Pl), commands_raw::CmdBindPipeline<Pl>);
impl_always!((V), commands_raw::CmdBindVertexBuffers<V>);
impl_always!((Pc, Pl), commands_raw::CmdPushConstants<Pc, Pl>);
macro_rules! impl_inside_only {
(($($param:ident),*), $cmd:ty) => {
unsafe impl<'a, I, O $(, $param)*> AddCommand<$cmd> for ContextCheckLayer<I>
where I: AddCommand<$cmd, Out = O>
{
type Out = ContextCheckLayer<O>;
#[inline]
fn add(self, command: $cmd) -> Self::Out {
assert!(self.inside_render_pass); // TODO: proper error
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: self.inside_render_pass,
allow_render_pass_ops: self.allow_render_pass_ops,
}
}
}
}
}
impl_inside_only!((), commands_raw::CmdClearAttachments);
impl_inside_only!((), commands_raw::CmdDrawIndexedRaw);
impl_inside_only!((B), commands_raw::CmdDrawIndirectRaw<B>);
impl_inside_only!((), commands_raw::CmdDrawRaw);
macro_rules! impl_outside_only {
(($($param:ident),*), $cmd:ty) => {
unsafe impl<'a, I, O $(, $param)*> AddCommand<$cmd> for ContextCheckLayer<I>
where I: AddCommand<$cmd, Out = O>
{
type Out = ContextCheckLayer<O>;
#[inline]
fn add(self, command: $cmd) -> Self::Out {
assert!(!self.inside_render_pass); // TODO: proper error
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: self.inside_render_pass,
allow_render_pass_ops: self.allow_render_pass_ops,
}
}
}
}
}
impl_outside_only!((S, D), commands_raw::CmdBlitImage<S, D>);
impl_outside_only!((S, D), commands_raw::CmdCopyBuffer<S, D>);
impl_outside_only!((S, D), commands_raw::CmdCopyBufferToImage<S, D>);
impl_outside_only!((S, D), commands_raw::CmdCopyImage<S, D>);
impl_outside_only!((), commands_raw::CmdDispatchRaw);
impl_outside_only!((B), commands_raw::CmdFillBuffer<B>);
impl_outside_only!((S, D), commands_raw::CmdResolveImage<S, D>);
impl_outside_only!((), commands_raw::CmdSetEvent);
impl_outside_only!((), commands_raw::CmdSetState);
impl_outside_only!((B, D), commands_raw::CmdUpdateBuffer<B, D>);
unsafe impl<'a, I, O, Rp, F> AddCommand<commands_raw::CmdBeginRenderPass<Rp, F>> for ContextCheckLayer<I>
where I: AddCommand<commands_raw::CmdBeginRenderPass<Rp, F>, Out = O>
{
type Out = ContextCheckLayer<O>;
#[inline]
fn add(self, command: commands_raw::CmdBeginRenderPass<Rp, F>) -> Self::Out {
assert!(!self.inside_render_pass); // TODO: proper error
assert!(self.allow_render_pass_ops); // TODO: proper error
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: true,
allow_render_pass_ops: true,
}
}
}
unsafe impl<'a, I, O> AddCommand<commands_raw::CmdNextSubpass> for ContextCheckLayer<I>
where I: AddCommand<commands_raw::CmdNextSubpass, Out = O>
{
type Out = ContextCheckLayer<O>;
#[inline]
fn add(self, command: commands_raw::CmdNextSubpass) -> Self::Out {
assert!(self.inside_render_pass); // TODO: proper error
assert!(self.allow_render_pass_ops); // TODO: proper error
// FIXME: check number of subpasses
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: true,
allow_render_pass_ops: true,
}
}
}
unsafe impl<'a, I, O> AddCommand<commands_raw::CmdEndRenderPass> for ContextCheckLayer<I>
where I: AddCommand<commands_raw::CmdEndRenderPass, Out = O>
{
type Out = ContextCheckLayer<O>;
#[inline]
fn add(self, command: commands_raw::CmdEndRenderPass) -> Self::Out {
assert!(self.inside_render_pass); // TODO: proper error
assert!(self.allow_render_pass_ops); // TODO: proper error
// FIXME: check number of subpasses
ContextCheckLayer {
inner: self.inner.add(command),
inside_render_pass: false,
allow_render_pass_ops: true,
}
}
}