Stop using render pass boundary usages for resource transitions

This commit is contained in:
Dzmitry Malyshau
2021-07-09 13:17:28 -04:00
parent 12bc3eb3fc
commit 00859b9e0c
10 changed files with 98 additions and 212 deletions

View File

@@ -15,6 +15,7 @@ use crate::{
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
pipeline::PipelineFlags,
resource::{Texture, TextureView, TextureViewSource},
swap_chain::SwapChain,
track::{StatefulTrackerSubset, TextureSelector, UsageConflict},
validation::{
check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
@@ -491,11 +492,11 @@ where
struct RenderAttachment<'a> {
texture_id: &'a Stored<id::TextureId>,
selector: &'a TextureSelector,
previous_use: Option<hal::TextureUses>,
new_use: hal::TextureUses,
usage: hal::TextureUses,
}
type AttachmentDataVec<T> = ArrayVec<T, { hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1 }>;
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1;
type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
struct RenderPassInfo<'a, A: hal::Api> {
context: RenderPassContext,
@@ -514,6 +515,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
cmd_buf: &mut CommandBuffer<A>,
view_guard: &'a Storage<TextureView<A>, id::TextureViewId>,
swap_chain_guard: &'a Storage<SwapChain<A>, id::SwapChainId>,
) -> Result<Self, RenderPassErrorInner> {
profiling::scope!("start", "RenderPassInfo");
@@ -527,7 +529,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
let mut attachment_type_name = "";
let mut extent = None;
let mut sample_count = 0;
let mut used_swap_chain = None::<Stored<id::SwapChainId>>;
let mut used_swap_chain = None::<(Stored<id::SwapChainId>, hal::TextureUses)>;
let mut add_view = |view: &TextureView<A>, type_name| {
if let Some(ex) = extent {
@@ -577,12 +579,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
}
};
// Using render pass for transition.
let previous_use = cmd_buf
.trackers
.textures
.query(source_id.value, view.selector.clone());
let new_use = if at.is_read_only(ds_aspects)? {
let usage = if at.is_read_only(ds_aspects)? {
is_ds_read_only = true;
hal::TextureUses::DEPTH_STENCIL_READ | hal::TextureUses::SAMPLED
} else {
@@ -591,16 +588,13 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
render_attachments.push(RenderAttachment {
texture_id: source_id,
selector: &view.selector,
previous_use,
new_use,
usage,
});
let old_use = previous_use.unwrap_or(new_use);
depth_stencil = Some(hal::DepthStencilAttachment {
target: hal::Attachment {
view: &view.raw,
usage: new_use,
boundary_usage: old_use..new_use,
usage,
},
depth_ops: at.depth.hal_ops(),
stencil_ops: at.stencil.hal_ops(),
@@ -626,33 +620,22 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
));
}
let boundary_usage = match color_view.source {
match color_view.source {
TextureViewSource::Native(ref source_id) => {
let previous_use = cmd_buf
.trackers
.textures
.query(source_id.value, color_view.selector.clone());
let new_use = hal::TextureUses::COLOR_TARGET;
render_attachments.push(RenderAttachment {
texture_id: source_id,
selector: &color_view.selector,
previous_use,
new_use,
usage: hal::TextureUses::COLOR_TARGET,
});
let old_use = previous_use.unwrap_or(new_use);
old_use..new_use
}
TextureViewSource::SwapChain(ref source_id) => {
assert!(used_swap_chain.is_none());
used_swap_chain = Some(source_id.clone());
let end = hal::TextureUses::empty();
let start = match at.channel.load_op {
//HACK: guess the start usage based on the load op
let start_usage = match at.channel.load_op {
LoadOp::Load => hal::TextureUses::empty(),
LoadOp::Clear => hal::TextureUses::UNINITIALIZED,
LoadOp::Load => end,
};
start..end
assert!(used_swap_chain.is_none());
used_swap_chain = Some((source_id.clone(), start_usage));
}
};
@@ -676,34 +659,25 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
return Err(RenderPassErrorInner::InvalidResolveTargetSampleCount);
}
let boundary_usage = match resolve_view.source {
match resolve_view.source {
TextureViewSource::Native(ref source_id) => {
let previous_use = cmd_buf
.trackers
.textures
.query(source_id.value, resolve_view.selector.clone());
let new_use = hal::TextureUses::COLOR_TARGET;
render_attachments.push(RenderAttachment {
texture_id: source_id,
selector: &resolve_view.selector,
previous_use,
new_use,
usage: hal::TextureUses::COLOR_TARGET,
});
let old_use = previous_use.unwrap_or(new_use);
old_use..new_use
}
TextureViewSource::SwapChain(ref source_id) => {
//HACK: guess the start usage
let start_usage = hal::TextureUses::UNINITIALIZED;
assert!(used_swap_chain.is_none());
used_swap_chain = Some(source_id.clone());
hal::TextureUses::UNINITIALIZED..hal::TextureUses::empty()
used_swap_chain = Some((source_id.clone(), start_usage));
}
};
hal_resolve_target = Some(hal::Attachment {
view: &resolve_view.raw,
usage: hal::TextureUses::COLOR_TARGET,
boundary_usage,
});
}
@@ -711,7 +685,6 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
target: hal::Attachment {
view: &color_view.raw,
usage: hal::TextureUses::COLOR_TARGET,
boundary_usage,
},
resolve_target: hal_resolve_target,
ops: at.channel.hal_ops(),
@@ -723,6 +696,21 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
return Err(RenderPassErrorInner::InvalidSampleCount(sample_count));
}
if let Some((ref sc_id, start_usage)) = used_swap_chain {
let &(_, ref suf_texture) = swap_chain_guard[sc_id.value]
.acquired_texture
.as_ref()
.unwrap();
let barrier = hal::TextureBarrier {
texture: std::borrow::Borrow::borrow(suf_texture),
usage: start_usage..hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
};
unsafe {
cmd_buf.encoder.raw.transition_textures(iter::once(barrier));
}
}
let view_data = AttachmentData {
colors: color_attachments
.iter()
@@ -756,7 +744,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
context,
trackers: StatefulTrackerSubset::new(A::VARIANT),
render_attachments,
used_swap_chain,
used_swap_chain: used_swap_chain.map(|(sc_id, _)| sc_id),
is_ds_read_only,
extent,
_phantom: PhantomData,
@@ -767,9 +755,28 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
mut self,
raw: &mut A::CommandEncoder,
texture_guard: &Storage<Texture<A>, id::TextureId>,
swap_chain_guard: &Storage<SwapChain<A>, id::SwapChainId>,
) -> Result<(StatefulTrackerSubset, Option<Stored<id::SwapChainId>>), RenderPassErrorInner>
{
profiling::scope!("finish", "RenderPassInfo");
unsafe {
raw.end_render_pass();
}
if let Some(ref sc_id) = self.used_swap_chain {
let &(_, ref suf_texture) = swap_chain_guard[sc_id.value]
.acquired_texture
.as_ref()
.unwrap();
let barrier = hal::TextureBarrier {
texture: std::borrow::Borrow::borrow(suf_texture),
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(),
range: wgt::ImageSubresourceRange::default(),
};
unsafe {
raw.transition_textures(iter::once(barrier));
}
}
for ra in self.render_attachments {
let texture = &texture_guard[ra.texture_id.value];
@@ -782,29 +789,11 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
ra.texture_id.value,
&ra.texture_id.ref_count,
ra.selector.clone(),
ra.new_use,
ra.usage,
)
.map_err(UsageConflict::from)?;
if let Some(usage) = ra.previous_use {
// Make the attachment tracks to be aware of the internal
// transition done by the render pass, by registering the
// previous usage as the initial state.
self.trackers
.textures
.prepend(
ra.texture_id.value,
&ra.texture_id.ref_count,
ra.selector.clone(),
usage,
)
.unwrap();
}
}
unsafe {
raw.end_render_pass();
}
Ok((self.trackers, self.used_swap_chain))
}
}
@@ -842,7 +831,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (device_guard, mut token) = hub.devices.read(&mut token);
let (pass_raw, trackers, query_reset_state) = {
// read-only lock guard
let (swap_chain_guard, mut token) = hub.swap_chains.read(&mut token);
let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token);
let cmd_buf =
@@ -886,6 +875,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
depth_stencil_attachment,
cmd_buf,
&*view_guard,
&*swap_chain_guard,
)
.map_pass_err(scope)?;
@@ -1744,8 +1734,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
log::trace!("Merging {:?} with the render pass", encoder_id);
let (trackers, used_swapchain) =
info.finish(raw, &*texture_guard).map_pass_err(scope)?;
let (trackers, used_swapchain) = info
.finish(raw, &*texture_guard, &*swap_chain_guard)
.map_pass_err(scope)?;
let raw_cmd_buf = unsafe {
raw.end_encoding()
.map_err(|_| RenderPassErrorInner::OutOfMemory)

View File

@@ -75,25 +75,6 @@ impl ResourceState for BufferState {
Ok(())
}
fn prepend(
&mut self,
id: Valid<Self::Id>,
_selector: Self::Selector,
usage: Self::Usage,
) -> Result<(), PendingTransition<Self>> {
match self.first {
Some(old) if old != usage => Err(PendingTransition {
id,
selector: (),
usage: old..usage,
}),
_ => {
self.first = Some(usage);
Ok(())
}
}
}
fn merge(
&mut self,
id: Valid<Self::Id>,
@@ -205,30 +186,4 @@ mod test {
}
);
}
#[test]
fn prepend() {
let mut bs = Unit {
first: None,
last: BufferUses::VERTEX,
};
let id = Id::dummy();
bs.prepend(id, (), BufferUses::INDEX).unwrap();
bs.prepend(id, (), BufferUses::INDEX).unwrap();
assert_eq!(
bs.prepend(id, (), BufferUses::STORAGE_LOAD),
Err(PendingTransition {
id,
selector: (),
usage: BufferUses::INDEX..BufferUses::STORAGE_LOAD,
})
);
assert_eq!(
bs,
Unit {
first: Some(BufferUses::INDEX),
last: BufferUses::VERTEX,
}
);
}
}

View File

@@ -76,14 +76,6 @@ pub(crate) trait ResourceState: Clone + Default {
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>>;
/// Sets up the first usage of the selected sub-resources.
fn prepend(
&mut self,
id: Valid<Self::Id>,
selector: Self::Selector,
usage: Self::Usage,
) -> Result<(), PendingTransition<Self>>;
/// Merge the state of this resource tracked by a different instance
/// with the current one.
///
@@ -309,6 +301,7 @@ impl<S: ResourceState> ResourceTracker<S> {
///
/// Returns `Some(Usage)` only if this usage is consistent
/// across the given selector.
#[allow(unused)] // TODO: figure out if this needs to be removed
pub fn query(&self, id: Valid<S::Id>, selector: S::Selector) -> Option<S::Usage> {
let (index, epoch, backend) = id.0.unzip();
debug_assert_eq!(backend, self.backend);
@@ -397,21 +390,6 @@ impl<S: ResourceState> ResourceTracker<S> {
self.temp.drain(..)
}
/// Turn the tracking from the "expand" mode into the "replace" one,
/// installing the selected usage as the "first".
/// This is a special operation only used by the render pass attachments.
pub(crate) fn prepend(
&mut self,
id: Valid<S::Id>,
ref_count: &RefCount,
selector: S::Selector,
usage: S::Usage,
) -> Result<(), PendingTransition<S>> {
Self::get_or_insert(self.backend, &mut self.map, id, ref_count)
.state
.prepend(id, selector, usage)
}
/// Merge another tracker into `self` by extending the current states
/// without any transitions.
pub(crate) fn merge_extend(&mut self, other: &Self) -> Result<(), PendingTransition<S>> {
@@ -528,15 +506,6 @@ impl<I: Copy + fmt::Debug + TypedId> ResourceState for PhantomData<I> {
Ok(())
}
fn prepend(
&mut self,
_id: Valid<Self::Id>,
_selector: Self::Selector,
_usage: Self::Usage,
) -> Result<(), PendingTransition<Self>> {
Ok(())
}
fn merge(
&mut self,
_id: Valid<Self::Id>,

View File

@@ -136,40 +136,6 @@ impl ResourceState for TextureState {
Ok(())
}
fn prepend(
&mut self,
id: Valid<Self::Id>,
selector: Self::Selector,
usage: Self::Usage,
) -> Result<(), PendingTransition<Self>> {
assert!(self.mips.len() >= selector.levels.end as usize);
for (mip_id, mip) in self.mips[selector.levels.start as usize..selector.levels.end as usize]
.iter_mut()
.enumerate()
{
let level = selector.levels.start + mip_id as u32;
let layers = mip.isolate(&selector.layers, Unit::new(usage));
for &mut (ref range, ref mut unit) in layers {
match unit.first {
Some(old) if old != usage => {
return Err(PendingTransition {
id,
selector: TextureSelector {
levels: level..level + 1,
layers: range.clone(),
},
usage: old..usage,
});
}
_ => {
unit.first = Some(usage);
}
}
}
}
Ok(())
}
fn merge(
&mut self,
id: Valid<Self::Id>,

View File

@@ -593,11 +593,18 @@ impl<A: hal::Api> Example<A> {
let ctx = &mut self.contexts[self.context_index];
let surface_tex = unsafe { self.surface.acquire_texture(!0).unwrap().unwrap().texture };
let target_barrier0 = hal::TextureBarrier {
texture: surface_tex.borrow(),
range: wgt::ImageSubresourceRange::default(),
usage: hal::TextureUses::UNINITIALIZED..hal::TextureUses::COLOR_TARGET,
};
unsafe {
ctx.encoder.begin_encoding(Some("frame")).unwrap();
ctx.encoder.transition_textures(iter::once(target_barrier0));
}
let surface_tex = unsafe { self.surface.acquire_texture(!0).unwrap().unwrap().texture };
let surface_view_desc = hal::TextureViewDescriptor {
label: None,
format: self.surface_format,
@@ -622,7 +629,6 @@ impl<A: hal::Api> Example<A> {
target: hal::Attachment {
view: &surface_tex_view,
usage: hal::TextureUses::COLOR_TARGET,
boundary_usage: hal::TextureUses::UNINITIALIZED..hal::TextureUses::empty(),
},
resolve_target: None,
ops: hal::AttachmentOps::STORE,
@@ -655,8 +661,17 @@ impl<A: hal::Api> Example<A> {
ctx.frames_recorded += 1;
let do_fence = ctx.frames_recorded > COMMAND_BUFFER_PER_CONTEXT;
let target_barrier1 = hal::TextureBarrier {
texture: surface_tex.borrow(),
range: wgt::ImageSubresourceRange::default(),
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(),
};
unsafe {
ctx.encoder.end_render_pass();
ctx.encoder.transition_textures(iter::once(target_barrier1));
}
unsafe {
let cmd_buf = ctx.encoder.end_encoding().unwrap();
let fence_param = if do_fence {
Some((&mut ctx.fence, ctx.fence_value))

View File

@@ -349,7 +349,7 @@ pub struct PipelineLayout {
total_slots: u32,
// Storing for each associated bind group, which tables we created
// in the root signature. This is required for binding descriptor sets.
elements: arrayvec::ArrayVec<[RootElement; crate::MAX_BIND_GROUPS]>,
elements: arrayvec::ArrayVec<RootElement, crate::MAX_BIND_GROUPS>,
}
unsafe impl Send for PipelineLayout {}

View File

@@ -1044,13 +1044,9 @@ pub struct BufferTextureCopy {
#[derive(Debug)]
pub struct Attachment<'a, A: Api> {
pub view: &'a A::TextureView,
/// Contains either a single mutating usage as a target, or a valid combination
/// of read-only usages.
/// Contains either a single mutating usage as a target,
/// or a valid combination of read-only usages.
pub usage: TextureUses,
/// Defines the boundary usages for the attachment.
/// It is expected to begin a render pass with `boundary_usage.start` usage,
/// and will end it with `boundary_usage.end` usage.
pub boundary_usage: Range<TextureUses>,
}
// Rust gets confused about the impl requirements for `A`
@@ -1059,7 +1055,6 @@ impl<A: Api> Clone for Attachment<'_, A> {
Self {
view: self.view,
usage: self.usage,
boundary_usage: self.boundary_usage.clone(),
}
}
}

View File

@@ -119,9 +119,7 @@ impl crate::Attachment<'_, super::Api> {
let aspects = self.view.aspects();
super::AttachmentKey {
format: caps.map_texture_format(self.view.attachment.view_format),
layout_pre: derive_image_layout(self.boundary_usage.start, aspects),
layout_in: derive_image_layout(self.usage, aspects),
layout_post: derive_image_layout(self.boundary_usage.end, aspects),
layout: derive_image_layout(self.usage, aspects),
ops,
}
}
@@ -250,7 +248,7 @@ pub fn map_texture_usage_to_barrier(
access |= vk::AccessFlags::SHADER_WRITE;
}
if usage == crate::TextureUses::UNINITIALIZED {
if usage == crate::TextureUses::UNINITIALIZED || usage.is_empty() {
(
vk::PipelineStageFlags::TOP_OF_PIPE,
vk::AccessFlags::empty(),

View File

@@ -74,7 +74,7 @@ impl super::DeviceShared {
for cat in e.key().colors.iter() {
color_refs.push(vk::AttachmentReference {
attachment: vk_attachments.len() as u32,
layout: cat.base.layout_in,
layout: cat.base.layout,
});
vk_attachments.push({
let (load_op, store_op) = conv::map_attachment_ops(cat.base.ops);
@@ -83,14 +83,14 @@ impl super::DeviceShared {
.samples(samples)
.load_op(load_op)
.store_op(store_op)
.initial_layout(cat.base.layout_pre)
.final_layout(cat.base.layout_post)
.initial_layout(cat.base.layout)
.final_layout(cat.base.layout)
.build()
});
let at_ref = if let Some(ref rat) = cat.resolve {
let at_ref = vk::AttachmentReference {
attachment: vk_attachments.len() as u32,
layout: rat.layout_in,
layout: rat.layout,
};
let (load_op, store_op) = conv::map_attachment_ops(rat.ops);
let vk_attachment = vk::AttachmentDescription::builder()
@@ -98,8 +98,8 @@ impl super::DeviceShared {
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(load_op)
.store_op(store_op)
.initial_layout(rat.layout_pre)
.final_layout(rat.layout_post)
.initial_layout(rat.layout)
.final_layout(rat.layout)
.build();
vk_attachments.push(vk_attachment);
at_ref
@@ -115,7 +115,7 @@ impl super::DeviceShared {
if let Some(ref ds) = e.key().depth_stencil {
ds_ref = Some(vk::AttachmentReference {
attachment: vk_attachments.len() as u32,
layout: ds.base.layout_in,
layout: ds.base.layout,
});
let (load_op, store_op) = conv::map_attachment_ops(ds.base.ops);
let (stencil_load_op, stencil_store_op) =
@@ -127,8 +127,8 @@ impl super::DeviceShared {
.store_op(store_op)
.stencil_load_op(stencil_load_op)
.stencil_store_op(stencil_store_op)
.initial_layout(ds.base.layout_pre)
.final_layout(ds.base.layout_post)
.initial_layout(ds.base.layout)
.final_layout(ds.base.layout)
.build();
vk_attachments.push(vk_attachment);
}

View File

@@ -162,20 +162,16 @@ struct PrivateCapabilities {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct AttachmentKey {
format: vk::Format,
layout_pre: vk::ImageLayout,
layout_in: vk::ImageLayout,
layout_post: vk::ImageLayout,
layout: vk::ImageLayout,
ops: crate::AttachmentOps,
}
impl AttachmentKey {
/// Returns an attachment key for a compatible attachment.
fn compatible(format: vk::Format, layout_in: vk::ImageLayout) -> Self {
fn compatible(format: vk::Format, layout: vk::ImageLayout) -> Self {
Self {
format,
layout_pre: vk::ImageLayout::GENERAL,
layout_in,
layout_post: vk::ImageLayout::GENERAL,
layout,
ops: crate::AttachmentOps::all(),
}
}