566: [0.5] tracking improvements r=kvark a=kvark

Sibling of #565
The code is mostly new tests :)

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot]
2020-04-10 05:48:26 +00:00
committed by GitHub
10 changed files with 297 additions and 71 deletions

View File

@@ -1,5 +1,9 @@
# Change Log
## v0.5.1 (10-04-2020)
- fix tracking of swapchain images that are used multiple times in a command buffer
- fix tracking of initial usage of a resource across a command buffer
## v0.5 (06-04-2020)
- Crates:
- `wgpu-types`: common types between native and web targets

6
Cargo.lock generated
View File

@@ -653,7 +653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wgpu-core"
version = "0.5.0"
version = "0.5.1"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"battery 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -687,7 +687,7 @@ dependencies = [
"objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raw-window-handle 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"wgpu-core 0.5.0",
"wgpu-core 0.5.1",
"wgpu-types 0.5.0",
]
@@ -697,7 +697,7 @@ version = "0.1.0"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wgpu-core 0.5.0",
"wgpu-core 0.5.1",
"wgpu-types 0.5.0",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "wgpu-core"
version = "0.5.0"
version = "0.5.1"
authors = [
"Dzmitry Malyshau <kvark@mozilla.com>",
"Joshua Groves <josh@joshgroves.com>",

View File

@@ -29,9 +29,7 @@ use wgt::{
TextureUsage, BIND_BUFFER_ALIGNMENT,
};
use std::{
borrow::Borrow, collections::hash_map::Entry, iter, marker::PhantomData, mem, ops::Range, slice,
};
use std::{borrow::Borrow, collections::hash_map::Entry, iter, mem, ops::Range, slice};
pub type RenderPassColorAttachmentDescriptor =
RenderPassColorAttachmentDescriptorBase<id::TextureViewId>;
@@ -438,7 +436,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut resolves = ArrayVec::new();
for at in &color_attachments {
let view = &view_guard[at.attachment];
let view = trackers
.views
.use_extend(&*view_guard, at.attachment, (), ())
.unwrap();
if let Some(ex) = extent {
assert_eq!(ex, view.extent);
} else {
@@ -448,10 +449,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
view.samples, sample_count,
"All attachments must have the same sample_count"
);
let first_use = trackers
.views
.init(at.attachment, view.life_guard.add_ref(), PhantomData)
.is_ok();
let layouts = match view.inner {
TextureViewInner::Native { ref source_id, .. } => {
@@ -477,10 +474,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
let end = hal::image::Layout::Present;
let start = if first_use {
hal::image::Layout::Undefined
} else {
end
let start = match base_trackers.views.query(at.attachment, ()) {
Some(_) => end,
None => hal::image::Layout::Undefined,
};
start..end
}
@@ -496,16 +492,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
for resolve_target in color_attachments.iter().flat_map(|at| at.resolve_target) {
let view = &view_guard[resolve_target];
let view = trackers
.views
.use_extend(&*view_guard, resolve_target, (), ())
.unwrap();
assert_eq!(extent, Some(view.extent));
assert_eq!(
view.samples, 1,
"All resolve_targets must have a sample_count of 1"
);
let first_use = trackers
.views
.init(resolve_target, view.life_guard.add_ref(), PhantomData)
.is_ok();
let layouts = match view.inner {
TextureViewInner::Native { ref source_id, .. } => {
@@ -531,10 +526,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
let end = hal::image::Layout::Present;
let start = if first_use {
hal::image::Layout::Undefined
} else {
end
let start = match base_trackers.views.query(resolve_target, ()) {
Some(_) => end,
None => hal::image::Layout::Undefined,
};
start..end
}
@@ -1158,6 +1152,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
log::trace!("Merging {:?} with the render pass", encoder_id);
unsafe {
raw.end_render_pass();
}
super::CommandBuffer::insert_barriers(
cmb.raw.last_mut().unwrap(),
&mut cmb.trackers,
@@ -1167,7 +1165,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
unsafe {
cmb.raw.last_mut().unwrap().finish();
raw.end_render_pass();
}
cmb.raw.push(raw);
}

View File

@@ -1503,6 +1503,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// execute resource transitions
let mut transit = device.com_allocator.extend(comb);
unsafe {
// the last buffer was open, closing now
comb.raw.last_mut().unwrap().finish();
transit.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT);
}
log::trace!("Stitching command buffer {:?} before submission", cmb_id);
@@ -1517,9 +1519,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
transit.finish();
}
comb.raw.insert(0, transit);
unsafe {
comb.raw.last_mut().unwrap().finish();
}
}
log::debug!("Device after submission {}: {:#?}", submit_index, trackers);

View File

@@ -23,7 +23,10 @@ pub struct Id<T>(NonZeroU64, PhantomData<T>);
// required for PeekPoke
impl<T> Default for Id<T> {
fn default() -> Self {
Id(unsafe { NonZeroU64::new_unchecked(!0) }, PhantomData)
Id(
unsafe { NonZeroU64::new_unchecked(!0 >> BACKEND_BITS) },
PhantomData,
)
}
}

View File

@@ -55,22 +55,28 @@ impl ResourceState for BufferState {
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
let old = self.last;
if usage != old || !BufferUsage::ORDERED.contains(usage) {
if old != usage || !BufferUsage::ORDERED.contains(usage) {
let pending = PendingTransition {
id,
selector: (),
usage: old..usage,
};
self.last = match output {
None => pending.collapse()?,
*self = match output {
None => {
assert_eq!(
self.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(transitions) => {
transitions.push(pending);
if self.first.is_none() {
self.first = Some(old);
Unit {
first: self.first.or(Some(old)),
last: usage,
}
usage
}
}
};
}
Ok(())
}
@@ -83,28 +89,33 @@ impl ResourceState for BufferState {
) -> Result<(), PendingTransition<Self>> {
let old = self.last;
let new = other.port();
self.last = if old == new && BufferUsage::ORDERED.contains(new) {
if self.first.is_none() {
if old == new && BufferUsage::ORDERED.contains(new) {
if output.is_some() && self.first.is_none() {
self.first = Some(old);
}
other.last
} else {
let pending = PendingTransition {
id,
selector: (),
usage: old..new,
};
match output {
None => pending.collapse()?,
*self = match output {
None => {
assert_eq!(
self.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(transitions) => {
transitions.push(pending);
if self.first.is_none() {
self.first = Some(old);
Unit {
first: self.first.or(Some(old)),
last: other.last,
}
other.last
}
}
};
};
}
Ok(())
}
@@ -114,19 +125,71 @@ impl ResourceState for BufferState {
#[cfg(test)]
mod test {
use super::*;
use crate::id::TypedId;
use crate::id::Id;
#[test]
fn change() {
fn change_extend() {
let mut bs = Unit {
first: Some(BufferUsage::INDEX),
first: None,
last: BufferUsage::INDEX,
};
let id = Id::default();
assert_eq!(
bs.change(id, (), BufferUsage::STORAGE, None),
Err(PendingTransition {
id,
selector: (),
usage: BufferUsage::INDEX..BufferUsage::STORAGE,
}),
);
bs.change(id, (), BufferUsage::VERTEX, None).unwrap();
bs.change(id, (), BufferUsage::INDEX, None).unwrap();
assert_eq!(bs, Unit::new(BufferUsage::VERTEX | BufferUsage::INDEX));
}
#[test]
fn change_replace() {
let mut bs = Unit {
first: None,
last: BufferUsage::STORAGE,
};
let id = TypedId::zip(1, 0, wgt::Backend::Empty);
assert!(bs.change(id, (), BufferUsage::VERTEX, None).is_err());
bs.change(id, (), BufferUsage::VERTEX, Some(&mut Vec::new()))
let id = Id::default();
let mut list = Vec::new();
bs.change(id, (), BufferUsage::VERTEX, Some(&mut list))
.unwrap();
bs.change(id, (), BufferUsage::INDEX, None).unwrap();
assert_eq!(bs.last, BufferUsage::VERTEX | BufferUsage::INDEX);
assert_eq!(
&list,
&[PendingTransition {
id,
selector: (),
usage: BufferUsage::STORAGE..BufferUsage::VERTEX,
}],
);
assert_eq!(
bs,
Unit {
first: Some(BufferUsage::STORAGE),
last: BufferUsage::VERTEX,
}
);
list.clear();
bs.change(id, (), BufferUsage::STORAGE, Some(&mut list))
.unwrap();
assert_eq!(
&list,
&[PendingTransition {
id,
selector: (),
usage: BufferUsage::VERTEX..BufferUsage::STORAGE,
}],
);
assert_eq!(
bs,
Unit {
first: Some(BufferUsage::STORAGE),
last: BufferUsage::STORAGE,
}
);
}
}

View File

@@ -111,7 +111,7 @@ struct Resource<S> {
/// A structure containing all the information about a particular resource
/// transition. User code should be able to generate a pipeline barrier
/// based on the contents.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct PendingTransition<S: ResourceState> {
pub id: S::Id,
pub selector: S::Selector,

View File

@@ -9,7 +9,7 @@ use std::{cmp::Ordering, fmt::Debug, iter, ops::Range, slice::Iter};
/// Structure that keeps track of a I -> T mapping,
/// optimized for a case where keys of the same values
/// are often grouped together linearly.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct RangedStates<I, T> {
/// List of ranges, each associated with a singe value.
/// Ranges of keys have to be non-intersecting and ordered.

View File

@@ -13,7 +13,7 @@ use std::{iter, ops::Range};
//TODO: store `hal::image::State` here to avoid extra conversions
type PlaneStates = RangedStates<hal::image::Layer, Unit<TextureUsage>>;
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TextureState {
mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS]>,
/// True if we have the information about all the subresources here
@@ -116,14 +116,20 @@ impl ResourceState for TextureState {
usage: unit.last..usage,
};
unit.last = match output {
None => pending.collapse()?,
*unit = match output {
None => {
assert_eq!(
unit.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(ref mut out) => {
out.push(pending);
if unit.first.is_none() {
unit.first = Some(unit.last);
Unit {
first: unit.first.or(Some(unit.last)),
last: usage,
}
usage
}
};
}
@@ -172,7 +178,10 @@ impl ResourceState for TextureState {
let to_usage = end.port();
if start.last == to_usage && TextureUsage::ORDERED.contains(to_usage) {
Unit {
first: start.first,
first: match output {
None => start.first,
Some(_) => start.first.or(Some(start.last)),
},
last: end.last,
}
} else {
@@ -191,14 +200,18 @@ impl ResourceState for TextureState {
};
match output {
None => Unit {
first: start.first,
last: pending.collapse()?,
},
None => {
assert_eq!(
start.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(ref mut out) => {
out.push(pending);
Unit {
first: Some(start.last),
// this has to leave a valid `first` state
first: start.first.or(Some(start.last)),
last: end.last,
}
}
@@ -222,9 +235,9 @@ impl ResourceState for TextureState {
#[cfg(test)]
mod test {
//TODO: change() and merge() tests
//use crate::TypedId;
//TODO: change() tests
use super::*;
use crate::id::Id;
use hal::{format::Aspects, image::SubresourceRange};
#[test]
@@ -274,4 +287,151 @@ mod test {
None,
);
}
#[test]
fn merge() {
let id = Id::default();
let mut ts1 = TextureState::default();
ts1.mips.push(PlaneStates::from_slice(&[(
1..3,
Unit::new(TextureUsage::SAMPLED),
)]));
let mut ts2 = TextureState::default();
assert_eq!(
ts1.merge(id, &ts2, None),
Ok(()),
"failed to merge with an emtpy"
);
ts2.mips.push(PlaneStates::from_slice(&[(
1..2,
Unit::new(TextureUsage::COPY_SRC),
)]));
assert_eq!(
ts1.merge(Id::default(), &ts2, None),
Ok(()),
"failed to extend a compatible state"
);
assert_eq!(
ts1.mips[0].query(&(1..2), |&v| v),
Some(Ok(Unit {
first: None,
last: TextureUsage::SAMPLED | TextureUsage::COPY_SRC,
})),
"wrong extension result"
);
ts2.mips[0] = PlaneStates::from_slice(&[(1..2, Unit::new(TextureUsage::COPY_DST))]);
assert_eq!(
ts1.clone().merge(Id::default(), &ts2, None),
Err(PendingTransition {
id,
selector: SubresourceRange {
aspects: Aspects::empty(),
levels: 0..1,
layers: 1..2,
},
usage: TextureUsage::SAMPLED | TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
}),
"wrong error on extending with incompatible state"
);
let mut list = Vec::new();
ts2.mips[0] = PlaneStates::from_slice(&[
(1..2, Unit::new(TextureUsage::COPY_DST)),
(
2..3,
Unit {
first: Some(TextureUsage::COPY_SRC),
last: TextureUsage::OUTPUT_ATTACHMENT,
},
),
]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
assert_eq!(
&list,
&[
PendingTransition {
id,
selector: SubresourceRange {
aspects: Aspects::empty(),
levels: 0..1,
layers: 1..2,
},
usage: TextureUsage::SAMPLED | TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
},
PendingTransition {
id,
selector: SubresourceRange {
aspects: Aspects::empty(),
levels: 0..1,
layers: 2..3,
},
// the transition links the end of the base rage (..SAMPLED)
// with the start of the next range (COPY_SRC..)
usage: TextureUsage::SAMPLED..TextureUsage::COPY_SRC,
},
],
"replacing produced wrong transitions"
);
assert_eq!(
ts1.mips[0].query(&(1..2), |&v| v),
Some(Ok(Unit {
first: Some(TextureUsage::SAMPLED | TextureUsage::COPY_SRC),
last: TextureUsage::COPY_DST,
})),
"wrong final layer 1 state"
);
assert_eq!(
ts1.mips[0].query(&(2..3), |&v| v),
Some(Ok(Unit {
first: Some(TextureUsage::SAMPLED),
last: TextureUsage::OUTPUT_ATTACHMENT,
})),
"wrong final layer 2 state"
);
list.clear();
ts2.mips[0] = PlaneStates::from_slice(&[(
2..3,
Unit {
first: Some(TextureUsage::OUTPUT_ATTACHMENT),
last: TextureUsage::COPY_SRC,
},
)]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
assert_eq!(&list, &[], "unexpected replacing transition");
list.clear();
ts2.mips[0] = PlaneStates::from_slice(&[(
2..3,
Unit {
first: Some(TextureUsage::COPY_DST),
last: TextureUsage::COPY_DST,
},
)]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
assert_eq!(
&list,
&[PendingTransition {
id,
selector: SubresourceRange {
aspects: Aspects::empty(),
levels: 0..1,
layers: 2..3,
},
usage: TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
},],
"invalid replacing transition"
);
assert_eq!(
ts1.mips[0].query(&(2..3), |&v| v),
Some(Ok(Unit {
// the initial state here is never expected to change
first: Some(TextureUsage::SAMPLED),
last: TextureUsage::COPY_DST,
})),
"wrong final layer 2 state"
);
}
}