mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 14:55:05 +00:00
Merge #412
412: Another big tracking refactor r=later a=kvark
Fixes #409
Note: there is way more code that had to be thrown out in the process of this PR than there is in the PR itself.
Also fixes [this comment](04e17b3f4f/wgpu-core/src/track/texture.rs (L29)
):
> //TODO: make this less awkward!
## Logic
Instead of having a run-time per-operation control over what state is being used for stitching (with `Stitch` enum), we are now deriving this automatically on a per-subresource level. Whenever we are detecting a transition for a sub-resource, and we know there wasn't any "first" state associated with it in the current tracker, we are saving this "first" state. Unlike previous code, where it was confusing what of `Unit` fields (`init`, `last`) are valid, now `first` is `Option<>`, so we know we should be using it if it's there.
This allows us to have this hybrid tracking state of a render pass, where all resources are immutable (and their usage is extended), except for the output attachments. This, together with a whole lot of refactoring, gets us #409.
I'm actually quite happy with the tracking code now. It's finally taking shape we aren't afraid to tell others about :)
Note: this was tested on wgpu-rs examples and vange-rs.
Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
commit
8f42ba6676
@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Generated with cbindgen:0.9.1 */
|
||||
/* Generated with cbindgen:0.11.1 */
|
||||
|
||||
/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
|
||||
* To generate this file:
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
hub::{GfxBackend, Global, IdentityFilter, Token},
|
||||
id::{BindGroupId, BufferId, CommandBufferId, ComputePassId, ComputePipelineId},
|
||||
resource::BufferUsage,
|
||||
track::{Stitch, TrackerSet},
|
||||
track::TrackerSet,
|
||||
BufferAddress,
|
||||
Stored,
|
||||
};
|
||||
@ -56,7 +56,7 @@ impl<F: IdentityFilter<ComputePassId>> Global<F> {
|
||||
|
||||
// There are no transitions to be made: we've already been inserting barriers
|
||||
// into the parent command buffer while recording this compute pass.
|
||||
log::debug!("Compute pass {:?} tracker: {:#?}", pass_id, pass.trackers);
|
||||
log::debug!("Compute pass {:?} {:#?}", pass_id, pass.trackers);
|
||||
cmb.trackers = pass.trackers;
|
||||
cmb.raw.push(pass.raw);
|
||||
}
|
||||
@ -112,7 +112,6 @@ impl<F> Global<F> {
|
||||
&mut pass.raw,
|
||||
&mut pass.trackers,
|
||||
&bind_group.used,
|
||||
Stitch::Last,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
);
|
||||
|
@ -34,7 +34,7 @@ use crate::{
|
||||
TextureViewId,
|
||||
},
|
||||
resource::{Buffer, Texture, TextureUsage, TextureViewInner},
|
||||
track::{Stitch, TrackerSet},
|
||||
track::TrackerSet,
|
||||
Color,
|
||||
Features,
|
||||
LifeGuard,
|
||||
@ -118,17 +118,15 @@ impl<B: GfxBackend> CommandBuffer<B> {
|
||||
raw: &mut B::CommandBuffer,
|
||||
base: &mut TrackerSet,
|
||||
head: &TrackerSet,
|
||||
stitch: Stitch,
|
||||
buffer_guard: &Storage<Buffer<B>, BufferId>,
|
||||
texture_guard: &Storage<Texture<B>, TextureId>,
|
||||
) {
|
||||
log::trace!("\tstitch {:?}", stitch);
|
||||
debug_assert_eq!(B::VARIANT, base.backend());
|
||||
debug_assert_eq!(B::VARIANT, head.backend());
|
||||
|
||||
let buffer_barriers = base
|
||||
.buffers
|
||||
.merge_replace(&head.buffers, stitch)
|
||||
.merge_replace(&head.buffers)
|
||||
.map(|pending| {
|
||||
log::trace!("\tbuffer -> {:?}", pending);
|
||||
hal::memory::Barrier::Buffer {
|
||||
@ -140,7 +138,7 @@ impl<B: GfxBackend> CommandBuffer<B> {
|
||||
});
|
||||
let texture_barriers = base
|
||||
.textures
|
||||
.merge_replace(&head.textures, stitch)
|
||||
.merge_replace(&head.textures)
|
||||
.map(|pending| {
|
||||
log::trace!("\ttexture -> {:?}", pending);
|
||||
hal::memory::Barrier::Image {
|
||||
@ -196,7 +194,7 @@ impl<F> Global<F> {
|
||||
if let Some((ref view_id, _)) = comb.used_swap_chain {
|
||||
comb.trackers.views.remove(view_id.value);
|
||||
}
|
||||
log::debug!("Command buffer {:?} tracker: {:#?}", encoder_id, comb.trackers);
|
||||
log::debug!("Command buffer {:?} {:#?}", encoder_id, comb.trackers);
|
||||
encoder_id
|
||||
}
|
||||
}
|
||||
@ -237,7 +235,6 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
let (view_guard, _) = hub.texture_views.read(&mut token);
|
||||
|
||||
let mut extent = None;
|
||||
let mut barriers = Vec::new();
|
||||
let mut used_swap_chain_image = None::<Stored<TextureViewId>>;
|
||||
|
||||
let color_attachments = unsafe {
|
||||
@ -254,77 +251,61 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
"Attachment sample_count must be supported by physical device limits"
|
||||
);
|
||||
|
||||
const MAX_TOTAL_ATTACHMENTS: usize = 10;
|
||||
type OutputAttachment<'a> = (TextureId, &'a hal::image::SubresourceRange, Option<TextureUsage>);
|
||||
let mut output_attachments = ArrayVec::<[OutputAttachment; MAX_TOTAL_ATTACHMENTS]>::new();
|
||||
|
||||
log::trace!(
|
||||
"Encoding render pass begin in command buffer {:?}",
|
||||
encoder_id
|
||||
);
|
||||
let rp_key = {
|
||||
let trackers = &mut cmb.trackers;
|
||||
|
||||
let depth_stencil = depth_stencil_attachment.map(|at| {
|
||||
let view = trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, at.attachment, (), ())
|
||||
.unwrap();
|
||||
if let Some(ex) = extent {
|
||||
assert_eq!(ex, view.extent);
|
||||
} else {
|
||||
extent = Some(view.extent);
|
||||
}
|
||||
let texture_id = match view.inner {
|
||||
TextureViewInner::Native { ref source_id, .. } => source_id.value,
|
||||
TextureViewInner::SwapChain { .. } => {
|
||||
panic!("Unexpected depth/stencil use of swapchain image!")
|
||||
let depth_stencil = match depth_stencil_attachment {
|
||||
Some(at) => {
|
||||
let view = cmb.trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, at.attachment, (), ())
|
||||
.unwrap();
|
||||
if let Some(ex) = extent {
|
||||
assert_eq!(ex, view.extent);
|
||||
} else {
|
||||
extent = Some(view.extent);
|
||||
}
|
||||
};
|
||||
let texture_id = match view.inner {
|
||||
TextureViewInner::Native { ref source_id, .. } => source_id.value,
|
||||
TextureViewInner::SwapChain { .. } => {
|
||||
panic!("Unexpected depth/stencil use of swapchain image!")
|
||||
}
|
||||
};
|
||||
|
||||
let texture = &texture_guard[texture_id];
|
||||
assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT));
|
||||
// Using render pass for transition.
|
||||
let consistent_usage = cmb.trackers.textures.query(
|
||||
texture_id,
|
||||
view.range.clone(),
|
||||
);
|
||||
output_attachments.push((texture_id, &view.range, consistent_usage));
|
||||
|
||||
let consistent_usage = trackers.textures.query(texture_id, view.range.clone());
|
||||
let pending = trackers.textures.change_replace(
|
||||
texture_id,
|
||||
&texture.life_guard.ref_count,
|
||||
view.range.clone(),
|
||||
TextureUsage::OUTPUT_ATTACHMENT,
|
||||
&texture.full_range,
|
||||
);
|
||||
|
||||
let old_layout = match consistent_usage {
|
||||
Some(usage) => {
|
||||
// Using render pass for transition.
|
||||
conv::map_texture_state(
|
||||
let old_layout = match consistent_usage {
|
||||
Some(usage) => conv::map_texture_state(
|
||||
usage,
|
||||
hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL,
|
||||
)
|
||||
.1
|
||||
}
|
||||
None => {
|
||||
// Required sub-resources have inconsistent states, we need to
|
||||
// issue individual barriers instead of relying on the render pass.
|
||||
barriers.extend(pending.map(|pending| {
|
||||
log::trace!("\tdepth-stencil {:?}", pending);
|
||||
hal::memory::Barrier::Image {
|
||||
states: pending.to_states(),
|
||||
target: &texture.raw,
|
||||
families: None,
|
||||
range: pending.selector,
|
||||
}
|
||||
}));
|
||||
hal::image::Layout::DepthStencilAttachmentOptimal
|
||||
}
|
||||
};
|
||||
hal::pass::Attachment {
|
||||
format: Some(conv::map_texture_format(view.format, device.features)),
|
||||
samples: view.samples,
|
||||
ops: conv::map_load_store_ops(at.depth_load_op, at.depth_store_op),
|
||||
stencil_ops: conv::map_load_store_ops(
|
||||
at.stencil_load_op,
|
||||
at.stencil_store_op,
|
||||
),
|
||||
layouts: old_layout .. hal::image::Layout::DepthStencilAttachmentOptimal,
|
||||
).1,
|
||||
None => hal::image::Layout::DepthStencilAttachmentOptimal,
|
||||
};
|
||||
|
||||
Some(hal::pass::Attachment {
|
||||
format: Some(conv::map_texture_format(view.format, device.features)),
|
||||
samples: view.samples,
|
||||
ops: conv::map_load_store_ops(at.depth_load_op, at.depth_store_op),
|
||||
stencil_ops: conv::map_load_store_ops(
|
||||
at.stencil_load_op,
|
||||
at.stencil_store_op,
|
||||
),
|
||||
layouts: old_layout .. hal::image::Layout::DepthStencilAttachmentOptimal,
|
||||
})
|
||||
}
|
||||
});
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut colors = ArrayVec::new();
|
||||
let mut resolves = ArrayVec::new();
|
||||
@ -340,49 +321,23 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
view.samples, sample_count,
|
||||
"All attachments must have the same sample_count"
|
||||
);
|
||||
let first_use = trackers.views.init(
|
||||
let first_use = cmb.trackers.views.init(
|
||||
at.attachment,
|
||||
view.life_guard.ref_count.clone(),
|
||||
(),
|
||||
(),
|
||||
);
|
||||
&(),
|
||||
).is_some();
|
||||
|
||||
let layouts = match view.inner {
|
||||
TextureViewInner::Native { ref source_id, .. } => {
|
||||
let texture = &texture_guard[source_id.value];
|
||||
assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT));
|
||||
|
||||
let consistent_usage = trackers.textures.query(
|
||||
let consistent_usage = cmb.trackers.textures.query(
|
||||
source_id.value,
|
||||
view.range.clone(),
|
||||
);
|
||||
let pending = trackers.textures.change_replace(
|
||||
source_id.value,
|
||||
&texture.life_guard.ref_count,
|
||||
view.range.clone(),
|
||||
TextureUsage::OUTPUT_ATTACHMENT,
|
||||
&texture.full_range,
|
||||
);
|
||||
output_attachments.push((source_id.value, &view.range, consistent_usage));
|
||||
|
||||
let old_layout = match consistent_usage {
|
||||
Some(usage) => {
|
||||
// Using render pass for transition.
|
||||
conv::map_texture_state(usage, hal::format::Aspects::COLOR).1
|
||||
}
|
||||
None => {
|
||||
// Required sub-resources have inconsistent states, we need to
|
||||
// issue individual barriers instead of relying on the render pass.
|
||||
barriers.extend(pending.map(|pending| {
|
||||
log::trace!("\tcolor {:?}", pending);
|
||||
hal::memory::Barrier::Image {
|
||||
states: pending.to_states(),
|
||||
target: &texture.raw,
|
||||
families: None,
|
||||
range: pending.selector,
|
||||
}
|
||||
}));
|
||||
hal::image::Layout::ColorAttachmentOptimal
|
||||
}
|
||||
Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1,
|
||||
None => hal::image::Layout::ColorAttachmentOptimal,
|
||||
};
|
||||
old_layout .. hal::image::Layout::ColorAttachmentOptimal
|
||||
}
|
||||
@ -426,49 +381,23 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
view.samples, 1,
|
||||
"All resolve_targets must have a sample_count of 1"
|
||||
);
|
||||
let first_use = trackers.views.init(
|
||||
let first_use = cmb.trackers.views.init(
|
||||
resolve_target,
|
||||
view.life_guard.ref_count.clone(),
|
||||
(),
|
||||
(),
|
||||
);
|
||||
&(),
|
||||
).is_some();
|
||||
|
||||
let layouts = match view.inner {
|
||||
TextureViewInner::Native { ref source_id, .. } => {
|
||||
let texture = &texture_guard[source_id.value];
|
||||
assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT));
|
||||
|
||||
let consistent_usage = trackers.textures.query(
|
||||
let consistent_usage = cmb.trackers.textures.query(
|
||||
source_id.value,
|
||||
view.range.clone(),
|
||||
);
|
||||
let pending = trackers.textures.change_replace(
|
||||
source_id.value,
|
||||
&texture.life_guard.ref_count,
|
||||
view.range.clone(),
|
||||
TextureUsage::OUTPUT_ATTACHMENT,
|
||||
&texture.full_range,
|
||||
);
|
||||
output_attachments.push((source_id.value, &view.range, consistent_usage));
|
||||
|
||||
let old_layout = match consistent_usage {
|
||||
Some(usage) => {
|
||||
// Using render pass for transition.
|
||||
conv::map_texture_state(usage, hal::format::Aspects::COLOR).1
|
||||
}
|
||||
None => {
|
||||
// Required sub-resources have inconsistent states, we need to
|
||||
// issue individual barriers instead of relying on the render pass.
|
||||
barriers.extend(pending.map(|pending| {
|
||||
log::trace!("\tresolve {:?}", pending);
|
||||
hal::memory::Barrier::Image {
|
||||
states: pending.to_states(),
|
||||
target: &texture.raw,
|
||||
families: None,
|
||||
range: pending.selector,
|
||||
}
|
||||
}));
|
||||
hal::image::Layout::ColorAttachmentOptimal
|
||||
}
|
||||
Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1,
|
||||
None => hal::image::Layout::ColorAttachmentOptimal,
|
||||
};
|
||||
old_layout .. hal::image::Layout::ColorAttachmentOptimal
|
||||
}
|
||||
@ -512,14 +441,32 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
}
|
||||
};
|
||||
|
||||
if !barriers.is_empty() {
|
||||
unsafe {
|
||||
current_comb.pipeline_barrier(
|
||||
all_image_stages() .. all_image_stages(),
|
||||
hal::memory::Dependencies::empty(),
|
||||
barriers,
|
||||
let mut trackers = TrackerSet::new(B::VARIANT);
|
||||
for (texture_id, view_range, consistent_usage) in output_attachments {
|
||||
let texture = &texture_guard[texture_id];
|
||||
assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT));
|
||||
|
||||
let usage = consistent_usage.unwrap_or(TextureUsage::OUTPUT_ATTACHMENT);
|
||||
match trackers.textures.init(
|
||||
texture_id,
|
||||
texture.life_guard.ref_count.clone(),
|
||||
&texture.full_range,
|
||||
) {
|
||||
Some(mut init) => init.set(view_range.clone(), usage),
|
||||
None => panic!("Your texture {:?} is in the another attachment!", texture_id),
|
||||
};
|
||||
|
||||
if consistent_usage.is_some() {
|
||||
// If we expect the texture to be transited to a new state by the
|
||||
// render pass configuration, make the tracker aware of that.
|
||||
let _ = trackers.textures.change_replace(
|
||||
texture_id,
|
||||
&texture.life_guard.ref_count,
|
||||
view_range.clone(),
|
||||
TextureUsage::OUTPUT_ATTACHMENT,
|
||||
&texture.full_range,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut render_pass_cache = device.render_passes.lock();
|
||||
@ -730,6 +677,7 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
ref_count: cmb.life_guard.ref_count.clone(),
|
||||
},
|
||||
context,
|
||||
trackers,
|
||||
sample_count,
|
||||
cmb.features.max_bind_groups,
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
id::{BindGroupId, BufferId, CommandBufferId, RenderPassId, RenderPipelineId},
|
||||
pipeline::{IndexFormat, InputStepMode, PipelineFlags},
|
||||
resource::BufferUsage,
|
||||
track::{Stitch, TrackerSet},
|
||||
track::TrackerSet,
|
||||
BufferAddress,
|
||||
Color,
|
||||
Stored,
|
||||
@ -129,6 +129,7 @@ impl<B: GfxBackend> RenderPass<B> {
|
||||
raw: B::CommandBuffer,
|
||||
cmb_id: Stored<CommandBufferId>,
|
||||
context: RenderPassContext,
|
||||
trackers: TrackerSet,
|
||||
sample_count: u8,
|
||||
max_bind_groups: u32,
|
||||
) -> Self {
|
||||
@ -137,7 +138,7 @@ impl<B: GfxBackend> RenderPass<B> {
|
||||
cmb_id,
|
||||
context,
|
||||
binder: Binder::new(max_bind_groups),
|
||||
trackers: TrackerSet::new(B::VARIANT),
|
||||
trackers,
|
||||
blend_color_status: OptionalState::Unused,
|
||||
stencil_reference_status: OptionalState::Unused,
|
||||
index_state: IndexState {
|
||||
@ -185,7 +186,7 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
pass.raw.end_render_pass();
|
||||
}
|
||||
pass.trackers.optimize();
|
||||
log::debug!("Render pass {:?} tracker: {:#?}", pass_id, pass.trackers);
|
||||
log::debug!("Render pass {:?} {:#?}", pass_id, pass.trackers);
|
||||
|
||||
let cmb = &mut cmb_guard[pass.cmb_id.value];
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
@ -198,7 +199,6 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
last,
|
||||
&mut cmb.trackers,
|
||||
&pass.trackers,
|
||||
Stitch::Last,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
);
|
||||
@ -209,6 +209,11 @@ impl<F: IdentityFilter<RenderPassId>> Global<F> {
|
||||
}
|
||||
}
|
||||
|
||||
if false {
|
||||
log::debug!("Command buffer {:?} after render pass {:#?}",
|
||||
pass.cmb_id.value, cmb.trackers);
|
||||
}
|
||||
|
||||
cmb.raw.push(pass.raw);
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ use crate::{
|
||||
pipeline,
|
||||
resource,
|
||||
swap_chain,
|
||||
track::{SEPARATE_DEPTH_STENCIL_STATES, Stitch, TrackerSet},
|
||||
track::{SEPARATE_DEPTH_STENCIL_STATES, TrackerSet},
|
||||
BufferAddress,
|
||||
FastHashMap,
|
||||
Features,
|
||||
@ -817,13 +817,12 @@ impl<F: IdentityFilter<BufferId>> Global<F> {
|
||||
let ref_count = buffer.life_guard.ref_count.clone();
|
||||
|
||||
let id = hub.buffers.register_identity(id_in, buffer, &mut token);
|
||||
let ok =
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.buffers
|
||||
.init(id, ref_count, (), resource::BufferUsage::empty());
|
||||
assert!(ok);
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.buffers
|
||||
.init(id, ref_count, &())
|
||||
.unwrap();
|
||||
id
|
||||
}
|
||||
|
||||
@ -857,13 +856,15 @@ impl<F: IdentityFilter<BufferId>> Global<F> {
|
||||
}
|
||||
|
||||
let id = hub.buffers.register_identity(id_in, buffer, &mut token);
|
||||
let ok = device.trackers.lock().buffers.init(
|
||||
id,
|
||||
ref_count,
|
||||
(),
|
||||
resource::BufferUsage::MAP_WRITE,
|
||||
);
|
||||
assert!(ok);
|
||||
device.trackers
|
||||
.lock()
|
||||
.buffers.init(
|
||||
id,
|
||||
ref_count,
|
||||
&(),
|
||||
)
|
||||
.unwrap()
|
||||
.set((), resource::BufferUsage::MAP_WRITE);
|
||||
id
|
||||
}
|
||||
|
||||
@ -967,13 +968,15 @@ impl<F: IdentityFilter<TextureId>> Global<F> {
|
||||
let ref_count = texture.life_guard.ref_count.clone();
|
||||
|
||||
let id = hub.textures.register_identity(id_in, texture, &mut token);
|
||||
let ok = device.trackers.lock().textures.init(
|
||||
id,
|
||||
ref_count,
|
||||
range,
|
||||
resource::TextureUsage::UNINITIALIZED,
|
||||
);
|
||||
assert!(ok);
|
||||
device.trackers
|
||||
.lock()
|
||||
.textures.init(
|
||||
id,
|
||||
ref_count,
|
||||
&range,
|
||||
)
|
||||
.unwrap()
|
||||
.set(range, resource::TextureUsage::UNINITIALIZED);
|
||||
id
|
||||
}
|
||||
|
||||
@ -1079,8 +1082,10 @@ impl<F: IdentityFilter<TextureViewId>> Global<F> {
|
||||
let ref_count = view.life_guard.ref_count.clone();
|
||||
|
||||
let id = hub.texture_views.register_identity(id_in, view, &mut token);
|
||||
let ok = device.trackers.lock().views.init(id, ref_count, (), ());
|
||||
assert!(ok);
|
||||
device.trackers
|
||||
.lock()
|
||||
.views.init(id, ref_count, &())
|
||||
.unwrap();
|
||||
id
|
||||
}
|
||||
|
||||
@ -1150,8 +1155,10 @@ impl<F: IdentityFilter<SamplerId>> Global<F> {
|
||||
let ref_count = sampler.life_guard.ref_count.clone();
|
||||
|
||||
let id = hub.samplers.register_identity(id_in, sampler, &mut token);
|
||||
let ok = device.trackers.lock().samplers.init(id, ref_count, (), ());
|
||||
assert!(ok);
|
||||
device.trackers
|
||||
.lock()
|
||||
.samplers.init(id, ref_count, &())
|
||||
.unwrap();
|
||||
id
|
||||
}
|
||||
|
||||
@ -1446,15 +1453,15 @@ impl<F: IdentityFilter<BindGroupId>> Global<F> {
|
||||
let id = hub
|
||||
.bind_groups
|
||||
.register_identity(id_in, bind_group, &mut token);
|
||||
log::debug!("Bind group {:?} tracker : {:#?}",
|
||||
log::debug!("Bind group {:?} {:#?}",
|
||||
id, hub.bind_groups.read(&mut token).0[id].used);
|
||||
|
||||
let ok = device
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.bind_groups
|
||||
.init(id, ref_count, (), ());
|
||||
assert!(ok);
|
||||
.init(id, ref_count, &())
|
||||
.unwrap();
|
||||
id
|
||||
}
|
||||
|
||||
@ -1637,7 +1644,6 @@ impl<F: AllIdentityFilter + IdentityFilter<CommandBufferId>> Global<F> {
|
||||
&mut transit,
|
||||
&mut *trackers,
|
||||
&comb.trackers,
|
||||
Stitch::Init,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
);
|
||||
@ -1650,7 +1656,7 @@ impl<F: AllIdentityFilter + IdentityFilter<CommandBufferId>> Global<F> {
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Device tracker after submission: {:#?}", trackers);
|
||||
log::debug!("Device after submission {}: {:#?}", submit_index, trackers);
|
||||
|
||||
// now prepare the GPU submission
|
||||
let fence = device.raw.create_fence(false).unwrap();
|
||||
|
@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use super::{PendingTransition, ResourceState, Stitch, Unit};
|
||||
use super::{PendingTransition, ResourceState, Unit};
|
||||
use crate::{conv, id::BufferId, resource::BufferUsage};
|
||||
use std::ops::Range;
|
||||
|
||||
@ -14,6 +14,17 @@ impl PendingTransition<BufferState> {
|
||||
pub fn to_states(&self) -> Range<hal::buffer::State> {
|
||||
conv::map_buffer_state(self.usage.start) .. conv::map_buffer_state(self.usage.end)
|
||||
}
|
||||
|
||||
fn collapse(self) -> Result<BufferUsage, Self> {
|
||||
if self.usage.start.is_empty()
|
||||
|| self.usage.start == self.usage.end
|
||||
|| !BufferUsage::WRITE_ALL.intersects(self.usage.start | self.usage.end)
|
||||
{
|
||||
Ok(self.usage.start | self.usage.end)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceState for BufferState {
|
||||
@ -23,7 +34,7 @@ impl ResourceState for BufferState {
|
||||
|
||||
fn new(_full_selector: &Self::Selector) -> Self {
|
||||
BufferState {
|
||||
init: BufferUsage::empty(),
|
||||
first: None,
|
||||
last: BufferUsage::empty(),
|
||||
}
|
||||
}
|
||||
@ -47,20 +58,15 @@ impl ResourceState for BufferState {
|
||||
usage: old .. usage,
|
||||
};
|
||||
self.last = match output {
|
||||
None => pending.collapse()?,
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
if self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
}
|
||||
usage
|
||||
}
|
||||
None => {
|
||||
if !old.is_empty()
|
||||
&& old != usage
|
||||
&& BufferUsage::WRITE_ALL.intersects(old | usage)
|
||||
{
|
||||
return Err(pending);
|
||||
}
|
||||
old | usage
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -69,12 +75,14 @@ impl ResourceState for BufferState {
|
||||
&mut self,
|
||||
id: Self::Id,
|
||||
other: &Self,
|
||||
stitch: Stitch,
|
||||
output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
let old = self.last;
|
||||
let new = other.select(stitch);
|
||||
let new = other.port();
|
||||
self.last = if old == new && BufferUsage::ORDERED.contains(new) {
|
||||
if self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
}
|
||||
other.last
|
||||
} else {
|
||||
let pending = PendingTransition {
|
||||
@ -83,15 +91,13 @@ impl ResourceState for BufferState {
|
||||
usage: old .. new,
|
||||
};
|
||||
match output {
|
||||
None => pending.collapse()?,
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
other.last
|
||||
}
|
||||
None => {
|
||||
if !old.is_empty() && BufferUsage::WRITE_ALL.intersects(old | new) {
|
||||
return Err(pending);
|
||||
if self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
}
|
||||
old | new
|
||||
other.last
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -109,7 +115,7 @@ mod test {
|
||||
#[test]
|
||||
fn change() {
|
||||
let mut bs = Unit {
|
||||
init: BufferUsage::INDEX,
|
||||
first: Some(BufferUsage::INDEX),
|
||||
last: BufferUsage::STORAGE,
|
||||
};
|
||||
let id = TypedId::zip(0, 0, Backend::Empty);
|
||||
|
@ -35,7 +35,7 @@ pub const SEPARATE_DEPTH_STENCIL_STATES: bool = false;
|
||||
/// usage as well as the last/current one, similar to `Range`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Unit<U> {
|
||||
init: U,
|
||||
first: Option<U>,
|
||||
last: U,
|
||||
}
|
||||
|
||||
@ -43,34 +43,17 @@ impl<U: Copy> Unit<U> {
|
||||
/// Create a new unit from a given usage.
|
||||
fn new(usage: U) -> Self {
|
||||
Unit {
|
||||
init: usage,
|
||||
first: None,
|
||||
last: usage,
|
||||
}
|
||||
}
|
||||
|
||||
/// Select one of the ends of the usage, based on the
|
||||
/// given `Stitch`.
|
||||
///
|
||||
/// In some scenarios, when merging two trackers
|
||||
/// A and B for a resource, we want to connect A to the initial state
|
||||
/// of B. In other scenarios, we want to reach the last state of B.
|
||||
fn select(&self, stitch: Stitch) -> U {
|
||||
match stitch {
|
||||
Stitch::Init => self.init,
|
||||
Stitch::Last => self.last,
|
||||
}
|
||||
/// Return a usage to link to.
|
||||
fn port(&self) -> U {
|
||||
self.first.unwrap_or(self.last)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mode of stitching to states together.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Stitch {
|
||||
/// Stitch to the init state of the other resource.
|
||||
Init,
|
||||
/// Stitch to the last state of the other resource.
|
||||
Last,
|
||||
}
|
||||
|
||||
/// The main trait that abstracts away the tracking logic of
|
||||
/// a particular resource type, like a buffer or a texture.
|
||||
pub trait ResourceState: Clone {
|
||||
@ -119,15 +102,10 @@ pub trait ResourceState: Clone {
|
||||
/// `PendingTransition` pushed to this vector, or extended with the
|
||||
/// other read-only usage, unless there is a usage conflict, and
|
||||
/// the error is generated (returning the conflict).
|
||||
///
|
||||
/// `stitch` only defines the end points of generated transitions.
|
||||
/// Last states of `self` are nevertheless updated to the *last* states
|
||||
/// of `other`, if `output` is provided.
|
||||
fn merge(
|
||||
&mut self,
|
||||
id: Self::Id,
|
||||
other: &Self,
|
||||
stitch: Stitch,
|
||||
output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>>;
|
||||
|
||||
@ -154,6 +132,21 @@ pub struct PendingTransition<S: ResourceState> {
|
||||
pub usage: Range<S::Usage>,
|
||||
}
|
||||
|
||||
/// Helper initialization structure that allows setting the usage on
|
||||
/// various sub-resources.
|
||||
#[derive(Debug)]
|
||||
pub struct Initializer<'a, S: ResourceState> {
|
||||
id: S::Id,
|
||||
state: &'a mut S,
|
||||
}
|
||||
|
||||
impl<S: ResourceState> Initializer<'_, S> {
|
||||
pub fn set(&mut self, selector: S::Selector, usage: S::Usage) -> bool {
|
||||
self.state.change(self.id, selector, usage, None)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// A tracker for all resources of a given type.
|
||||
pub struct ResourceTracker<S: ResourceState> {
|
||||
/// An association of known resource indices with their tracked states.
|
||||
@ -226,27 +219,24 @@ impl<S: ResourceState> ResourceTracker<S> {
|
||||
&mut self,
|
||||
id: S::Id,
|
||||
ref_count: RefCount,
|
||||
selector: S::Selector,
|
||||
default: S::Usage,
|
||||
) -> bool {
|
||||
let mut state = S::new(&selector);
|
||||
match state.change(id, selector, default, None) {
|
||||
Ok(()) => (),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
|
||||
full_selector: &S::Selector,
|
||||
) -> Option<Initializer<S>> {
|
||||
let (index, epoch, backend) = id.unzip();
|
||||
debug_assert_eq!(backend, self.backend);
|
||||
self.map
|
||||
.insert(
|
||||
index,
|
||||
Resource {
|
||||
match self.map.entry(index) {
|
||||
Entry::Vacant(e) => {
|
||||
let res = e.insert(Resource {
|
||||
ref_count,
|
||||
state,
|
||||
state: S::new(full_selector),
|
||||
epoch,
|
||||
},
|
||||
)
|
||||
.is_none()
|
||||
});
|
||||
Some(Initializer {
|
||||
id,
|
||||
state: &mut res.state,
|
||||
})
|
||||
}
|
||||
Entry::Occupied(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Query the usage of a resource selector.
|
||||
@ -331,7 +321,7 @@ impl<S: ResourceState> ResourceTracker<S> {
|
||||
let id = S::Id::zip(index, new.epoch, self.backend);
|
||||
e.into_mut()
|
||||
.state
|
||||
.merge(id, &new.state, Stitch::Last, None)?;
|
||||
.merge(id, &new.state, None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -343,7 +333,6 @@ impl<S: ResourceState> ResourceTracker<S> {
|
||||
pub fn merge_replace<'a>(
|
||||
&'a mut self,
|
||||
other: &'a Self,
|
||||
stitch: Stitch,
|
||||
) -> Drain<PendingTransition<S>> {
|
||||
for (&index, new) in other.map.iter() {
|
||||
match self.map.entry(index) {
|
||||
@ -355,7 +344,7 @@ impl<S: ResourceState> ResourceTracker<S> {
|
||||
let id = S::Id::zip(index, new.epoch, self.backend);
|
||||
e.into_mut()
|
||||
.state
|
||||
.merge(id, &new.state, stitch, Some(&mut self.temp))
|
||||
.merge(id, &new.state, Some(&mut self.temp))
|
||||
.ok(); //TODO: unwrap?
|
||||
}
|
||||
}
|
||||
@ -426,7 +415,6 @@ impl<I: Copy + fmt::Debug + TypedId> ResourceState for PhantomData<I> {
|
||||
&mut self,
|
||||
_id: Self::Id,
|
||||
_other: &Self,
|
||||
_stitch: Stitch,
|
||||
_output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
Ok(())
|
||||
|
@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use super::{SEPARATE_DEPTH_STENCIL_STATES, range::RangedStates, PendingTransition, ResourceState, Stitch, Unit};
|
||||
use super::{SEPARATE_DEPTH_STENCIL_STATES, range::RangedStates, PendingTransition, ResourceState, Unit};
|
||||
use crate::{conv, device::MAX_MIP_LEVELS, id::TextureId, resource::TextureUsage};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
@ -26,34 +26,14 @@ impl PendingTransition<TextureState> {
|
||||
.. conv::map_texture_state(self.usage.end, self.selector.aspects)
|
||||
}
|
||||
|
||||
//TODO: make this less awkward!
|
||||
/// Check for the validity of `self` with regards to the presence of `output`.
|
||||
///
|
||||
/// Return the end usage if the `output` is provided and pushes self to it.
|
||||
/// Otherwise, return the extended usage, or an error if extension is impossible.
|
||||
///
|
||||
/// When a transition is generated, returns the specified `replace` usage.
|
||||
fn record(
|
||||
self,
|
||||
output: Option<&mut &mut Vec<Self>>,
|
||||
replace: TextureUsage,
|
||||
) -> Result<TextureUsage, Self> {
|
||||
let u = self.usage.clone();
|
||||
match output {
|
||||
Some(out) => {
|
||||
out.push(self);
|
||||
Ok(replace)
|
||||
}
|
||||
None => {
|
||||
if !u.start.is_empty()
|
||||
&& u.start != u.end
|
||||
&& TextureUsage::WRITE_ALL.intersects(u.start | u.end)
|
||||
{
|
||||
Err(self)
|
||||
} else {
|
||||
Ok(u.start | u.end)
|
||||
}
|
||||
}
|
||||
fn collapse(self) -> Result<TextureUsage, Self> {
|
||||
if self.usage.start.is_empty()
|
||||
|| self.usage.start == self.usage.end
|
||||
|| !TextureUsage::WRITE_ALL.intersects(self.usage.start | self.usage.end)
|
||||
{
|
||||
Ok(self.usage.start | self.usage.end)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,7 +117,17 @@ impl ResourceState for TextureState {
|
||||
},
|
||||
usage: unit.last .. usage,
|
||||
};
|
||||
unit.last = pending.record(output.as_mut(), usage)?;
|
||||
|
||||
unit.last = match output {
|
||||
None => pending.collapse()?,
|
||||
Some(ref mut out) => {
|
||||
out.push(pending);
|
||||
if unit.first.is_none() {
|
||||
unit.first = Some(unit.last);
|
||||
}
|
||||
usage
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,11 +138,8 @@ impl ResourceState for TextureState {
|
||||
&mut self,
|
||||
id: Self::Id,
|
||||
other: &Self,
|
||||
stitch: Stitch,
|
||||
mut output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
assert!(output.is_some() || stitch == Stitch::Last);
|
||||
|
||||
let mut temp = Vec::new();
|
||||
while self.mips.len() < other.mips.len() as usize {
|
||||
self.mips.push(MipState::default());
|
||||
@ -183,10 +170,15 @@ impl ResourceState for TextureState {
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
} => {
|
||||
let mut final_usage = end.select(stitch);
|
||||
if start.last != final_usage
|
||||
|| !TextureUsage::ORDERED.contains(final_usage)
|
||||
let to_usage = end.port();
|
||||
if start.last == to_usage
|
||||
&& TextureUsage::ORDERED.contains(to_usage)
|
||||
{
|
||||
Unit {
|
||||
first: start.first,
|
||||
last: end.last,
|
||||
}
|
||||
} else {
|
||||
let pending = PendingTransition {
|
||||
id,
|
||||
selector: hal::image::SubresourceRange {
|
||||
@ -194,13 +186,24 @@ impl ResourceState for TextureState {
|
||||
levels: level .. level + 1,
|
||||
layers: layers.clone(),
|
||||
},
|
||||
usage: start.last .. final_usage,
|
||||
usage: start.last .. to_usage,
|
||||
};
|
||||
final_usage = pending.record(output.as_mut(), end.last)?;
|
||||
}
|
||||
Unit {
|
||||
init: start.init,
|
||||
last: final_usage,
|
||||
|
||||
match output {
|
||||
None => {
|
||||
Unit {
|
||||
first: start.first,
|
||||
last: pending.collapse()?,
|
||||
}
|
||||
}
|
||||
Some(ref mut out) => {
|
||||
out.push(pending);
|
||||
Unit {
|
||||
first: Some(start.last),
|
||||
last: end.last,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user