From 2d5e67d6d5f7e5d4e4080734fe5fe33990621042 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 4 Jun 2019 23:56:18 -0400 Subject: [PATCH 01/25] Basic data structure to track mip levels and layers of a texture --- wgpu-native/src/instance.rs | 3 +-- wgpu-native/src/track.rs | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/wgpu-native/src/instance.rs b/wgpu-native/src/instance.rs index 4b7c32707a..c99818d1ef 100644 --- a/wgpu-native/src/instance.rs +++ b/wgpu-native/src/instance.rs @@ -1,6 +1,5 @@ use crate::{ binding_model::MAX_BIND_GROUPS, - device::BIND_BUFFER_ALIGNMENT, hub::HUB, AdapterHandle, AdapterId, @@ -9,7 +8,7 @@ use crate::{ SurfaceHandle, }; #[cfg(feature = "local")] -use crate::{DeviceId, SurfaceId}; +use crate::{device::BIND_BUFFER_ALIGNMENT, DeviceId, SurfaceId}; #[cfg(feature = "local")] use log::info; diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index e1af6fdfd6..e5a4b77f20 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -22,6 +22,51 @@ use std::{ ops::{BitOr, Range}, }; +#[derive(Clone, Debug)] +pub struct RangedStates { + ranges: Vec<(Range, T)>, +} + +pub type TextureLayerStates = RangedStates; +pub type TextureStates = RangedStates; + +impl RangedStates { + fn isolate(&mut self, index: Range) -> &mut T { + let mut pos = self.ranges + .iter() + .position(|&(ref range, _)| index.start >= range.start) + .unwrap(); + let base_range = self.ranges[pos].0.clone(); + assert!(index.end <= base_range.end); + if base_range.start < index.start { + let value = ((base_range.start .. index.start), self.ranges[pos].1.clone()); + self.ranges.insert(pos, value); + pos += 1; + self.ranges[pos].0.start = index.start; + } + if base_range.end > index.end { + let value = ((index.end .. base_range.end), self.ranges[pos].1.clone()); + self.ranges.insert(pos + 1, value); + self.ranges[pos].0.end = index.end; + } + &mut self.ranges[pos].1 + } +} + +impl TextureStates { + fn change_state( + &mut self, level: hal::image::Level, layer: hal::image::Layer, usage: TextureUsage + ) -> Option { + let layer_states = self.isolate(level .. level + 1); + let cur_usage = layer_states.isolate(layer .. layer + 1); + if *cur_usage != usage { + Some(mem::replace(cur_usage, usage)) + } else { + None + } + } +} + #[derive(Clone, Debug, PartialEq)] #[allow(unused)] pub enum Tracktion { From 0dcd0c449d87b827c9a8def13c096163d3594e00 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 5 Jun 2019 23:29:42 -0400 Subject: [PATCH 02/25] Parametrize Tracker by a state of a resource, add new plane tracker --- wgpu-native/src/device.rs | 3 + wgpu-native/src/track.rs | 269 +++++++++++++++++++++++++------------- 2 files changed, 179 insertions(+), 93 deletions(-) diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index e98e10f2b6..7f2e16a844 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -58,6 +58,7 @@ use std::{collections::hash_map::Entry, ffi, iter, ptr, ops::Range, slice, sync: const CLEANUP_WAIT_MS: u64 = 5000; pub const MAX_COLOR_TARGETS: usize = 4; +pub const MAX_MIP_LEVELS: usize = 16; pub const MAX_VERTEX_BUFFERS: usize = 8; /// Bound uniform/storage buffer offsets must be aligned to this number. @@ -686,6 +687,8 @@ pub fn device_create_texture( let device_guard = HUB.devices.read(); let device = &device_guard[device_id]; + assert!((desc.mip_level_count as usize) < MAX_MIP_LEVELS); + let mut image = unsafe { device.raw.create_image( kind, diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index e5a4b77f20..226d55b90f 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -1,5 +1,6 @@ use crate::{ hub::Storage, + device::MAX_MIP_LEVELS, resource::{BufferUsage, TextureUsage}, BufferId, Epoch, @@ -11,6 +12,7 @@ use crate::{ BindGroupId, }; +use arrayvec::ArrayVec; use bitflags::bitflags; use hal::backend::FastHashMap; @@ -28,7 +30,7 @@ pub struct RangedStates { } pub type TextureLayerStates = RangedStates; -pub type TextureStates = RangedStates; +pub type TextureStates2 = RangedStates; impl RangedStates { fn isolate(&mut self, index: Range) -> &mut T { @@ -53,7 +55,7 @@ impl RangedStates { } } -impl TextureStates { +impl TextureStates2 { fn change_state( &mut self, level: hal::image::Level, layer: hal::image::Layer, usage: TextureUsage ) -> Option { @@ -67,6 +69,61 @@ impl TextureStates { } } + +pub enum PlaneStates { + Single(T), + Multi(Vec<(Range, T)>), +} + +impl Default for PlaneStates { + fn default() -> Self { + PlaneStates::Multi(Vec::new()) + } +} + +pub struct DepthStencilState { + depth: TextureUsage, + stencil: TextureUsage, +} + +pub struct TextureStates { + color_mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS]>, + depth_stencil: PlaneStates, +} + +impl TextureStates { + fn change<'a>( + &mut self, + what: hal::image::SubresourceRange, + usage: TextureUsage, + permit: TrackPermit, + fun: impl FnMut(hal::image::SubresourceRange, Tracktion), + ) { + /*if what.aspects.contains(hal::format::Aspects::COLOR) { + for level in what.levels.clone() { + match self.color_mips[level as usize] { + PlaneStates::Single(ref mut cur_usage) => { + assert_eq!(what.layers, 0 .. 1); + if *cur_usage != usage { + let sub = hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: 0 .. 1, + }; + fun(sub, *cur_usage); + *cur_usage = usage; + } + } + PlaneStates::Multi(ref mut layers) => { + let pos = + } + } + } + }*/ + } +} + + #[derive(Clone, Debug, PartialEq)] #[allow(unused)] pub enum Tracktion { @@ -131,24 +188,60 @@ impl GenericUsage for DummyUsage { } } +/// A single unit of state tracking. #[derive(Clone, Debug)] -struct Track { - ref_count: RefCount, +pub struct Unit { init: U, last: U, +} + +impl Unit { + fn new(usage: U) -> Self { + Unit { + init: usage, + last: usage, + } + } + + fn select(&self, stitch: Stitch) -> U { + match stitch { + Stitch::Init => self.init, + Stitch::Last => self.last, + } + } +} + +impl + PartialEq + GenericUsage> Unit { + fn transit(&mut self, usage: U, permit: TrackPermit) -> Result, U> { + let old = self.last; + if usage == old { + Ok(Tracktion::Keep) + } else if permit.contains(TrackPermit::EXTEND) && !(old | usage).is_exclusive() { + self.last = old | usage; + Ok(Tracktion::Extend { old }) + } else if permit.contains(TrackPermit::REPLACE) { + self.last = usage; + Ok(Tracktion::Replace { old }) + } else { + Err(old) + } + } +} + +#[derive(Clone, Debug)] +struct Resource { + ref_count: RefCount, + state: S, epoch: Epoch, } //TODO: consider having `I` as an associated type of `U`? #[derive(Debug)] -pub struct Tracker { - map: FastHashMap>, +pub struct Tracker { + /// An association of known resource indices with their tracked states. + map: FastHashMap>, _phantom: PhantomData, } -pub type BufferTracker = Tracker; -pub type TextureTracker = Tracker; -pub type TextureViewTracker = Tracker; -pub type BindGroupTracker = Tracker; //TODO: make this a generic parameter. /// Mode of stitching to states together. @@ -163,8 +256,8 @@ pub enum Stitch { //TODO: consider rewriting this without any iterators that have side effects. #[derive(Debug)] pub struct ConsumeIterator<'a, I: TypedId, U: Copy + PartialEq> { - src: Iter<'a, Index, Track>, - dst: &'a mut FastHashMap>, + src: Iter<'a, Index, Resource>>, + dst: &'a mut FastHashMap>>, stitch: Stitch, _marker: PhantomData, } @@ -180,13 +273,10 @@ impl<'a, I: TypedId, U: Copy + PartialEq> Iterator for ConsumeIterator<'a, I, U> } Entry::Occupied(mut e) => { assert_eq!(e.get().epoch, new.epoch); - let old = mem::replace(&mut e.get_mut().last, new.last); - if old != new.init { - let state = match self.stitch { - Stitch::Init => new.init, - Stitch::Last => new.last, - }; - return Some((I::new(index, new.epoch), old .. state)) + let old = mem::replace(&mut e.get_mut().state.last, new.state.last); + if old != new.state.init { + let states = old .. new.state.select(self.stitch); + return Some((I::new(index, new.epoch), states)) } } } @@ -201,41 +291,7 @@ impl<'a, I: TypedId, U: Copy + PartialEq> Drop for ConsumeIterator<'a, I, U> { } } -#[derive(Debug)] -pub struct TrackerSet { - pub buffers: BufferTracker, - pub textures: TextureTracker, - pub views: TextureViewTracker, - pub bind_groups: BindGroupTracker, - //TODO: samplers -} - -impl TrackerSet { - pub fn new() -> Self { - TrackerSet { - buffers: BufferTracker::new(), - textures: TextureTracker::new(), - views: TextureViewTracker::new(), - bind_groups: BindGroupTracker::new(), - } - } - - pub fn clear(&mut self) { - self.buffers.clear(); - self.textures.clear(); - self.views.clear(); - self.bind_groups.clear(); - } - - pub fn consume_by_extend(&mut self, other: &Self) { - self.buffers.consume_by_extend(&other.buffers).unwrap(); - self.textures.consume_by_extend(&other.textures).unwrap(); - self.views.consume_by_extend(&other.views).unwrap(); - self.bind_groups.consume_by_extend(&other.bind_groups).unwrap(); - } -} - -impl + PartialEq> Tracker { +impl Tracker { pub fn new() -> Self { Tracker { map: FastHashMap::default(), @@ -246,22 +302,34 @@ impl + PartialEq> Tracker /// Remove an id from the tracked map. pub(crate) fn remove(&mut self, id: I) -> bool { match self.map.remove(&id.index()) { - Some(track) => { - assert_eq!(track.epoch, id.epoch()); + Some(resource) => { + assert_eq!(resource.epoch, id.epoch()); true } None => false, } } + /// Return an iterator over used resources keys. + pub fn used<'a>(&'a self) -> impl 'a + Iterator { + self.map + .iter() + .map(|(&index, resource)| I::new(index, resource.epoch)) + } + + fn clear(&mut self) { + self.map.clear(); + } +} + +impl + PartialEq> Tracker> { /// Get the last usage on a resource. pub(crate) fn query(&mut self, id: I, ref_count: &RefCount, default: U) -> Query { match self.map.entry(id.index()) { Entry::Vacant(e) => { - e.insert(Track { + e.insert(Resource { ref_count: ref_count.clone(), - init: default, - last: default, + state: Unit::new(default), epoch: id.epoch(), }); Query { @@ -272,7 +340,7 @@ impl + PartialEq> Tracker Entry::Occupied(e) => { assert_eq!(e.get().epoch, id.epoch()); Query { - usage: e.get().last, + usage: e.get().state.last, initialized: false, } } @@ -289,28 +357,16 @@ impl + PartialEq> Tracker ) -> Result, U> { match self.map.entry(id.index()) { Entry::Vacant(e) => { - e.insert(Track { + e.insert(Resource { ref_count: ref_count.clone(), - init: usage, - last: usage, + state: Unit::new(usage), epoch: id.epoch(), }); Ok(Tracktion::Init) } Entry::Occupied(mut e) => { assert_eq!(e.get().epoch, id.epoch()); - let old = e.get().last; - if usage == old { - Ok(Tracktion::Keep) - } else if permit.contains(TrackPermit::EXTEND) && !(old | usage).is_exclusive() { - e.get_mut().last = old | usage; - Ok(Tracktion::Extend { old }) - } else if permit.contains(TrackPermit::REPLACE) { - e.get_mut().last = usage; - Ok(Tracktion::Replace { old }) - } else { - Err(old) - } + e.get_mut().state.transit(usage, permit) } } } @@ -340,14 +396,14 @@ impl + PartialEq> Tracker } Entry::Occupied(mut e) => { assert_eq!(e.get().epoch, new.epoch); - let old = e.get().last; - if old != new.last { - let extended = old | new.last; + let old = e.get().state.last; + if old != new.state.last { + let extended = old | new.state.last; if extended.is_exclusive() { let id = I::new(index, new.epoch); - return Err((id, old .. new.last)); + return Err((id, old .. new.state.last)); } - e.get_mut().last = extended; + e.get_mut().state.last = extended; } } } @@ -355,19 +411,6 @@ impl + PartialEq> Tracker Ok(()) } - /// Return an iterator over used resources keys. - pub fn used<'a>(&'a self) -> impl 'a + Iterator { - self.map - .iter() - .map(|(&index, track)| I::new(index, track.epoch)) - } -} - -impl + PartialEq> Tracker { - fn clear(&mut self) { - self.map.clear(); - } - fn _get_with_usage<'a, T: 'a + Borrow>( &mut self, storage: &'a Storage, @@ -411,3 +454,43 @@ impl + PartialEq> }) } } + + +pub type BufferTracker = Tracker>; +pub type TextureTracker = Tracker>; +pub type TextureViewTracker = Tracker>; +pub type BindGroupTracker = Tracker>; + +#[derive(Debug)] +pub struct TrackerSet { + pub buffers: BufferTracker, + pub textures: TextureTracker, + pub views: TextureViewTracker, + pub bind_groups: BindGroupTracker, + //TODO: samplers +} + +impl TrackerSet { + pub fn new() -> Self { + TrackerSet { + buffers: BufferTracker::new(), + textures: TextureTracker::new(), + views: TextureViewTracker::new(), + bind_groups: BindGroupTracker::new(), + } + } + + pub fn clear(&mut self) { + self.buffers.clear(); + self.textures.clear(); + self.views.clear(); + self.bind_groups.clear(); + } + + pub fn consume_by_extend(&mut self, other: &Self) { + self.buffers.consume_by_extend(&other.buffers).unwrap(); + self.textures.consume_by_extend(&other.textures).unwrap(); + self.views.consume_by_extend(&other.views).unwrap(); + self.bind_groups.consume_by_extend(&other.bind_groups).unwrap(); + } +} From 444220c6eb53aa5f703cec14d5326d08bf050cc6 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 9 Jun 2019 00:00:17 -0400 Subject: [PATCH 03/25] Another iteration of internal tracking --- wgpu-native/src/track.rs | 626 ++++++++++++++++++++++++++++++++------- 1 file changed, 516 insertions(+), 110 deletions(-) diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 226d55b90f..83c167f488 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -22,107 +22,9 @@ use std::{ marker::PhantomData, mem, ops::{BitOr, Range}, + vec::Drain, }; -#[derive(Clone, Debug)] -pub struct RangedStates { - ranges: Vec<(Range, T)>, -} - -pub type TextureLayerStates = RangedStates; -pub type TextureStates2 = RangedStates; - -impl RangedStates { - fn isolate(&mut self, index: Range) -> &mut T { - let mut pos = self.ranges - .iter() - .position(|&(ref range, _)| index.start >= range.start) - .unwrap(); - let base_range = self.ranges[pos].0.clone(); - assert!(index.end <= base_range.end); - if base_range.start < index.start { - let value = ((base_range.start .. index.start), self.ranges[pos].1.clone()); - self.ranges.insert(pos, value); - pos += 1; - self.ranges[pos].0.start = index.start; - } - if base_range.end > index.end { - let value = ((index.end .. base_range.end), self.ranges[pos].1.clone()); - self.ranges.insert(pos + 1, value); - self.ranges[pos].0.end = index.end; - } - &mut self.ranges[pos].1 - } -} - -impl TextureStates2 { - fn change_state( - &mut self, level: hal::image::Level, layer: hal::image::Layer, usage: TextureUsage - ) -> Option { - let layer_states = self.isolate(level .. level + 1); - let cur_usage = layer_states.isolate(layer .. layer + 1); - if *cur_usage != usage { - Some(mem::replace(cur_usage, usage)) - } else { - None - } - } -} - - -pub enum PlaneStates { - Single(T), - Multi(Vec<(Range, T)>), -} - -impl Default for PlaneStates { - fn default() -> Self { - PlaneStates::Multi(Vec::new()) - } -} - -pub struct DepthStencilState { - depth: TextureUsage, - stencil: TextureUsage, -} - -pub struct TextureStates { - color_mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS]>, - depth_stencil: PlaneStates, -} - -impl TextureStates { - fn change<'a>( - &mut self, - what: hal::image::SubresourceRange, - usage: TextureUsage, - permit: TrackPermit, - fun: impl FnMut(hal::image::SubresourceRange, Tracktion), - ) { - /*if what.aspects.contains(hal::format::Aspects::COLOR) { - for level in what.levels.clone() { - match self.color_mips[level as usize] { - PlaneStates::Single(ref mut cur_usage) => { - assert_eq!(what.layers, 0 .. 1); - if *cur_usage != usage { - let sub = hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: 0 .. 1, - }; - fun(sub, *cur_usage); - *cur_usage = usage; - } - } - PlaneStates::Multi(ref mut layers) => { - let pos = - } - } - } - }*/ - } -} - #[derive(Clone, Debug, PartialEq)] #[allow(unused)] @@ -189,7 +91,7 @@ impl GenericUsage for DummyUsage { } /// A single unit of state tracking. -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct Unit { init: U, last: U, @@ -228,14 +130,7 @@ impl + PartialEq + GenericUsage> Unit { } } -#[derive(Clone, Debug)] -struct Resource { - ref_count: RefCount, - state: S, - epoch: Epoch, -} - -//TODO: consider having `I` as an associated type of `U`? +//TODO: consider having `I` as an associated type of `S`? #[derive(Debug)] pub struct Tracker { /// An association of known resource indices with their tracked states. @@ -300,7 +195,7 @@ impl Tracker { } /// Remove an id from the tracked map. - pub(crate) fn remove(&mut self, id: I) -> bool { + pub fn remove(&mut self, id: I) -> bool { match self.map.remove(&id.index()) { Some(resource) => { assert_eq!(resource.epoch, id.epoch()); @@ -455,7 +350,6 @@ impl + PartialEq> } } - pub type BufferTracker = Tracker>; pub type TextureTracker = Tracker>; pub type TextureViewTracker = Tracker>; @@ -494,3 +388,515 @@ impl TrackerSet { self.bind_groups.consume_by_extend(&other.bind_groups).unwrap(); } } + + +pub trait ResourceState: Clone + Default { + type Id: Copy + TypedId; + type Selector; + type Usage; + + fn query( + &self, + selector: Self::Selector, + ) -> Option; + + fn change( + &mut self, + id: Self::Id, + selector: Self::Selector, + usage: Self::Usage, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition>; + + fn merge( + &mut self, + id: Self::Id, + other: &Self, + stitch: Stitch, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition>; +} + +#[derive(Clone, Debug)] +struct Resource { + ref_count: RefCount, + state: S, + epoch: Epoch, +} + +pub struct PendingTransition { + pub id: S::Id, + pub selector: S::Selector, + pub usage: Range, +} + +struct ResourceTracker { + /// An association of known resource indices with their tracked states. + map: FastHashMap>, + /// Temporary storage for collecting transitions. + temp: Vec>, +} + +impl ResourceTracker { + pub fn new() -> Self { + ResourceTracker { + map: FastHashMap::default(), + temp: Vec::new(), + } + } + + /// Remove an id from the tracked map. + pub fn remove(&mut self, id: S::Id) -> bool { + match self.map.remove(&id.index()) { + Some(resource) => { + assert_eq!(resource.epoch, id.epoch()); + true + } + None => false, + } + } + + /// Return an iterator over used resources keys. + pub fn used<'a>(&'a self) -> impl 'a + Iterator { + self.map + .iter() + .map(|(&index, resource)| S::Id::new(index, resource.epoch)) + } + + fn clear(&mut self) { + self.map.clear(); + } + + /// Initialize a resource to be used. + pub fn init( + &mut self, + id: S::Id, + ref_count: &RefCount, + selector: S::Selector, + default: S::Usage, + ) -> bool { + let mut state = S::default(); + let _ = state.change( + id, + selector, + default, + None, + ); + self.map + .insert(id.index(), Resource { + ref_count: ref_count.clone(), + state, + epoch: id.epoch(), + }) + .is_none() + } + + /// Query a resource selector. Returns `Some(Usage)` only if + /// this usage is consistent across the given selector. + pub fn query( + &mut self, + id: S::Id, + selector: S::Selector, + ) -> Option { + let res = self.map.get(&id.index())?; + assert_eq!(res.epoch, id.epoch()); + res.state.query(selector) + } + + fn grab<'a>( + map: &'a mut FastHashMap>, + id: S::Id, + ref_count: &RefCount, + ) -> &'a mut Resource { + match map.entry(id.index()) { + Entry::Vacant(e) => { + e.insert(Resource { + ref_count: ref_count.clone(), + state: S::default(), + epoch: id.epoch(), + }) + } + Entry::Occupied(e) => { + assert_eq!(e.get().epoch, id.epoch()); + e.into_mut() + } + } + } + + /// Extend the usage of a specified resource. + pub fn change_extend( + &mut self, + id: S::Id, + ref_count: &RefCount, + selector: S::Selector, + usage: S::Usage, + ) -> Result<(), PendingTransition> { + Self::grab(&mut self.map, id, ref_count) + .state.change(id, selector, usage, None) + } + + /// Replace the usage of a specified resource. + pub fn change_replace( + &mut self, + id: S::Id, + ref_count: &RefCount, + selector: S::Selector, + usage: S::Usage, + ) -> Result>, PendingTransition> { + let res = Self::grab(&mut self.map, id, ref_count); + res.state.change(id, selector, usage, Some(&mut self.temp))?; + Ok(self.temp.drain(..)) + } + + /// Merge another tacker into `self` by extending the current states + /// without any transitions. + pub fn merge_extend( + &mut self, other: &Self + ) -> Result<(), PendingTransition> { + for (&index, new) in other.map.iter() { + match self.map.entry(index) { + Entry::Vacant(e) => { + e.insert(new.clone()); + } + Entry::Occupied(e) => { + assert_eq!(e.get().epoch, new.epoch); + let id = S::Id::new(index, new.epoch); + e.into_mut().state.merge(id, &new.state, Stitch::Last, None)?; + } + } + } + Ok(()) + } + + /// Merge another tacker, adding it's transitions to `self`. + /// Transitions the current usage to the new one. + pub fn merge_replace<'a>( + &'a mut self, + other: &'a Self, + stitch: Stitch, + ) -> Result>, PendingTransition> { + for (&index, new) in other.map.iter() { + match self.map.entry(index) { + Entry::Vacant(e) => { + e.insert(new.clone()); + } + Entry::Occupied(e) => { + assert_eq!(e.get().epoch, new.epoch); + let id = S::Id::new(index, new.epoch); + e.into_mut().state.merge(id, &new.state, stitch, Some(&mut self.temp))?; + } + } + } + Ok(self.temp.drain(..)) + } + + pub fn use_extend<'a, T: 'a + Borrow>( + &mut self, + storage: &'a Storage, + id: S::Id, + selector: S::Selector, + usage: S::Usage, + ) -> Result<&'a T, S::Usage> { + let item = &storage[id]; + self.change_extend(id, item.borrow(), selector, usage) + .map(|()| item) + .map_err(|pending| pending.usage.start) + } + + pub fn use_replace<'a, T: 'a + Borrow>( + &mut self, + storage: &'a Storage, + id: S::Id, + selector: S::Selector, + usage: S::Usage, + ) -> Result<(&'a T, Drain>), S::Usage> { + let item = &storage[id]; + self.change_replace(id, item.borrow(), selector, usage) + .map(|drain| (item, drain)) + .map_err(|pending| pending.usage.start) + } +} + +pub type BufferState = Unit; + +impl Default for BufferState { + fn default() -> Self { + BufferState { + init: BufferUsage::empty(), + last: BufferUsage::empty(), + } + } +} + +impl ResourceState for BufferState { + type Id = BufferId; + type Selector = (); + type Usage = BufferUsage; + + fn query( + &self, + _selector: Self::Selector, + ) -> Option { + Some(self.last) + } + + fn change( + &mut self, + id: Self::Id, + _selector: Self::Selector, + usage: Self::Usage, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + let old = self.last; + if usage != old { + let pending = PendingTransition { + id, + selector: (), + usage: old .. usage, + }; + self.last = match output { + Some(transitions) => { + transitions.push(pending); + usage + } + None => { + if !old.is_empty() && BufferUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + Ok(()) + } + + fn merge( + &mut self, + id: Self::Id, + other: &Self, + stitch: Stitch, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + let usage = other.select(stitch); + self.change(id, (), usage, output) + } +} + + +#[derive(Clone, Debug)] +pub struct RangedStates { + ranges: Vec<(Range, T)>, +} + +impl Default for RangedStates { + fn default() -> Self { + RangedStates { + ranges: Vec::new(), + } + } +} + +impl RangedStates { + fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { + let start_pos = match self.ranges + .iter() + .position(|pair| pair.0.end > index.start) + { + Some(pos) => pos, + None => { + let pos = self.ranges.len(); + self.ranges.push((index.clone(), default)); + return &mut self.ranges[pos ..]; + } + }; + + let mut pos = start_pos; + let mut range_pos = index.start; + loop { + let (range, unit) = self.ranges[pos].clone(); + if range.start >= index.end { + self.ranges.insert(pos, (range_pos .. index.end, default)); + pos += 1; + break; + } + if range.start > range_pos { + self.ranges.insert(pos, (range_pos .. range.start, default)); + pos += 1; + range_pos = range.start; + } + if range.end >= index.end { + self.ranges[pos].0.start = index.end; + self.ranges.insert(pos, (range_pos .. index.end, unit)); + pos += 1; + break; + } + pos += 1; + range_pos = range.end; + if pos == self.ranges.len() { + self.ranges.push((range_pos .. index.end, default)); + pos += 1; + break; + } + } + + &mut self.ranges[start_pos .. pos] + } +} + +type PlaneStates = RangedStates; + +#[derive(Clone)] +struct DepthStencilState { + depth: Unit, + stencil: Unit, +} + +#[derive(Clone, Default)] +struct TextureStates { + color_mips: ArrayVec<[PlaneStates>; MAX_MIP_LEVELS]>, + depth_stencil: PlaneStates, +} + +impl ResourceState for TextureStates { + type Id = TextureId; + type Selector = hal::image::SubresourceRange; + type Usage = TextureUsage; + + fn query( + &self, + selector: Self::Selector, + ) -> Option { + let mut usage = None; + if selector.aspects.contains(hal::format::Aspects::COLOR) { + let num_levels = self.color_mips.len(); + let layer_start = num_levels.min(selector.levels.start as usize); + let layer_end = num_levels.min(selector.levels.end as usize); + for layer in self.color_mips[layer_start .. layer_end].iter() { + for &(ref range, ref unit) in layer.ranges.iter() { + if range.end > selector.layers.start && range.start < selector.layers.end { + let old = usage.replace(unit.last); + if old.is_some() && old != usage { + return None + } + } + } + } + } + if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { + for &(ref range, ref ds) in self.depth_stencil.ranges.iter() { + if range.end > selector.layers.start && range.start < selector.layers.end { + if selector.aspects.contains(hal::format::Aspects::DEPTH) { + let old = usage.replace(ds.depth.last); + if old.is_some() && old != usage { + return None + } + } + if selector.aspects.contains(hal::format::Aspects::STENCIL) { + let old = usage.replace(ds.stencil.last); + if old.is_some() && old != usage { + return None + } + } + } + } + } + usage + } + + fn change( + &mut self, + id: Self::Id, + selector: Self::Selector, + usage: Self::Usage, + mut output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + if selector.aspects.contains(hal::format::Aspects::COLOR) { + while self.color_mips.len() < selector.levels.end as usize { + self.color_mips.push(PlaneStates::default()); + } + for level in selector.levels.clone() { + let layers = self + .color_mips[level as usize] + .isolate(&selector.layers, Unit::new(usage)); + for &mut (ref range, ref mut unit) in layers { + let old = unit.last; + if old == usage { + continue + } + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + } + } + if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { + unimplemented!() //TODO + } + Ok(()) + } + + fn merge( + &mut self, + _id: Self::Id, + _other: &Self, + _stitch: Stitch, + _output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + Ok(()) + } +} + + +#[derive(Clone, Debug, Default)] +pub struct TextureViewState; + +impl ResourceState for TextureViewState { + type Id = TextureViewId; + type Selector = (); + type Usage = (); + + fn query( + &self, + _selector: Self::Selector, + ) -> Option { + Some(()) + } + + fn change( + &mut self, + _id: Self::Id, + _selector: Self::Selector, + _usage: Self::Usage, + _output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + Ok(()) + } + + fn merge( + &mut self, + _id: Self::Id, + _other: &Self, + _stitch: Stitch, + _output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + Ok(()) + } +} From 2446190a0bbf21ececc8bce018494bf529140655 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 11 Jun 2019 22:10:52 -0400 Subject: [PATCH 04/25] Finished first implementation of layer tracking --- wgpu-native/src/track.rs | 252 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 243 insertions(+), 9 deletions(-) diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 83c167f488..89395bacbf 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -18,10 +18,13 @@ use hal::backend::FastHashMap; use std::{ borrow::Borrow, + cmp::Ordering, collections::hash_map::{Entry, Iter}, + iter::Peekable, marker::PhantomData, mem, ops::{BitOr, Range}, + slice, vec::Drain, }; @@ -91,7 +94,7 @@ impl GenericUsage for DummyUsage { } /// A single unit of state tracking. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Unit { init: U, last: U, @@ -551,7 +554,7 @@ impl ResourceTracker { /// Merge another tacker into `self` by extending the current states /// without any transitions. pub fn merge_extend( - &mut self, other: &Self + &mut self, other: &Self, ) -> Result<(), PendingTransition> { for (&index, new) in other.map.iter() { match self.map.entry(index) { @@ -696,7 +699,7 @@ impl Default for RangedStates { } } -impl RangedStates { +impl RangedStates { fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { let start_pos = match self.ranges .iter() @@ -741,11 +744,99 @@ impl RangedStates { &mut self.ranges[start_pos .. pos] } + + fn coalesce(&mut self) { + let mut num_removed = 0; + let mut iter = self.ranges.iter_mut(); + let mut cur = match iter.next() { + Some(elem) => elem, + None => return, + }; + while let Some(next) = iter.next() { + if cur.0.end == next.0.start && cur.1 == next.1 { + num_removed += 1; + cur.0.end = next.0.end; + next.0.end = next.0.start; + } else { + cur = next; + } + } + if num_removed != 0 { + self.ranges.retain(|pair| pair.0.start != pair.0.end); + } + } +} + +struct Merge<'a, I, T> { + base: I, + sa: Peekable, T)>>, + sb: Peekable, T)>>, +} + +impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { + type Item = (Range, Range); + fn next(&mut self) -> Option { + match (self.sa.peek(), self.sb.peek()) { + // we have both streams + (Some(&(ref ra, va)), Some(&(ref rb, vb))) => { + let (range, usage) = if ra.start < self.base { // in the middle of the left stream + if self.base == rb.start { // right stream is starting + debug_assert!(self.base < ra.end); + (self.base .. ra.end.min(rb.end), *va .. *vb) + } else { // right hasn't started yet + debug_assert!(self.base < rb.start); + (self.base .. rb.start, *va .. *va) + } + } else if rb.start < self.base { // in the middle of the right stream + if self.base == ra.start { // left stream is starting + debug_assert!(self.base < rb.end); + (self.base .. ra.end.min(rb.end), *va .. *vb) + } else { // left hasn't started yet + debug_assert!(self.base < ra.start); + (self.base .. ra.start, *vb .. *vb) + } + } else { // no active streams + match ra.start.cmp(&rb.start) { + // both are starting + Ordering::Equal => (ra.start .. ra.end.min(rb.end), *va .. *vb), + // only left is starting + Ordering::Less => (ra.start .. rb.start, *va .. *va), + // only right is starting + Ordering::Greater => (rb.start .. ra.start, *vb .. *vb), + } + }; + self.base = range.end; + if ra.end == range.end { + let _ = self.sa.next(); + } + if rb.end == range.end { + let _ = self.sb.next(); + } + Some((range, usage)) + } + // only right stream + (None, Some(&(ref rb, vb))) => { + let range = self.base.max(rb.start) .. rb.end; + self.base = rb.end; + let _ = self.sb.next(); + Some((range, *vb .. *vb)) + } + // only left stream + (Some(&(ref ra, va)), None) => { + let range = self.base.max(ra.start) .. ra.end; + self.base = ra.end; + let _ = self.sa.next(); + Some((range, *va .. *va)) + } + // done + (None, None) => None, + } + } } type PlaneStates = RangedStates; -#[derive(Clone)] +#[derive(Clone, Copy, PartialEq)] struct DepthStencilState { depth: Unit, stencil: Unit, @@ -848,18 +939,161 @@ impl ResourceState for TextureStates { } } if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - unimplemented!() //TODO + for level in selector.levels.clone() { + let ds_state = DepthStencilState { + depth: Unit::new(usage), + stencil: Unit::new(usage), + }; + for &mut (ref range, ref mut unit) in self.depth_stencil + .isolate(&selector.layers, ds_state) + { + //TODO: check if anything needs to be done when only one of the depth/stencil + // is selected? + if unit.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { + let old = unit.depth.last; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.depth.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + if unit.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { + let old = unit.stencil.last; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.stencil.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + } + } } Ok(()) } fn merge( &mut self, - _id: Self::Id, - _other: &Self, - _stitch: Stitch, - _output: Option<&mut Vec>>, + id: Self::Id, + other: &Self, + stitch: Stitch, + mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { + let mut temp_color = Vec::new(); + while self.color_mips.len() < other.color_mips.len() { + self.color_mips.push(PlaneStates::default()); + } + for (mip_id, (mip_self, mip_other)) in self.color_mips + .iter_mut() + .zip(&other.color_mips) + .enumerate() + { + temp_color.extend(Merge { + base: 0, + sa: mip_self.ranges.iter().peekable(), + sb: mip_other.ranges.iter().peekable(), + }); + mip_self.ranges.clear(); + for (layers, states) in temp_color.drain(..) { + let color_usage = states.start.last .. states.end.select(stitch); + if let Some(out) = output.as_mut() { + if color_usage.start != color_usage.end { + let level = mip_id as hal::image::Level; + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: layers.clone(), + }, + usage: color_usage.clone(), + }); + } + } + mip_self.ranges.push((layers, Unit { + init: states.start.init, + last: color_usage.end, + })); + } + } + + let mut temp_ds = Vec::new(); + temp_ds.extend(Merge { + base: 0, + sa: self.depth_stencil.ranges.iter().peekable(), + sb: other.depth_stencil.ranges.iter().peekable(), + }); + self.depth_stencil.ranges.clear(); + for (layers, states) in temp_ds.drain(..) { + let usage_depth = states.start.depth.last .. states.end.depth.select(stitch); + let usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); + if let Some(out) = output.as_mut() { + if usage_depth.start != usage_depth.end { + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_depth.clone(), + }); + } + if usage_stencil.start != usage_stencil.end { + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_stencil.clone(), + }); + } + } + self.depth_stencil.ranges.push((layers, DepthStencilState { + depth: Unit { + init: states.start.depth.init, + last: usage_depth.end, + }, + stencil: Unit { + init: states.start.stencil.init, + last: usage_stencil.end, + }, + })); + } + Ok(()) } } From bc7b842f12873b9c7d190ee1f3837104131525b4 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 12 Jun 2019 12:07:14 -0400 Subject: [PATCH 05/25] Moving the code to use the new tracker API --- wgpu-native/src/binding_model.rs | 1 - wgpu-native/src/command/allocator.rs | 2 - wgpu-native/src/command/compute.rs | 1 - wgpu-native/src/command/mod.rs | 144 ++++++++++++++-------- wgpu-native/src/command/render.rs | 11 +- wgpu-native/src/command/transfer.rs | 174 +++++++++++++++------------ wgpu-native/src/device.rs | 79 ++++++------ wgpu-native/src/resource.rs | 7 ++ wgpu-native/src/swap_chain.rs | 20 ++- wgpu-native/src/track.rs | 105 ++++++++++++++-- 10 files changed, 344 insertions(+), 200 deletions(-) diff --git a/wgpu-native/src/binding_model.rs b/wgpu-native/src/binding_model.rs index bbb97f8a16..8867451330 100644 --- a/wgpu-native/src/binding_model.rs +++ b/wgpu-native/src/binding_model.rs @@ -105,7 +105,6 @@ pub struct BindGroupDescriptor { pub bindings_length: usize, } -#[derive(Debug)] pub struct BindGroup { pub(crate) raw: DescriptorSet, pub(crate) device_id: Stored, diff --git a/wgpu-native/src/command/allocator.rs b/wgpu-native/src/command/allocator.rs index 7fed4ca9b4..60c6cf958d 100644 --- a/wgpu-native/src/command/allocator.rs +++ b/wgpu-native/src/command/allocator.rs @@ -24,7 +24,6 @@ impl CommandPool { } } -#[derive(Debug)] struct Inner { pools: HashMap>, pending: Vec>, @@ -42,7 +41,6 @@ impl Inner { } } -#[derive(Debug)] pub struct CommandAllocator { queue_family: hal::queue::QueueFamilyId, inner: Mutex>, diff --git a/wgpu-native/src/command/compute.rs b/wgpu-native/src/command/compute.rs index 3d097480d4..79f5930162 100644 --- a/wgpu-native/src/command/compute.rs +++ b/wgpu-native/src/command/compute.rs @@ -17,7 +17,6 @@ use hal::{self, command::RawCommandBuffer}; use std::{iter, slice}; -#[derive(Debug)] pub struct ComputePass { raw: B::CommandBuffer, cmb_id: Stored, diff --git a/wgpu-native/src/command/mod.rs b/wgpu-native/src/command/mod.rs index 75ab60ea21..2fdeb72577 100644 --- a/wgpu-native/src/command/mod.rs +++ b/wgpu-native/src/command/mod.rs @@ -21,7 +21,7 @@ use crate::{ hub::{Storage, HUB}, resource::TexturePlacement, swap_chain::{SwapChainLink, SwapImageEpoch}, - track::{DummyUsage, Stitch, TrackerSet}, + track::{Stitch, TrackerSet}, BufferHandle, BufferId, Color, @@ -88,7 +88,6 @@ pub struct RenderPassDescriptor { pub depth_stencil_attachment: *const RenderPassDepthStencilAttachmentDescriptor, } -#[derive(Debug)] pub struct CommandBuffer { pub(crate) raw: Vec, is_recording: bool, @@ -110,34 +109,31 @@ impl CommandBufferHandle { ) { let buffer_barriers = base.buffers - .consume_by_replace(&head.buffers, stitch) - .map(|(id, transit)| { - let b = &buffer_guard[id]; - trace!("transit buffer {:?} {:?}", id, transit); + .merge_replace(&head.buffers, stitch) + .unwrap() + .map(|pending| { + trace!("transit buffer {:?}", pending); hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(transit.start) - .. conv::map_buffer_state(transit.end), - target: &b.raw, + states: pending.to_states(), + target: &buffer_guard[pending.id].raw, range: None .. None, families: None, } }); let texture_barriers = base .textures - .consume_by_replace(&head.textures, stitch) - .map(|(id, transit)| { - let t = &texture_guard[id]; - trace!("transit texture {:?} {:?}", id, transit); - let aspects = t.full_range.aspects; + .merge_replace(&head.textures, stitch) + .unwrap() + .map(|pending| { + trace!("transit texture {:?}", pending); hal::memory::Barrier::Image { - states: conv::map_texture_state(transit.start, aspects) - .. conv::map_texture_state(transit.end, aspects), - target: &t.raw, - range: t.full_range.clone(), //TODO? + states: pending.to_states(), + target: &texture_guard[pending.id].raw, + range: pending.selector, families: None, } }); - base.views.consume_by_extend(&head.views).unwrap(); + base.views.merge_extend(&head.views).unwrap(); let stages = all_buffer_stages() | all_image_stages(); unsafe { @@ -175,6 +171,7 @@ pub fn command_encoder_begin_render_pass( let mut cmb_guard = HUB.command_buffers.write(); let cmb = &mut cmb_guard[command_encoder_id]; let device = &device_guard[cmb.device_id.value]; + let texture_guard = HUB.textures.read(); let view_guard = HUB.texture_views.read(); let mut current_comb = device.com_allocator.extend(cmb); @@ -185,6 +182,7 @@ pub fn command_encoder_begin_render_pass( ); } let mut extent = None; + let mut barriers = Vec::new(); let color_attachments = unsafe { slice::from_raw_parts(desc.color_attachments, desc.color_attachments_length) }; @@ -195,38 +193,65 @@ pub fn command_encoder_begin_render_pass( let swap_chain_links = &mut cmb.swap_chain_links; let depth_stencil = depth_stencil_attachment.map(|at| { - 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 { extent = Some(view.extent); } - trackers - .views - .query(at.attachment, &view.life_guard.ref_count, DummyUsage); - let query = trackers.textures.query( + let old_layout = match trackers.textures.query( view.texture_id.value, - &view.texture_id.ref_count, - TextureUsage::empty(), - ); - let (_, layout) = conv::map_texture_state( - query.usage, - hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, - ); + view.range.clone(), + ) { + 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. + let (texture, pending) = trackers.textures + .use_replace( + &*texture_guard, + view.texture_id.value, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ) + .unwrap(); + barriers.extend(pending.map(|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)), 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: layout .. layout, + layouts: old_layout .. hal::image::Layout::DepthStencilAttachmentOptimal, } }); let color_keys = color_attachments.iter().map(|at| { - 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 { + extent = Some(view.extent); + } if view.is_owned_by_swap_chain { - let link = match HUB.textures.read()[view.texture_id.value].placement { + let link = match texture_guard[view.texture_id.value].placement { TexturePlacement::SwapChain(ref link) => SwapChainLink { swap_chain_id: link.swap_chain_id.clone(), epoch: *link.epoch.lock(), @@ -237,26 +262,39 @@ pub fn command_encoder_begin_render_pass( swap_chain_links.push(link); } - if let Some(ex) = extent { - assert_eq!(ex, view.extent); - } else { - extent = Some(view.extent); - } - trackers - .views - .query(at.attachment, &view.life_guard.ref_count, DummyUsage); - let query = trackers.textures.query( + let old_layout = match trackers.textures.query( view.texture_id.value, - &view.texture_id.ref_count, - TextureUsage::empty(), - ); - let (_, layout) = conv::map_texture_state(query.usage, hal::format::Aspects::COLOR); + view.range.clone(), + ) { + Some(usage) => { + 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. + let (texture, pending) = trackers.textures + .use_replace( + &*texture_guard, + view.texture_id.value, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ) + .unwrap(); + barriers.extend(pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture.raw, + families: None, + range: pending.selector, + })); + hal::image::Layout::ColorAttachmentOptimal + } + }; hal::pass::Attachment { format: Some(conv::map_texture_format(view.format)), samples: view.samples, ops: conv::map_load_store_ops(at.load_op, at.store_op), stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts: layout .. layout, + layouts: old_layout .. hal::image::Layout::ColorAttachmentOptimal, } }); @@ -266,6 +304,16 @@ pub fn command_encoder_begin_render_pass( } }; + if !barriers.is_empty() { + unsafe { + current_comb.pipeline_barrier( + all_image_stages() .. all_image_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + } + } + let mut render_pass_cache = device.render_passes.lock(); let render_pass = match render_pass_cache.entry(rp_key.clone()) { Entry::Occupied(e) => e.into_mut(), diff --git a/wgpu-native/src/command/render.rs b/wgpu-native/src/command/render.rs index e8fc3d214d..cfa15b0735 100644 --- a/wgpu-native/src/command/render.rs +++ b/wgpu-native/src/command/render.rs @@ -109,7 +109,6 @@ impl VertexState { } } -#[derive(Debug)] pub struct RenderPass { raw: B::CommandBuffer, cmb_id: Stored, @@ -192,7 +191,7 @@ pub extern "C" fn wgpu_render_pass_end_pass(pass_id: RenderPassId) -> CommandBuf unsafe { last.finish() }; } None => { - cmb.trackers.consume_by_extend(&pass.trackers); + cmb.trackers.merge_extend(&pass.trackers); } } @@ -232,7 +231,7 @@ pub extern "C" fn wgpu_render_pass_set_bind_group( } } - pass.trackers.consume_by_extend(&bind_group.used); + pass.trackers.merge_extend(&bind_group.used); if let Some((pipeline_layout_id, follow_up)) = pass.binder @@ -282,7 +281,7 @@ pub extern "C" fn wgpu_render_pass_set_index_buffer( let buffer = pass .trackers .buffers - .get_with_extended_usage(&*buffer_guard, buffer_id, BufferUsage::INDEX) + .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) .unwrap(); let range = offset .. buffer.size; @@ -316,7 +315,7 @@ pub extern "C" fn wgpu_render_pass_set_vertex_buffers( for (vbs, (&id, &offset)) in pass.vertex_state.inputs.iter_mut().zip(buffers.iter().zip(offsets)) { let buffer = pass.trackers .buffers - .get_with_extended_usage(&*buffer_guard, id, BufferUsage::VERTEX) + .use_extend(&*buffer_guard, id, (), BufferUsage::VERTEX) .unwrap(); vbs.total_size = buffer.size - offset; } @@ -450,7 +449,7 @@ pub extern "C" fn wgpu_render_pass_set_pipeline( let buffer = pass .trackers .buffers - .get_with_extended_usage(&*buffer_guard, buffer_id, BufferUsage::INDEX) + .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) .unwrap(); let view = hal::buffer::IndexBufferView { diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index 10f9aae4d9..c08659a40e 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -39,6 +39,27 @@ pub struct TextureCopyView { pub origin: Origin3d, } +impl TextureCopyView { + fn to_selector(&self) -> hal::image::SubresourceRange { + let level = self.mip_level as hal::image::Level; + let layer = self.array_layer as hal::image::Layer; + hal::image::SubresourceRange { + aspects: hal::format::Aspects::all(), //TODO + levels: level .. level + 1, + layers: layer .. layer + 1, + } + } + + fn to_sub_layers(&self) -> hal::image::SubresourceLayers { + let layer = self.array_layer as hal::image::Layer; + hal::image::SubresourceLayers { + aspects: hal::format::Aspects::all(), //TODO + level: self.mip_level as hal::image::Level, + layers: layer .. layer + 1, + } + } +} + #[no_mangle] pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( command_buffer_id: CommandBufferId, @@ -51,30 +72,33 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( let mut cmb_guard = HUB.command_buffers.write(); let cmb = &mut cmb_guard[command_buffer_id]; let buffer_guard = HUB.buffers.read(); + // we can't hold both src_pending and dst_pending in scope because they + // borrow the buffer tracker mutably... + let mut barriers = Vec::new(); - let (src_buffer, src_usage) = cmb + let (src_buffer, src_pending) = cmb .trackers .buffers - .get_with_replaced_usage(&*buffer_guard, src, BufferUsage::TRANSFER_SRC) + .use_replace(&*buffer_guard, src, (), BufferUsage::TRANSFER_SRC) .unwrap(); - let src_barrier = src_usage.map(|old| hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(old) .. hal::buffer::Access::TRANSFER_READ, + barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), target: &src_buffer.raw, families: None, range: None .. None, - }); + })); - let (dst_buffer, dst_usage) = cmb + let (dst_buffer, dst_pending) = cmb .trackers .buffers - .get_with_replaced_usage(&*buffer_guard, dst, BufferUsage::TRANSFER_DST) + .use_replace(&*buffer_guard, dst, (), BufferUsage::TRANSFER_DST) .unwrap(); - let dst_barrier = dst_usage.map(|old| hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(old) .. hal::buffer::Access::TRANSFER_WRITE, + barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), target: &dst_buffer.raw, families: None, range: None .. None, - }); + })); let region = hal::command::BufferCopy { src: src_offset, @@ -86,7 +110,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( cmb_raw.pipeline_barrier( all_buffer_stages() .. all_buffer_stages(), hal::memory::Dependencies::empty(), - src_barrier.into_iter().chain(dst_barrier), + barriers, ); cmb_raw.copy_buffer(&src_buffer.raw, &dst_buffer.raw, iter::once(region)); } @@ -104,34 +128,33 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); - let (src_buffer, src_usage) = cmb + let (src_buffer, src_pending) = cmb .trackers .buffers - .get_with_replaced_usage(&*buffer_guard, source.buffer, BufferUsage::TRANSFER_SRC) + .use_replace(&*buffer_guard, source.buffer, (), BufferUsage::TRANSFER_SRC) .unwrap(); - let src_barrier = src_usage.map(|old| hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(old) .. hal::buffer::Access::TRANSFER_READ, + let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), target: &src_buffer.raw, families: None, range: None .. None, }); - let (dst_texture, dst_usage) = cmb + let (dst_texture, dst_pending) = cmb .trackers .textures - .get_with_replaced_usage( + .use_replace( &*texture_guard, destination.texture, + destination.to_selector(), TextureUsage::TRANSFER_DST, ) .unwrap(); - let aspects = dst_texture.full_range.aspects; - let dst_texture_state = conv::map_texture_state(TextureUsage::TRANSFER_DST, aspects); - let dst_barrier = dst_usage.map(|old| hal::memory::Barrier::Image { - states: conv::map_texture_state(old, aspects) .. dst_texture_state, + let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), target: &dst_texture.raw, families: None, - range: dst_texture.full_range.clone(), + range: pending.selector, }); if let TexturePlacement::SwapChain(ref link) = dst_texture.placement { @@ -152,11 +175,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( buffer_offset: source.offset, buffer_width, buffer_height: source.image_height, - image_layers: hal::image::SubresourceLayers { - aspects, //TODO - level: destination.mip_level as hal::image::Level, - layers: destination.array_layer as u16 .. destination.array_layer as u16 + 1, - }, + image_layers: destination.to_sub_layers(), image_offset: conv::map_origin(destination.origin), image_extent: conv::map_extent(copy_size), }; @@ -166,12 +185,12 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( cmb_raw.pipeline_barrier( stages .. stages, hal::memory::Dependencies::empty(), - src_barrier.into_iter().chain(dst_barrier), + src_barriers.chain(dst_barriers), ); cmb_raw.copy_buffer_to_image( &src_buffer.raw, &dst_texture.raw, - dst_texture_state.1, + hal::image::Layout::TransferDstOptimal, iter::once(region), ); } @@ -189,18 +208,21 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); - let (src_texture, src_usage) = cmb + let (src_texture, src_pending) = cmb .trackers .textures - .get_with_replaced_usage(&*texture_guard, source.texture, TextureUsage::TRANSFER_SRC) + .use_replace( + &*texture_guard, + source.texture, + source.to_selector(), + TextureUsage::TRANSFER_SRC, + ) .unwrap(); - let aspects = src_texture.full_range.aspects; - let src_texture_state = conv::map_texture_state(TextureUsage::TRANSFER_SRC, aspects); - let src_barrier = src_usage.map(|old| hal::memory::Barrier::Image { - states: conv::map_texture_state(old, aspects) .. src_texture_state, + let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), target: &src_texture.raw, families: None, - range: src_texture.full_range.clone(), + range: pending.selector, }); match src_texture.placement { TexturePlacement::SwapChain(_) => unimplemented!(), @@ -208,17 +230,18 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( TexturePlacement::Memory(_) => (), } - let (dst_buffer, dst_usage) = cmb + let (dst_buffer, dst_barriers) = cmb .trackers .buffers - .get_with_replaced_usage( + .use_replace( &*buffer_guard, destination.buffer, + (), BufferUsage::TRANSFER_DST, ) .unwrap(); - let dst_barrier = dst_usage.map(|old| hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(old) .. hal::buffer::Access::TRANSFER_WRITE, + let dst_barrier = dst_barriers.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), target: &dst_buffer.raw, families: None, range: None .. None, @@ -234,11 +257,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( buffer_offset: destination.offset, buffer_width, buffer_height: destination.image_height, - image_layers: hal::image::SubresourceLayers { - aspects, //TODO - level: source.mip_level as hal::image::Level, - layers: source.array_layer as u16 .. source.array_layer as u16 + 1, - }, + image_layers: source.to_sub_layers(), image_offset: conv::map_origin(source.origin), image_extent: conv::map_extent(copy_size), }; @@ -248,11 +267,11 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( cmb_raw.pipeline_barrier( stages .. stages, hal::memory::Dependencies::empty(), - src_barrier.into_iter().chain(dst_barrier), + src_barriers.chain(dst_barrier), ); cmb_raw.copy_image_to_buffer( &src_texture.raw, - src_texture_state.1, + hal::image::Layout::TransferSrcOptimal, &dst_buffer.raw, iter::once(region), ); @@ -269,38 +288,43 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( let mut cmb_guard = HUB.command_buffers.write(); let cmb = &mut cmb_guard[command_buffer_id]; let texture_guard = HUB.textures.read(); + // we can't hold both src_pending and dst_pending in scope because they + // borrow the buffer tracker mutably... + let mut barriers = Vec::new(); - let (src_texture, src_usage) = cmb + let (src_texture, src_pending) = cmb .trackers .textures - .get_with_replaced_usage(&*texture_guard, source.texture, TextureUsage::TRANSFER_SRC) + .use_replace( + &*texture_guard, + source.texture, + source.to_selector(), + TextureUsage::TRANSFER_SRC, + ) .unwrap(); - let (dst_texture, dst_usage) = cmb + barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &src_texture.raw, + families: None, + range: pending.selector, + })); + + let (dst_texture, dst_pending) = cmb .trackers .textures - .get_with_replaced_usage( + .use_replace( &*texture_guard, destination.texture, + destination.to_selector(), TextureUsage::TRANSFER_DST, ) .unwrap(); - - let aspects = src_texture.full_range.aspects & dst_texture.full_range.aspects; - let src_texture_state = conv::map_texture_state(TextureUsage::TRANSFER_SRC, aspects); - let dst_texture_state = conv::map_texture_state(TextureUsage::TRANSFER_DST, aspects); - - let src_barrier = src_usage.map(|old| hal::memory::Barrier::Image { - states: conv::map_texture_state(old, aspects) .. src_texture_state, - target: &src_texture.raw, - families: None, - range: src_texture.full_range.clone(), - }); - let dst_barrier = dst_usage.map(|old| hal::memory::Barrier::Image { - states: conv::map_texture_state(old, aspects) .. dst_texture_state, + barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), target: &dst_texture.raw, families: None, - range: dst_texture.full_range.clone(), - }); + range: pending.selector, + })); if let TexturePlacement::SwapChain(ref link) = dst_texture.placement { cmb.swap_chain_links.alloc().init(SwapChainLink { @@ -311,17 +335,9 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( } let region = hal::command::ImageCopy { - src_subresource: hal::image::SubresourceLayers { - aspects, - level: source.mip_level as hal::image::Level, - layers: source.array_layer as u16 .. source.array_layer as u16 + 1, - }, + src_subresource: source.to_sub_layers(), src_offset: conv::map_origin(source.origin), - dst_subresource: hal::image::SubresourceLayers { - aspects, - level: destination.mip_level as hal::image::Level, - layers: destination.array_layer as u16 .. destination.array_layer as u16 + 1, - }, + dst_subresource: destination.to_sub_layers(), dst_offset: conv::map_origin(destination.origin), extent: conv::map_extent(copy_size), }; @@ -330,13 +346,13 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( cmb_raw.pipeline_barrier( all_image_stages() .. all_image_stages(), hal::memory::Dependencies::empty(), - src_barrier.into_iter().chain(dst_barrier), + barriers, ); cmb_raw.copy_image( &src_texture.raw, - src_texture_state.1, + hal::image::Layout::TransferSrcOptimal, &dst_texture.raw, - dst_texture_state.1, + hal::image::Layout::TransferDstOptimal, iter::once(region), ); } diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 7f2e16a844..dbd3f532a2 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -6,7 +6,7 @@ use crate::{ pipeline, resource, swap_chain, - track::{DummyUsage, Stitch, TrackPermit, TrackerSet, Tracktion}, + track::{Stitch, TrackerSet}, AdapterId, BindGroupId, BufferAddress, @@ -605,12 +605,12 @@ pub fn device_track_buffer( ref_count: RefCount, flags: resource::BufferUsage, ) { - let query = HUB.devices.read()[device_id] + let ok = HUB.devices.read()[device_id] .trackers .lock() .buffers - .query(buffer_id, &ref_count, flags); - assert!(query.initialized); + .init(buffer_id, &ref_count, (), flags); + assert!(ok); } #[cfg(feature = "local")] @@ -737,17 +737,23 @@ pub fn device_create_texture( } } -pub fn device_track_texture(device_id: DeviceId, texture_id: TextureId, ref_count: RefCount) { - let query = HUB.devices.read()[device_id] +pub fn device_track_texture( + device_id: DeviceId, + texture_id: TextureId, + ref_count: RefCount, + full_range: hal::image::SubresourceRange, +) { + let ok = HUB.devices.read()[device_id] .trackers .lock() .textures - .query( + .init( texture_id, &ref_count, + full_range, resource::TextureUsage::UNINITIALIZED, ); - assert!(query.initialized); + assert!(ok); } #[cfg(feature = "local")] @@ -758,8 +764,9 @@ pub extern "C" fn wgpu_device_create_texture( ) -> TextureId { let texture = device_create_texture(device_id, desc); let ref_count = texture.life_guard.ref_count.clone(); + let range = texture.full_range; let id = HUB.textures.register_local(texture); - device_track_texture(device_id, id, ref_count); + device_track_texture(device_id, id, ref_count, range); id } @@ -781,7 +788,7 @@ pub fn texture_create_view( view_kind, conv::map_texture_format(format), hal::format::Swizzle::NO, - range, + range.clone(), ) .unwrap() }; @@ -795,6 +802,7 @@ pub fn texture_create_view( format: texture.format, extent: texture.kind.extent(), samples: texture.kind.num_samples(), + range, is_owned_by_swap_chain: false, life_guard: LifeGuard::new(), } @@ -802,12 +810,12 @@ pub fn texture_create_view( pub fn device_track_view(texture_id: TextureId, view_id: TextureViewId, ref_count: RefCount) { let device_id = HUB.textures.read()[texture_id].device_id.value; - let query = HUB.devices.read()[device_id] + let ok = HUB.devices.read()[device_id] .trackers .lock() .views - .query(view_id, &ref_count, DummyUsage); - assert!(query.initialized); + .init(view_id, &ref_count, (), ()); + assert!(ok); } #[cfg(feature = "local")] @@ -1067,7 +1075,7 @@ pub fn device_create_bind_group( ); let buffer = used .buffers - .get_with_extended_usage(&*buffer_guard, bb.buffer, usage) + .use_extend(&*buffer_guard, bb.buffer, (), usage) .unwrap(); let range = Some(bb.offset) .. Some(bb.offset + bb.size); hal::pso::Descriptor::Buffer(&buffer.raw, range) @@ -1089,14 +1097,15 @@ pub fn device_create_bind_group( ), _ => panic!("Mismatched texture binding for {:?}", decl), }; - let view = &texture_view_guard[id]; - used.views.query(id, &view.life_guard.ref_count, DummyUsage); + let view = used.views + .use_extend(&*texture_view_guard, id, (), ()) + .unwrap(); used.textures - .transit( + .change_extend( view.texture_id.value, &view.texture_id.ref_count, + view.range.clone(), usage, - TrackPermit::EXTEND, ) .unwrap(); hal::pso::Descriptor::Image(&view.raw, image_layout) @@ -1129,15 +1138,15 @@ pub fn device_create_bind_group( pub fn device_track_bind_group( device_id: DeviceId, - buffer_id: BindGroupId, + bind_group_id: BindGroupId, ref_count: RefCount, ) { - let query = HUB.devices.read()[device_id] + let ok = HUB.devices.read()[device_id] .trackers .lock() .bind_groups - .query(buffer_id, &ref_count, DummyUsage); - assert!(query.initialized); + .init(bind_group_id, &ref_count, (), ()); + assert!(ok); } #[cfg(feature = "local")] @@ -1902,19 +1911,12 @@ pub extern "C" fn wgpu_buffer_map_read_async( let device_guard = HUB.devices.read(); let device = &device_guard[device_id]; - let usage = resource::BufferUsage::MAP_READ; - match device + device .trackers .lock() .buffers - .transit(buffer_id, &ref_count, usage, TrackPermit::REPLACE) - { - Ok(Tracktion::Keep) => {} - Ok(Tracktion::Replace { .. }) => { - //TODO: launch a memory barrier into `HOST_READ` access? - } - other => panic!("Invalid mapping transition {:?}", other), - } + .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_READ) + .unwrap(); device.pending.lock().map(buffer_id, ref_count); } @@ -1945,19 +1947,12 @@ pub extern "C" fn wgpu_buffer_map_write_async( let device_guard = HUB.devices.read(); let device = &device_guard[device_id]; - let usage = resource::BufferUsage::MAP_WRITE; - match device + device .trackers .lock() .buffers - .transit(buffer_id, &ref_count, usage, TrackPermit::REPLACE) - { - Ok(Tracktion::Keep) => {} - Ok(Tracktion::Replace { .. }) => { - //TODO: launch a memory barrier into `HOST_WRITE` access? - } - other => panic!("Invalid mapping transition {:?}", other), - } + .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_WRITE) + .unwrap(); device.pending.lock().map(buffer_id, ref_count); } diff --git a/wgpu-native/src/resource.rs b/wgpu-native/src/resource.rs index e82e115c20..285f3476a2 100644 --- a/wgpu-native/src/resource.rs +++ b/wgpu-native/src/resource.rs @@ -255,11 +255,18 @@ pub struct TextureView { pub(crate) format: TextureFormat, pub(crate) extent: hal::image::Extent, pub(crate) samples: hal::image::NumSamples, + pub(crate) range: hal::image::SubresourceRange, pub(crate) is_owned_by_swap_chain: bool, #[cfg_attr(not(feature = "local"), allow(dead_code))] pub(crate) life_guard: LifeGuard, } +impl Borrow for TextureView { + fn borrow(&self) -> &RefCount { + &self.life_guard.ref_count + } +} + #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum AddressMode { diff --git a/wgpu-native/src/swap_chain.rs b/wgpu-native/src/swap_chain.rs index 64a66a831f..f49f81277b 100644 --- a/wgpu-native/src/swap_chain.rs +++ b/wgpu-native/src/swap_chain.rs @@ -3,7 +3,6 @@ use crate::{ device::all_image_stages, hub::HUB, resource, - track::TrackPermit, DeviceId, Extent3d, Stored, @@ -231,27 +230,24 @@ pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: SwapChainId) { //TODO: support for swapchain being sampled or read by the shader? trace!("transit {:?} to present", frame.texture_id.value); - let barrier = device - .trackers - .lock() - .textures - .transit( + let mut trackers = device.trackers.lock(); + let barriers = trackers.textures + .change_replace( frame.texture_id.value, &texture.life_guard.ref_count, + texture.full_range.clone(), resource::TextureUsage::UNINITIALIZED, - TrackPermit::REPLACE, ) .unwrap() - .into_source() - .map(|old| hal::memory::Barrier::Image { - states: conv::map_texture_state(old, hal::format::Aspects::COLOR) + .map(|pending| hal::memory::Barrier::Image { + states: conv::map_texture_state(pending.usage.start, hal::format::Aspects::COLOR) .. ( hal::image::Access::COLOR_ATTACHMENT_WRITE, hal::image::Layout::Present, ), target: &texture.raw, families: None, - range: texture.full_range.clone(), + range: pending.selector, }); let err = unsafe { @@ -259,7 +255,7 @@ pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: SwapChainId) { frame.comb.pipeline_barrier( all_image_stages() .. hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT, hal::memory::Dependencies::empty(), - barrier, + barriers, ); frame.comb.finish(); diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 89395bacbf..1a62fe0c4d 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -1,6 +1,7 @@ use crate::{ - hub::Storage, + conv, device::MAX_MIP_LEVELS, + hub::Storage, resource::{BufferUsage, TextureUsage}, BufferId, Epoch, @@ -359,7 +360,7 @@ pub type TextureViewTracker = Tracker>; pub type BindGroupTracker = Tracker>; #[derive(Debug)] -pub struct TrackerSet { +pub struct TrackerSetOld { pub buffers: BufferTracker, pub textures: TextureTracker, pub views: TextureViewTracker, @@ -367,9 +368,9 @@ pub struct TrackerSet { //TODO: samplers } -impl TrackerSet { +impl TrackerSetOld { pub fn new() -> Self { - TrackerSet { + TrackerSetOld { buffers: BufferTracker::new(), textures: TextureTracker::new(), views: TextureViewTracker::new(), @@ -427,13 +428,14 @@ struct Resource { epoch: Epoch, } +#[derive(Debug)] pub struct PendingTransition { pub id: S::Id, pub selector: S::Selector, pub usage: Range, } -struct ResourceTracker { +pub struct ResourceTracker { /// An association of known resource indices with their tracked states. map: FastHashMap>, /// Temporary storage for collecting transitions. @@ -620,8 +622,16 @@ impl ResourceTracker { } } +//TODO: store `hal::buffer::State` here to avoid extra conversions pub type BufferState = Unit; +impl PendingTransition { + pub fn to_states(&self) -> Range { + conv::map_buffer_state(self.usage.start) .. + conv::map_buffer_state(self.usage.end) + } +} + impl Default for BufferState { fn default() -> Self { BufferState { @@ -745,7 +755,7 @@ impl RangedStates { &mut self.ranges[start_pos .. pos] } - fn coalesce(&mut self) { + fn _coalesce(&mut self) { let mut num_removed = 0; let mut iter = self.ranges.iter_mut(); let mut cur = match iter.next() { @@ -836,18 +846,26 @@ impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { type PlaneStates = RangedStates; -#[derive(Clone, Copy, PartialEq)] +//TODO: store `hal::image::State` here to avoid extra conversions +#[derive(Clone, Copy, Debug, PartialEq)] struct DepthStencilState { depth: Unit, stencil: Unit, } -#[derive(Clone, Default)] -struct TextureStates { +#[derive(Clone, Debug, Default)] +pub struct TextureStates { color_mips: ArrayVec<[PlaneStates>; MAX_MIP_LEVELS]>, depth_stencil: PlaneStates, } +impl PendingTransition { + pub fn to_states(&self) -> Range { + conv::map_texture_state(self.usage.start, self.selector.aspects) .. + conv::map_texture_state(self.usage.end, self.selector.aspects) + } +} + impl ResourceState for TextureStates { type Id = TextureId; type Selector = hal::image::SubresourceRange; @@ -1134,3 +1152,72 @@ impl ResourceState for TextureViewState { Ok(()) } } + +#[derive(Clone, Debug, Default)] +pub struct BindGroupState; + +impl ResourceState for BindGroupState { + type Id = BindGroupId; + type Selector = (); + type Usage = (); + + fn query( + &self, + _selector: Self::Selector, + ) -> Option { + Some(()) + } + + fn change( + &mut self, + _id: Self::Id, + _selector: Self::Selector, + _usage: Self::Usage, + _output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + Ok(()) + } + + fn merge( + &mut self, + _id: Self::Id, + _other: &Self, + _stitch: Stitch, + _output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + Ok(()) + } +} + +pub struct TrackerSet { + pub buffers: ResourceTracker, + pub textures: ResourceTracker, + pub views: ResourceTracker, + pub bind_groups: ResourceTracker, + //TODO: samplers +} + +impl TrackerSet { + pub fn new() -> Self { + TrackerSet { + buffers: ResourceTracker::new(), + textures: ResourceTracker::new(), + views: ResourceTracker::new(), + bind_groups: ResourceTracker::new(), + } + } + + pub fn clear(&mut self) { + self.buffers.clear(); + self.textures.clear(); + self.views.clear(); + self.bind_groups.clear(); + } + + pub fn merge_extend(&mut self, other: &Self) { + self.buffers.merge_extend(&other.buffers).unwrap(); + self.textures.merge_extend(&other.textures).unwrap(); + self.views.merge_extend(&other.views).unwrap(); + self.bind_groups.merge_extend(&other.bind_groups).unwrap(); + } +} From d92b623bd83f204c63af8f2454869b4ef48ef832 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 12 Jun 2019 12:15:22 -0400 Subject: [PATCH 06/25] Remove the old tracking code, update local feature and the headers --- ffi/wgpu-remote.h | 6 - ffi/wgpu.h | 8 +- wgpu-native/src/device.rs | 11 +- wgpu-native/src/track.rs | 339 +------------------------------------- 4 files changed, 11 insertions(+), 353 deletions(-) diff --git a/ffi/wgpu-remote.h b/ffi/wgpu-remote.h index 65336655dc..643aaea25b 100644 --- a/ffi/wgpu-remote.h +++ b/ffi/wgpu-remote.h @@ -19,8 +19,6 @@ typedef struct WGPUClientFactory WGPUClientFactory; typedef struct WGPUServer WGPUServer; -typedef struct WGPUTrackPermit WGPUTrackPermit; - typedef uint32_t WGPUIndex; typedef uint32_t WGPUEpoch; @@ -57,10 +55,6 @@ typedef struct { const uint8_t *error; } WGPUInfrastructure; - - - - WGPUDeviceId wgpu_client_adapter_create_device(const WGPUClient *client, WGPUAdapterId adapter_id, const WGPUDeviceDescriptor *desc); diff --git a/ffi/wgpu.h b/ffi/wgpu.h index 33893c36fb..b78df368f7 100644 --- a/ffi/wgpu.h +++ b/ffi/wgpu.h @@ -11,6 +11,8 @@ #define WGPUMAX_COLOR_TARGETS 4 +#define WGPUMAX_MIP_LEVELS 16 + #define WGPUMAX_VERTEX_BUFFERS 8 typedef enum { @@ -230,8 +232,6 @@ typedef enum { WGPUVertexFormat_Int4 = 48, } WGPUVertexFormat; -typedef struct WGPUTrackPermit WGPUTrackPermit; - typedef uint32_t WGPUIndex; typedef uint32_t WGPUEpoch; @@ -615,10 +615,6 @@ typedef struct { uint32_t array_count; } WGPUTextureViewDescriptor; - - - - #if defined(WGPU_LOCAL) WGPUDeviceId wgpu_adapter_request_device(WGPUAdapterId adapter_id, const WGPUDeviceDescriptor *desc); diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index dbd3f532a2..415bbcd31d 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -764,7 +764,7 @@ pub extern "C" fn wgpu_device_create_texture( ) -> TextureId { let texture = device_create_texture(device_id, desc); let ref_count = texture.life_guard.ref_count.clone(); - let range = texture.full_range; + let range = texture.full_range.clone(); let id = HUB.textures.register_local(texture); device_track_texture(device_id, id, ref_count, range); id @@ -1797,6 +1797,7 @@ pub fn swap_chain_populate_textures( for (i, mut texture) in textures.into_iter().enumerate() { let format = texture.format; let kind = texture.kind; + let range = texture.full_range.clone(); let view_raw = unsafe { device @@ -1806,7 +1807,7 @@ pub fn swap_chain_populate_textures( hal::image::ViewKind::D2, conv::map_texture_format(format), hal::format::Swizzle::NO, - texture.full_range.clone(), + range.clone(), ) .unwrap() }; @@ -1819,9 +1820,10 @@ pub fn swap_chain_populate_textures( ref_count: texture.life_guard.ref_count.clone(), value: HUB.textures.register_local(texture), }; - trackers.textures.query( + trackers.textures.init( texture_id.value, &texture_id.ref_count, + range.clone(), resource::TextureUsage::UNINITIALIZED, ); @@ -1831,6 +1833,7 @@ pub fn swap_chain_populate_textures( format, extent: kind.extent(), samples: kind.num_samples(), + range, is_owned_by_swap_chain: true, life_guard: LifeGuard::new(), }; @@ -1840,7 +1843,7 @@ pub fn swap_chain_populate_textures( }; trackers .views - .query(view_id.value, &view_id.ref_count, DummyUsage); + .init(view_id.value, &view_id.ref_count, (), ()); swap_chain.frames.alloc().init(swap_chain::Frame { texture_id, diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 1a62fe0c4d..92db8199f4 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -14,86 +14,19 @@ use crate::{ }; use arrayvec::ArrayVec; -use bitflags::bitflags; use hal::backend::FastHashMap; use std::{ borrow::Borrow, cmp::Ordering, - collections::hash_map::{Entry, Iter}, + collections::hash_map::Entry, iter::Peekable, - marker::PhantomData, - mem, - ops::{BitOr, Range}, + ops::Range, slice, vec::Drain, }; -#[derive(Clone, Debug, PartialEq)] -#[allow(unused)] -pub enum Tracktion { - Init, - Keep, - Extend { old: T }, - Replace { old: T }, -} - -impl Tracktion { - pub fn into_source(self) -> Option { - match self { - Tracktion::Init | Tracktion::Keep => None, - Tracktion::Extend { old } | Tracktion::Replace { old } => Some(old), - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Query { - pub usage: T, - pub initialized: bool, -} - -bitflags! { - pub struct TrackPermit: u32 { - /// Allow extension of the current usage. This is useful during render pass - /// recording, where the usage has to stay constant, but we can defer the - /// decision on what it is until the end of the pass. - const EXTEND = 1; - /// Allow replacing the current usage with the new one. This is useful when - /// recording a command buffer live, and the current usage is already been set. - const REPLACE = 2; - } -} - -pub trait GenericUsage { - fn is_exclusive(&self) -> bool; -} -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct DummyUsage; -impl BitOr for DummyUsage { - type Output = Self; - fn bitor(self, other: Self) -> Self { - other - } -} - -impl GenericUsage for BufferUsage { - fn is_exclusive(&self) -> bool { - BufferUsage::WRITE_ALL.intersects(*self) - } -} -impl GenericUsage for TextureUsage { - fn is_exclusive(&self) -> bool { - TextureUsage::WRITE_ALL.intersects(*self) - } -} -impl GenericUsage for DummyUsage { - fn is_exclusive(&self) -> bool { - false - } -} - /// A single unit of state tracking. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Unit { @@ -117,32 +50,6 @@ impl Unit { } } -impl + PartialEq + GenericUsage> Unit { - fn transit(&mut self, usage: U, permit: TrackPermit) -> Result, U> { - let old = self.last; - if usage == old { - Ok(Tracktion::Keep) - } else if permit.contains(TrackPermit::EXTEND) && !(old | usage).is_exclusive() { - self.last = old | usage; - Ok(Tracktion::Extend { old }) - } else if permit.contains(TrackPermit::REPLACE) { - self.last = usage; - Ok(Tracktion::Replace { old }) - } else { - Err(old) - } - } -} - -//TODO: consider having `I` as an associated type of `S`? -#[derive(Debug)] -pub struct Tracker { - /// An association of known resource indices with their tracked states. - map: FastHashMap>, - _phantom: PhantomData, -} - -//TODO: make this a generic parameter. /// Mode of stitching to states together. #[derive(Clone, Copy, Debug)] pub enum Stitch { @@ -152,248 +59,6 @@ pub enum Stitch { Last, } -//TODO: consider rewriting this without any iterators that have side effects. -#[derive(Debug)] -pub struct ConsumeIterator<'a, I: TypedId, U: Copy + PartialEq> { - src: Iter<'a, Index, Resource>>, - dst: &'a mut FastHashMap>>, - stitch: Stitch, - _marker: PhantomData, -} - -impl<'a, I: TypedId, U: Copy + PartialEq> Iterator for ConsumeIterator<'a, I, U> { - type Item = (I, Range); - fn next(&mut self) -> Option { - loop { - let (&index, new) = self.src.next()?; - match self.dst.entry(index) { - Entry::Vacant(e) => { - e.insert(new.clone()); - } - Entry::Occupied(mut e) => { - assert_eq!(e.get().epoch, new.epoch); - let old = mem::replace(&mut e.get_mut().state.last, new.state.last); - if old != new.state.init { - let states = old .. new.state.select(self.stitch); - return Some((I::new(index, new.epoch), states)) - } - } - } - } - } -} - -// Make sure to finish all side effects on drop -impl<'a, I: TypedId, U: Copy + PartialEq> Drop for ConsumeIterator<'a, I, U> { - fn drop(&mut self) { - self.for_each(drop) - } -} - -impl Tracker { - pub fn new() -> Self { - Tracker { - map: FastHashMap::default(), - _phantom: PhantomData, - } - } - - /// Remove an id from the tracked map. - pub fn remove(&mut self, id: I) -> bool { - match self.map.remove(&id.index()) { - Some(resource) => { - assert_eq!(resource.epoch, id.epoch()); - true - } - None => false, - } - } - - /// Return an iterator over used resources keys. - pub fn used<'a>(&'a self) -> impl 'a + Iterator { - self.map - .iter() - .map(|(&index, resource)| I::new(index, resource.epoch)) - } - - fn clear(&mut self) { - self.map.clear(); - } -} - -impl + PartialEq> Tracker> { - /// Get the last usage on a resource. - pub(crate) fn query(&mut self, id: I, ref_count: &RefCount, default: U) -> Query { - match self.map.entry(id.index()) { - Entry::Vacant(e) => { - e.insert(Resource { - ref_count: ref_count.clone(), - state: Unit::new(default), - epoch: id.epoch(), - }); - Query { - usage: default, - initialized: true, - } - } - Entry::Occupied(e) => { - assert_eq!(e.get().epoch, id.epoch()); - Query { - usage: e.get().state.last, - initialized: false, - } - } - } - } - - /// Transit a specified resource into a different usage. - pub(crate) fn transit( - &mut self, - id: I, - ref_count: &RefCount, - usage: U, - permit: TrackPermit, - ) -> Result, U> { - match self.map.entry(id.index()) { - Entry::Vacant(e) => { - e.insert(Resource { - ref_count: ref_count.clone(), - state: Unit::new(usage), - epoch: id.epoch(), - }); - Ok(Tracktion::Init) - } - Entry::Occupied(mut e) => { - assert_eq!(e.get().epoch, id.epoch()); - e.get_mut().state.transit(usage, permit) - } - } - } - - /// Consume another tacker, adding it's transitions to `self`. - /// Transitions the current usage to the new one. - pub fn consume_by_replace<'a>( - &'a mut self, - other: &'a Self, - stitch: Stitch, - ) -> ConsumeIterator<'a, I, U> { - ConsumeIterator { - src: other.map.iter(), - dst: &mut self.map, - stitch, - _marker: PhantomData, - } - } - - /// Consume another tacker, adding it's transitions to `self`. - /// Extends the current usage without doing any transitions. - pub fn consume_by_extend<'a>(&'a mut self, other: &'a Self) -> Result<(), (I, Range)> { - for (&index, new) in other.map.iter() { - match self.map.entry(index) { - Entry::Vacant(e) => { - e.insert(new.clone()); - } - Entry::Occupied(mut e) => { - assert_eq!(e.get().epoch, new.epoch); - let old = e.get().state.last; - if old != new.state.last { - let extended = old | new.state.last; - if extended.is_exclusive() { - let id = I::new(index, new.epoch); - return Err((id, old .. new.state.last)); - } - e.get_mut().state.last = extended; - } - } - } - } - Ok(()) - } - - fn _get_with_usage<'a, T: 'a + Borrow>( - &mut self, - storage: &'a Storage, - id: I, - usage: U, - permit: TrackPermit, - ) -> Result<(&'a T, Tracktion), U> { - let item = &storage[id]; - self.transit(id, item.borrow(), usage, permit) - .map(|tracktion| (item, tracktion)) - } - - pub(crate) fn get_with_extended_usage<'a, T: 'a + Borrow>( - &mut self, - storage: &'a Storage, - id: I, - usage: U, - ) -> Result<&'a T, U> { - let item = &storage[id]; - self.transit(id, item.borrow(), usage, TrackPermit::EXTEND) - .map(|_tracktion| item) - } - - pub(crate) fn get_with_replaced_usage<'a, T: 'a + Borrow>( - &mut self, - storage: &'a Storage, - id: I, - usage: U, - ) -> Result<(&'a T, Option), U> { - let item = &storage[id]; - self.transit(id, item.borrow(), usage, TrackPermit::REPLACE) - .map(|tracktion| { - ( - item, - match tracktion { - Tracktion::Init | Tracktion::Keep => None, - Tracktion::Extend { .. } => unreachable!(), - Tracktion::Replace { old } => Some(old), - }, - ) - }) - } -} - -pub type BufferTracker = Tracker>; -pub type TextureTracker = Tracker>; -pub type TextureViewTracker = Tracker>; -pub type BindGroupTracker = Tracker>; - -#[derive(Debug)] -pub struct TrackerSetOld { - pub buffers: BufferTracker, - pub textures: TextureTracker, - pub views: TextureViewTracker, - pub bind_groups: BindGroupTracker, - //TODO: samplers -} - -impl TrackerSetOld { - pub fn new() -> Self { - TrackerSetOld { - buffers: BufferTracker::new(), - textures: TextureTracker::new(), - views: TextureViewTracker::new(), - bind_groups: BindGroupTracker::new(), - } - } - - pub fn clear(&mut self) { - self.buffers.clear(); - self.textures.clear(); - self.views.clear(); - self.bind_groups.clear(); - } - - pub fn consume_by_extend(&mut self, other: &Self) { - self.buffers.consume_by_extend(&other.buffers).unwrap(); - self.textures.consume_by_extend(&other.textures).unwrap(); - self.views.consume_by_extend(&other.views).unwrap(); - self.bind_groups.consume_by_extend(&other.bind_groups).unwrap(); - } -} - - pub trait ResourceState: Clone + Default { type Id: Copy + TypedId; type Selector; From 30955e565cfb60db3c7a45605e17fda478037bf3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 08:18:56 -0400 Subject: [PATCH 07/25] Phantom tracking state --- wgpu-native/src/track.rs | 48 +++++----------------------------------- 1 file changed, 5 insertions(+), 43 deletions(-) diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 92db8199f4..898d846c00 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -21,6 +21,7 @@ use std::{ cmp::Ordering, collections::hash_map::Entry, iter::Peekable, + marker::PhantomData, ops::Range, slice, vec::Drain, @@ -781,12 +782,8 @@ impl ResourceState for TextureStates { } } - -#[derive(Clone, Debug, Default)] -pub struct TextureViewState; - -impl ResourceState for TextureViewState { - type Id = TextureViewId; +impl ResourceState for PhantomData { + type Id = I; type Selector = (); type Usage = (); @@ -818,47 +815,12 @@ impl ResourceState for TextureViewState { } } -#[derive(Clone, Debug, Default)] -pub struct BindGroupState; - -impl ResourceState for BindGroupState { - type Id = BindGroupId; - type Selector = (); - type Usage = (); - - fn query( - &self, - _selector: Self::Selector, - ) -> Option { - Some(()) - } - - fn change( - &mut self, - _id: Self::Id, - _selector: Self::Selector, - _usage: Self::Usage, - _output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - Ok(()) - } - - fn merge( - &mut self, - _id: Self::Id, - _other: &Self, - _stitch: Stitch, - _output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - Ok(()) - } -} pub struct TrackerSet { pub buffers: ResourceTracker, pub textures: ResourceTracker, - pub views: ResourceTracker, - pub bind_groups: ResourceTracker, + pub views: ResourceTracker>, + pub bind_groups: ResourceTracker>, //TODO: samplers } From 0e20549235dc69f65ce26d1a830d34050dcba5b6 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 08:43:19 -0400 Subject: [PATCH 08/25] RangedState unit tests --- wgpu-native/src/track.rs | 127 ++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track.rs index 898d846c00..c6d31f0f28 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track.rs @@ -376,6 +376,36 @@ impl Default for RangedStates { } impl RangedStates { + fn _check_sanity(&self) { + for a in self.ranges.iter() { + assert!(a.0.start < a.0.end); + } + for (a, b) in self.ranges.iter().zip(self.ranges[1..].iter()) { + assert!(a.0.end <= b.0.start); + } + } + + fn _coalesce(&mut self) { + let mut num_removed = 0; + let mut iter = self.ranges.iter_mut(); + let mut cur = match iter.next() { + Some(elem) => elem, + None => return, + }; + while let Some(next) = iter.next() { + if cur.0.end == next.0.start && cur.1 == next.1 { + num_removed += 1; + cur.0.end = next.0.end; + next.0.end = next.0.start; + } else { + cur = next; + } + } + if num_removed != 0 { + self.ranges.retain(|pair| pair.0.start != pair.0.end); + } + } + fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { let start_pos = match self.ranges .iter() @@ -420,27 +450,6 @@ impl RangedStates { &mut self.ranges[start_pos .. pos] } - - fn _coalesce(&mut self) { - let mut num_removed = 0; - let mut iter = self.ranges.iter_mut(); - let mut cur = match iter.next() { - Some(elem) => elem, - None => return, - }; - while let Some(next) = iter.next() { - if cur.0.end == next.0.start && cur.1 == next.1 { - num_removed += 1; - cur.0.end = next.0.end; - next.0.end = next.0.start; - } else { - cur = next; - } - } - if num_removed != 0 { - self.ranges.retain(|pair| pair.0.start != pair.0.end); - } - } } struct Merge<'a, I, T> { @@ -848,3 +857,79 @@ impl TrackerSet { self.bind_groups.merge_extend(&other.bind_groups).unwrap(); } } + +#[cfg(test)] +mod test_range { + use super::RangedStates; + + #[test] + fn test_sane0() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + ]}; + rs._check_sanity(); + } + + #[test(must_fail)] + fn test_sane1() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (5..5, 9), + ]}; + rs._check_sanity(); + } + + #[test(must_fail)] + fn test_sane2() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (3..5, 9), + ]}; + rs._check_sanity(); + } + + #[test] + fn test_coalesce() { + let mut rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + (5..7, 1), + (8..9, 1), + ]}; + rs._coalesce(); + assert_eq!(rs.ranges, vec![ + (1..5, 9), + (5..7, 1), + (8..9, 1), + ]); + } + + #[test] + fn test_isolate() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + (5..7, 1), + (8..9, 1), + ]}; + assert_eq!(rs.clone().isolate(&(4..5), 0), [ + (4..5, 9u8), + ]); + assert_eq!(rs.clone().isolate(&(0..6), 0), [ + (0..1, 0), + (1..4, 9u8), + (4..5, 9), + (5..6, 1), + ]); + assert_eq!(rs.clone().isolate(&(8..10), 1), [ + (8..9, 1), + (9..10, 1), + ]); + assert_eq!(rs.clone().isolate(&(6..8), 0), [ + (6..7, 1), + (7..8, 0), + (8..9, 1), + ]); + } +} From 7b843f220f728c2db0771efc523718462454605f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 10:15:24 -0400 Subject: [PATCH 09/25] Move track logic into a module, fix isolate() first split --- wgpu-native/src/{track.rs => track/mod.rs} | 268 ++------------------ wgpu-native/src/track/range.rs | 278 +++++++++++++++++++++ 2 files changed, 292 insertions(+), 254 deletions(-) rename wgpu-native/src/{track.rs => track/mod.rs} (73%) create mode 100644 wgpu-native/src/track/range.rs diff --git a/wgpu-native/src/track.rs b/wgpu-native/src/track/mod.rs similarity index 73% rename from wgpu-native/src/track.rs rename to wgpu-native/src/track/mod.rs index c6d31f0f28..9acbcca85e 100644 --- a/wgpu-native/src/track.rs +++ b/wgpu-native/src/track/mod.rs @@ -1,3 +1,5 @@ +mod range; + use crate::{ conv, device::MAX_MIP_LEVELS, @@ -18,15 +20,14 @@ use hal::backend::FastHashMap; use std::{ borrow::Borrow, - cmp::Ordering, collections::hash_map::Entry, - iter::Peekable, marker::PhantomData, ops::Range, - slice, vec::Drain, }; +use range::RangedStates; + /// A single unit of state tracking. #[derive(Clone, Copy, Debug, PartialEq)] @@ -362,163 +363,6 @@ impl ResourceState for BufferState { } -#[derive(Clone, Debug)] -pub struct RangedStates { - ranges: Vec<(Range, T)>, -} - -impl Default for RangedStates { - fn default() -> Self { - RangedStates { - ranges: Vec::new(), - } - } -} - -impl RangedStates { - fn _check_sanity(&self) { - for a in self.ranges.iter() { - assert!(a.0.start < a.0.end); - } - for (a, b) in self.ranges.iter().zip(self.ranges[1..].iter()) { - assert!(a.0.end <= b.0.start); - } - } - - fn _coalesce(&mut self) { - let mut num_removed = 0; - let mut iter = self.ranges.iter_mut(); - let mut cur = match iter.next() { - Some(elem) => elem, - None => return, - }; - while let Some(next) = iter.next() { - if cur.0.end == next.0.start && cur.1 == next.1 { - num_removed += 1; - cur.0.end = next.0.end; - next.0.end = next.0.start; - } else { - cur = next; - } - } - if num_removed != 0 { - self.ranges.retain(|pair| pair.0.start != pair.0.end); - } - } - - fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { - let start_pos = match self.ranges - .iter() - .position(|pair| pair.0.end > index.start) - { - Some(pos) => pos, - None => { - let pos = self.ranges.len(); - self.ranges.push((index.clone(), default)); - return &mut self.ranges[pos ..]; - } - }; - - let mut pos = start_pos; - let mut range_pos = index.start; - loop { - let (range, unit) = self.ranges[pos].clone(); - if range.start >= index.end { - self.ranges.insert(pos, (range_pos .. index.end, default)); - pos += 1; - break; - } - if range.start > range_pos { - self.ranges.insert(pos, (range_pos .. range.start, default)); - pos += 1; - range_pos = range.start; - } - if range.end >= index.end { - self.ranges[pos].0.start = index.end; - self.ranges.insert(pos, (range_pos .. index.end, unit)); - pos += 1; - break; - } - pos += 1; - range_pos = range.end; - if pos == self.ranges.len() { - self.ranges.push((range_pos .. index.end, default)); - pos += 1; - break; - } - } - - &mut self.ranges[start_pos .. pos] - } -} - -struct Merge<'a, I, T> { - base: I, - sa: Peekable, T)>>, - sb: Peekable, T)>>, -} - -impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { - type Item = (Range, Range); - fn next(&mut self) -> Option { - match (self.sa.peek(), self.sb.peek()) { - // we have both streams - (Some(&(ref ra, va)), Some(&(ref rb, vb))) => { - let (range, usage) = if ra.start < self.base { // in the middle of the left stream - if self.base == rb.start { // right stream is starting - debug_assert!(self.base < ra.end); - (self.base .. ra.end.min(rb.end), *va .. *vb) - } else { // right hasn't started yet - debug_assert!(self.base < rb.start); - (self.base .. rb.start, *va .. *va) - } - } else if rb.start < self.base { // in the middle of the right stream - if self.base == ra.start { // left stream is starting - debug_assert!(self.base < rb.end); - (self.base .. ra.end.min(rb.end), *va .. *vb) - } else { // left hasn't started yet - debug_assert!(self.base < ra.start); - (self.base .. ra.start, *vb .. *vb) - } - } else { // no active streams - match ra.start.cmp(&rb.start) { - // both are starting - Ordering::Equal => (ra.start .. ra.end.min(rb.end), *va .. *vb), - // only left is starting - Ordering::Less => (ra.start .. rb.start, *va .. *va), - // only right is starting - Ordering::Greater => (rb.start .. ra.start, *vb .. *vb), - } - }; - self.base = range.end; - if ra.end == range.end { - let _ = self.sa.next(); - } - if rb.end == range.end { - let _ = self.sb.next(); - } - Some((range, usage)) - } - // only right stream - (None, Some(&(ref rb, vb))) => { - let range = self.base.max(rb.start) .. rb.end; - self.base = rb.end; - let _ = self.sb.next(); - Some((range, *vb .. *vb)) - } - // only left stream - (Some(&(ref ra, va)), None) => { - let range = self.base.max(ra.start) .. ra.end; - self.base = ra.end; - let _ = self.sa.next(); - Some((range, *va .. *va)) - } - // done - (None, None) => None, - } - } -} - type PlaneStates = RangedStates; //TODO: store `hal::image::State` here to avoid extra conversions @@ -556,7 +400,7 @@ impl ResourceState for TextureStates { let layer_start = num_levels.min(selector.levels.start as usize); let layer_end = num_levels.min(selector.levels.end as usize); for layer in self.color_mips[layer_start .. layer_end].iter() { - for &(ref range, ref unit) in layer.ranges.iter() { + for &(ref range, ref unit) in layer.iter() { if range.end > selector.layers.start && range.start < selector.layers.end { let old = usage.replace(unit.last); if old.is_some() && old != usage { @@ -567,7 +411,7 @@ impl ResourceState for TextureStates { } } if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - for &(ref range, ref ds) in self.depth_stencil.ranges.iter() { + for &(ref range, ref ds) in self.depth_stencil.iter() { if range.end > selector.layers.start && range.start < selector.layers.end { if selector.aspects.contains(hal::format::Aspects::DEPTH) { let old = usage.replace(ds.depth.last); @@ -712,12 +556,8 @@ impl ResourceState for TextureStates { .zip(&other.color_mips) .enumerate() { - temp_color.extend(Merge { - base: 0, - sa: mip_self.ranges.iter().peekable(), - sb: mip_other.ranges.iter().peekable(), - }); - mip_self.ranges.clear(); + temp_color.extend(mip_self.merge(mip_other, 0)); + mip_self.clear(); for (layers, states) in temp_color.drain(..) { let color_usage = states.start.last .. states.end.select(stitch); if let Some(out) = output.as_mut() { @@ -734,20 +574,16 @@ impl ResourceState for TextureStates { }); } } - mip_self.ranges.push((layers, Unit { + mip_self.append(layers, Unit { init: states.start.init, last: color_usage.end, - })); + }); } } let mut temp_ds = Vec::new(); - temp_ds.extend(Merge { - base: 0, - sa: self.depth_stencil.ranges.iter().peekable(), - sb: other.depth_stencil.ranges.iter().peekable(), - }); - self.depth_stencil.ranges.clear(); + temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); + self.depth_stencil.clear(); for (layers, states) in temp_ds.drain(..) { let usage_depth = states.start.depth.last .. states.end.depth.select(stitch); let usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); @@ -775,7 +611,7 @@ impl ResourceState for TextureStates { }); } } - self.depth_stencil.ranges.push((layers, DepthStencilState { + self.depth_stencil.append(layers, DepthStencilState { depth: Unit { init: states.start.depth.init, last: usage_depth.end, @@ -784,7 +620,7 @@ impl ResourceState for TextureStates { init: states.start.stencil.init, last: usage_stencil.end, }, - })); + }); } Ok(()) @@ -857,79 +693,3 @@ impl TrackerSet { self.bind_groups.merge_extend(&other.bind_groups).unwrap(); } } - -#[cfg(test)] -mod test_range { - use super::RangedStates; - - #[test] - fn test_sane0() { - let rs = RangedStates { ranges: vec![ - (1..4, 9u8), - (4..5, 9), - ]}; - rs._check_sanity(); - } - - #[test(must_fail)] - fn test_sane1() { - let rs = RangedStates { ranges: vec![ - (1..4, 9u8), - (5..5, 9), - ]}; - rs._check_sanity(); - } - - #[test(must_fail)] - fn test_sane2() { - let rs = RangedStates { ranges: vec![ - (1..4, 9u8), - (3..5, 9), - ]}; - rs._check_sanity(); - } - - #[test] - fn test_coalesce() { - let mut rs = RangedStates { ranges: vec![ - (1..4, 9u8), - (4..5, 9), - (5..7, 1), - (8..9, 1), - ]}; - rs._coalesce(); - assert_eq!(rs.ranges, vec![ - (1..5, 9), - (5..7, 1), - (8..9, 1), - ]); - } - - #[test] - fn test_isolate() { - let rs = RangedStates { ranges: vec![ - (1..4, 9u8), - (4..5, 9), - (5..7, 1), - (8..9, 1), - ]}; - assert_eq!(rs.clone().isolate(&(4..5), 0), [ - (4..5, 9u8), - ]); - assert_eq!(rs.clone().isolate(&(0..6), 0), [ - (0..1, 0), - (1..4, 9u8), - (4..5, 9), - (5..6, 1), - ]); - assert_eq!(rs.clone().isolate(&(8..10), 1), [ - (8..9, 1), - (9..10, 1), - ]); - assert_eq!(rs.clone().isolate(&(6..8), 0), [ - (6..7, 1), - (7..8, 0), - (8..9, 1), - ]); - } -} diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs new file mode 100644 index 0000000000..6ae346f87e --- /dev/null +++ b/wgpu-native/src/track/range.rs @@ -0,0 +1,278 @@ +use std::{ + cmp::Ordering, + iter::Peekable, + ops::Range, + slice::Iter, +}; + +#[derive(Clone, Debug)] +pub struct RangedStates { + ranges: Vec<(Range, T)>, +} + +impl Default for RangedStates { + fn default() -> Self { + RangedStates { + ranges: Vec::new(), + } + } +} + +impl RangedStates { + pub fn clear(&mut self) { + self.ranges.clear(); + } + + pub fn append(&mut self, index: Range, value: T) { + self.ranges.push((index, value)); + } + + pub fn iter(&self) -> Iter<(Range, T)> { + self.ranges.iter() + } + + #[cfg(test)] + fn check_sanity(&self) { + for a in self.ranges.iter() { + assert!(a.0.start < a.0.end); + } + for (a, b) in self.ranges.iter().zip(self.ranges[1..].iter()) { + assert!(a.0.end <= b.0.start); + } + } + + #[cfg(test)] + fn coalesce(&mut self) { + let mut num_removed = 0; + let mut iter = self.ranges.iter_mut(); + let mut cur = match iter.next() { + Some(elem) => elem, + None => return, + }; + while let Some(next) = iter.next() { + if cur.0.end == next.0.start && cur.1 == next.1 { + num_removed += 1; + cur.0.end = next.0.end; + next.0.end = next.0.start; + } else { + cur = next; + } + } + if num_removed != 0 { + self.ranges.retain(|pair| pair.0.start != pair.0.end); + } + } + + pub fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { + //TODO: implement this in 2 passes: + // 1. scan the ranges to figure out how many extra ones need to be inserted + // 2. go through the ranges by moving them them to the right and inserting the missing ones + + let mut start_pos = match self.ranges + .iter() + .position(|pair| pair.0.end > index.start) + { + Some(pos) => pos, + None => { + let pos = self.ranges.len(); + self.ranges.push((index.clone(), default)); + return &mut self.ranges[pos ..]; + } + }; + + { + let (range, value) = self.ranges[start_pos].clone(); + if range.start < index.start { + self.ranges[start_pos].0.start = index.start; + self.ranges.insert(start_pos, (range.start .. index.start, value)); + start_pos += 1; + } + } + let mut pos = start_pos; + let mut range_pos = index.start; + loop { + let (range, value) = self.ranges[pos].clone(); + if range.start >= index.end { + self.ranges.insert(pos, (range_pos .. index.end, default)); + pos += 1; + break; + } + if range.start > range_pos { + self.ranges.insert(pos, (range_pos .. range.start, default)); + pos += 1; + range_pos = range.start; + } + if range.end >= index.end { + if range.end != index.end { + self.ranges[pos].0.start = index.end; + self.ranges.insert(pos, (range_pos .. index.end, value)); + } + pos += 1; + break; + } + pos += 1; + range_pos = range.end; + if pos == self.ranges.len() { + self.ranges.push((range_pos .. index.end, default)); + pos += 1; + break; + } + } + + &mut self.ranges[start_pos .. pos] + } + + pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> { + Merge { + base, + sa: self.ranges.iter().peekable(), + sb: other.ranges.iter().peekable(), + } + } +} + +pub struct Merge<'a, I, T> { + base: I, + sa: Peekable, T)>>, + sb: Peekable, T)>>, +} + +impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { + type Item = (Range, Range); + fn next(&mut self) -> Option { + match (self.sa.peek(), self.sb.peek()) { + // we have both streams + (Some(&(ref ra, va)), Some(&(ref rb, vb))) => { + let (range, usage) = if ra.start < self.base { // in the middle of the left stream + if self.base == rb.start { // right stream is starting + debug_assert!(self.base < ra.end); + (self.base .. ra.end.min(rb.end), *va .. *vb) + } else { // right hasn't started yet + debug_assert!(self.base < rb.start); + (self.base .. rb.start, *va .. *va) + } + } else if rb.start < self.base { // in the middle of the right stream + if self.base == ra.start { // left stream is starting + debug_assert!(self.base < rb.end); + (self.base .. ra.end.min(rb.end), *va .. *vb) + } else { // left hasn't started yet + debug_assert!(self.base < ra.start); + (self.base .. ra.start, *vb .. *vb) + } + } else { // no active streams + match ra.start.cmp(&rb.start) { + // both are starting + Ordering::Equal => (ra.start .. ra.end.min(rb.end), *va .. *vb), + // only left is starting + Ordering::Less => (ra.start .. rb.start, *va .. *va), + // only right is starting + Ordering::Greater => (rb.start .. ra.start, *vb .. *vb), + } + }; + self.base = range.end; + if ra.end == range.end { + let _ = self.sa.next(); + } + if rb.end == range.end { + let _ = self.sb.next(); + } + Some((range, usage)) + } + // only right stream + (None, Some(&(ref rb, vb))) => { + let range = self.base.max(rb.start) .. rb.end; + self.base = rb.end; + let _ = self.sb.next(); + Some((range, *vb .. *vb)) + } + // only left stream + (Some(&(ref ra, va)), None) => { + let range = self.base.max(ra.start) .. ra.end; + self.base = ra.end; + let _ = self.sa.next(); + Some((range, *va .. *va)) + } + // done + (None, None) => None, + } + } +} + +#[cfg(test)] +mod test { + use super::RangedStates; + + #[test] + fn test_sane0() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + ]}; + rs.check_sanity(); + } + + #[test] + #[should_panic] + fn test_sane1() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (5..5, 9), + ]}; + rs.check_sanity(); + } + + #[test] + #[should_panic] + fn test_sane2() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (3..5, 9), + ]}; + rs.check_sanity(); + } + + #[test] + fn test_coalesce() { + let mut rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + (5..7, 1), + (8..9, 1), + ]}; + rs.coalesce(); + rs.check_sanity(); + assert_eq!(rs.ranges, vec![ + (1..5, 9), + (5..7, 1), + (8..9, 1), + ]); + } + + #[test] + fn test_isolate() { + let rs = RangedStates { ranges: vec![ + (1..4, 9u8), + (4..5, 9), + (5..7, 1), + (8..9, 1), + ]}; + assert_eq!(rs.clone().isolate(&(4..5), 0), [ + (4..5, 9u8), + ]); + assert_eq!(rs.clone().isolate(&(0..6), 0), [ + (0..1, 0), + (1..4, 9u8), + (4..5, 9), + (5..6, 1), + ]); + assert_eq!(rs.clone().isolate(&(8..10), 1), [ + (8..9, 1), + (9..10, 1), + ]); + assert_eq!(rs.clone().isolate(&(6..9), 0), [ + (6..7, 1), + (7..8, 0), + (8..9, 1), + ]); + } +} From 331fe5594876c742376ffd78dbdd5f3410927a3b Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 10:20:21 -0400 Subject: [PATCH 10/25] Check track isolation results for sanity --- wgpu-native/src/track/range.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 6ae346f87e..11e6ed8b0d 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -122,6 +122,14 @@ impl RangedStates { &mut self.ranges[start_pos .. pos] } + #[cfg(test)] + pub fn sanely_isolated(&self, index: Range, default: T) -> Vec<(Range, T)> { + let mut clone = self.clone(); + let result = clone.isolate(&index, default).to_vec(); + clone.check_sanity(); + result + } + pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> { Merge { base, @@ -256,20 +264,20 @@ mod test { (5..7, 1), (8..9, 1), ]}; - assert_eq!(rs.clone().isolate(&(4..5), 0), [ + assert_eq!(&rs.sanely_isolated(4..5, 0), &[ (4..5, 9u8), ]); - assert_eq!(rs.clone().isolate(&(0..6), 0), [ + assert_eq!(&rs.sanely_isolated(0..6, 0), &[ (0..1, 0), (1..4, 9u8), (4..5, 9), (5..6, 1), ]); - assert_eq!(rs.clone().isolate(&(8..10), 1), [ + assert_eq!(&rs.sanely_isolated(8..10, 1), &[ (8..9, 1), (9..10, 1), ]); - assert_eq!(rs.clone().isolate(&(6..9), 0), [ + assert_eq!(&rs.sanely_isolated(6..9, 0), &[ (6..7, 1), (7..8, 0), (8..9, 1), From 07ef7caddc5295e41b6f96b8bae7560eb2415b86 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 11:08:53 -0400 Subject: [PATCH 11/25] Range merging unit tests --- .gitignore | 1 + wgpu-native/src/track/range.rs | 150 +++++++++++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 8200d56c84..f176de3dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target **/*.rs.bk #Cargo.lock +.DS_Store .vscode .vs build diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 11e6ed8b0d..cd4b089094 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -1,5 +1,6 @@ use std::{ cmp::Ordering, + fmt::Debug, iter::Peekable, ops::Range, slice::Iter, @@ -122,6 +123,7 @@ impl RangedStates { &mut self.ranges[start_pos .. pos] } + #[cfg(test)] pub fn sanely_isolated(&self, index: Range, default: T) -> Vec<(Range, T)> { let mut clone = self.clone(); @@ -145,9 +147,10 @@ pub struct Merge<'a, I, T> { sb: Peekable, T)>>, } -impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { +impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { type Item = (Range, Range); fn next(&mut self) -> Option { + println!("Peek {:?} {:?} base = {:?}", self.sa.peek(), self.sb.peek(), self.base); match (self.sa.peek(), self.sb.peek()) { // we have both streams (Some(&(ref ra, va)), Some(&(ref rb, vb))) => { @@ -172,9 +175,9 @@ impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { // both are starting Ordering::Equal => (ra.start .. ra.end.min(rb.end), *va .. *vb), // only left is starting - Ordering::Less => (ra.start .. rb.start, *va .. *va), + Ordering::Less => (ra.start .. rb.start.min(ra.end), *va .. *va), // only right is starting - Ordering::Greater => (rb.start .. ra.start, *vb .. *vb), + Ordering::Greater => (rb.start .. ra.start.min(rb.end), *vb .. *vb), } }; self.base = range.end; @@ -208,10 +211,18 @@ impl<'a, I: Copy + Ord, T: Copy> Iterator for Merge<'a, I, T> { #[cfg(test)] mod test { + //TODO: randomized/fuzzy testing use super::RangedStates; + use std::{ fmt::Debug, ops::Range }; + + fn easy_merge( + ra: Vec<(Range, T)>, rb: Vec<(Range, T)> + ) -> Vec<(Range, Range)> { + RangedStates { ranges: ra }.merge(&RangedStates { ranges: rb }, 0).collect() + } #[test] - fn test_sane0() { + fn sane_good() { let rs = RangedStates { ranges: vec![ (1..4, 9u8), (4..5, 9), @@ -221,7 +232,7 @@ mod test { #[test] #[should_panic] - fn test_sane1() { + fn sane_empty() { let rs = RangedStates { ranges: vec![ (1..4, 9u8), (5..5, 9), @@ -231,7 +242,7 @@ mod test { #[test] #[should_panic] - fn test_sane2() { + fn sane_intersect() { let rs = RangedStates { ranges: vec![ (1..4, 9u8), (3..5, 9), @@ -240,7 +251,7 @@ mod test { } #[test] - fn test_coalesce() { + fn coalesce() { let mut rs = RangedStates { ranges: vec![ (1..4, 9u8), (4..5, 9), @@ -257,7 +268,7 @@ mod test { } #[test] - fn test_isolate() { + fn isolate() { let rs = RangedStates { ranges: vec![ (1..4, 9u8), (4..5, 9), @@ -283,4 +294,127 @@ mod test { (8..9, 1), ]); } + + #[test] + fn merge_same() { + assert_eq!( + easy_merge( + vec![ + (1..4, 0u8), + ], + vec![ + (1..4, 2u8), + ], + ), + vec![ + (1..4, 0..2), + ] + ); + } + + #[test] + fn merge_empty() { + assert_eq!( + easy_merge( + vec![ + (1..2, 0u8), + ], + vec![ + ], + ), + vec![ + (1..2, 0..0), + ] + ); + assert_eq!( + easy_merge( + vec![ + ], + vec![ + (3..4, 1u8), + ], + ), + vec![ + (3..4, 1..1), + ] + ); + } + + #[test] + fn merge_separate() { + assert_eq!( + easy_merge( + vec![ + (1..2, 0u8), + (5..6, 1u8), + ], + vec![ + (2..4, 2u8), + ], + ), + vec![ + (1..2, 0..0), + (2..4, 2..2), + (5..6, 1..1), + ] + ); + } + + #[test] + fn merge_subset() { + assert_eq!( + easy_merge( + vec![ + (1..6, 0u8), + ], + vec![ + (2..4, 2u8), + ], + ), + vec![ + (1..2, 0..0), + (2..4, 0..2), + (4..6, 0..0), + ] + ); + assert_eq!( + easy_merge( + vec![ + (2..4, 0u8), + ], + vec![ + (1..4, 2u8), + ], + ), + vec![ + (1..2, 2..2), + (2..4, 0..2), + ] + ); + } + + #[test] + fn merge_all() { + assert_eq!( + easy_merge( + vec![ + (1..4, 0u8), + (5..8, 1u8), + ], + vec![ + (2..6, 2u8), + (7..9, 3u8), + ], + ), + vec![ + (1..2, 0..0), + (2..4, 0..2), + (4..5, 2..2), + (5..6, 1..2), + (6..7, 1..1), + (7..8, 1..3), + (8..9, 3..3), + ] + ); + } } From 745bebcbb5063200b7905279d43947bb1daa9140 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 11:22:54 -0400 Subject: [PATCH 12/25] Move buffer state tracking into a module, add unit tests --- wgpu-native/src/track/buffer.rs | 99 +++++++++++++++++++++++++++++++++ wgpu-native/src/track/mod.rs | 78 +------------------------- 2 files changed, 102 insertions(+), 75 deletions(-) create mode 100644 wgpu-native/src/track/buffer.rs diff --git a/wgpu-native/src/track/buffer.rs b/wgpu-native/src/track/buffer.rs new file mode 100644 index 0000000000..f22a371e37 --- /dev/null +++ b/wgpu-native/src/track/buffer.rs @@ -0,0 +1,99 @@ +use crate::{ + conv, + resource::BufferUsage, + BufferId, +}; +use super::{PendingTransition, ResourceState, Stitch, Unit}; +use std::ops::Range; + +//TODO: store `hal::buffer::State` here to avoid extra conversions +pub type BufferState = Unit; + +impl PendingTransition { + pub fn to_states(&self) -> Range { + conv::map_buffer_state(self.usage.start) .. + conv::map_buffer_state(self.usage.end) + } +} + +impl Default for BufferState { + fn default() -> Self { + BufferState { + init: BufferUsage::empty(), + last: BufferUsage::empty(), + } + } +} + +impl ResourceState for BufferState { + type Id = BufferId; + type Selector = (); + type Usage = BufferUsage; + + fn query( + &self, + _selector: Self::Selector, + ) -> Option { + Some(self.last) + } + + fn change( + &mut self, + id: Self::Id, + _selector: Self::Selector, + usage: Self::Usage, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + let old = self.last; + if usage != old { + let pending = PendingTransition { + id, + selector: (), + usage: old .. usage, + }; + self.last = match output { + Some(transitions) => { + transitions.push(pending); + usage + } + None => { + if !old.is_empty() && BufferUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + Ok(()) + } + + fn merge( + &mut self, + id: Self::Id, + other: &Self, + stitch: Stitch, + output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + let usage = other.select(stitch); + self.change(id, (), usage, output) + } +} + +#[cfg(test)] +mod test { + use crate::TypedId; + use super::*; + + #[test] + fn change() { + let mut bs = Unit { + init: BufferUsage::INDEX, + last: BufferUsage::STORAGE, + }; + let id = TypedId::new(0, 0); + assert!(bs.change(id, (), BufferUsage::VERTEX, None).is_err()); + bs.change(id, (), BufferUsage::VERTEX, Some(&mut Vec::new())).unwrap(); + bs.change(id, (), BufferUsage::INDEX, None).unwrap(); + assert_eq!(bs.last, BufferUsage::VERTEX | BufferUsage::INDEX); + } +} diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 9acbcca85e..842fef8104 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -1,11 +1,11 @@ +mod buffer; mod range; use crate::{ conv, device::MAX_MIP_LEVELS, hub::Storage, - resource::{BufferUsage, TextureUsage}, - BufferId, + resource::{TextureUsage}, Epoch, Index, RefCount, @@ -26,6 +26,7 @@ use std::{ vec::Drain, }; +use buffer::BufferState; use range::RangedStates; @@ -289,79 +290,6 @@ impl ResourceTracker { } } -//TODO: store `hal::buffer::State` here to avoid extra conversions -pub type BufferState = Unit; - -impl PendingTransition { - pub fn to_states(&self) -> Range { - conv::map_buffer_state(self.usage.start) .. - conv::map_buffer_state(self.usage.end) - } -} - -impl Default for BufferState { - fn default() -> Self { - BufferState { - init: BufferUsage::empty(), - last: BufferUsage::empty(), - } - } -} - -impl ResourceState for BufferState { - type Id = BufferId; - type Selector = (); - type Usage = BufferUsage; - - fn query( - &self, - _selector: Self::Selector, - ) -> Option { - Some(self.last) - } - - fn change( - &mut self, - id: Self::Id, - _selector: Self::Selector, - usage: Self::Usage, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let old = self.last; - if usage != old { - let pending = PendingTransition { - id, - selector: (), - usage: old .. usage, - }; - self.last = match output { - Some(transitions) => { - transitions.push(pending); - usage - } - None => { - if !old.is_empty() && BufferUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } - }; - } - Ok(()) - } - - fn merge( - &mut self, - id: Self::Id, - other: &Self, - stitch: Stitch, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let usage = other.select(stitch); - self.change(id, (), usage, output) - } -} - type PlaneStates = RangedStates; From aca3f1e64df8a36679619df5b6efb57d8d725e79 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 11:26:44 -0400 Subject: [PATCH 13/25] Fix compile warnings when no features are selected --- wgpu-native/src/lib.rs | 2 +- wgpu-native/src/resource.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wgpu-native/src/lib.rs b/wgpu-native/src/lib.rs index 41f03606c6..30b4d1ca3e 100644 --- a/wgpu-native/src/lib.rs +++ b/wgpu-native/src/lib.rs @@ -34,7 +34,7 @@ mod track; pub use self::binding_model::*; pub use self::command::*; pub use self::device::*; -#[cfg(feature = "remote")] +#[cfg(not(feature = "local"))] pub use self::hub::{IdentityManager, Registry, HUB}; pub use self::instance::*; pub use self::pipeline::*; diff --git a/wgpu-native/src/resource.rs b/wgpu-native/src/resource.rs index 285f3476a2..ed3a8c4008 100644 --- a/wgpu-native/src/resource.rs +++ b/wgpu-native/src/resource.rs @@ -181,7 +181,7 @@ pub struct TextureDescriptor { #[derive(Debug)] pub(crate) enum TexturePlacement { - #[cfg_attr(feature = "remote", allow(unused))] + #[cfg_attr(not(feature = "local"), allow(unused))] SwapChain(SwapChainLink>), Memory(MemoryBlock), Void, From 0fb71c9fd86e16fabdd5582464c38c9d551fb709 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 11:30:47 -0400 Subject: [PATCH 14/25] Move texture tracking into a module --- wgpu-native/src/track/mod.rs | 272 +----------------------------- wgpu-native/src/track/texture.rs | 276 +++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 270 deletions(-) create mode 100644 wgpu-native/src/track/texture.rs diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 842fef8104..7481364fc8 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -1,21 +1,17 @@ mod buffer; mod range; +mod texture; use crate::{ - conv, - device::MAX_MIP_LEVELS, hub::Storage, - resource::{TextureUsage}, Epoch, Index, RefCount, - TextureId, TextureViewId, TypedId, BindGroupId, }; -use arrayvec::ArrayVec; use hal::backend::FastHashMap; use std::{ @@ -27,7 +23,7 @@ use std::{ }; use buffer::BufferState; -use range::RangedStates; +use texture::TextureStates; /// A single unit of state tracking. @@ -291,270 +287,6 @@ impl ResourceTracker { } -type PlaneStates = RangedStates; - -//TODO: store `hal::image::State` here to avoid extra conversions -#[derive(Clone, Copy, Debug, PartialEq)] -struct DepthStencilState { - depth: Unit, - stencil: Unit, -} - -#[derive(Clone, Debug, Default)] -pub struct TextureStates { - color_mips: ArrayVec<[PlaneStates>; MAX_MIP_LEVELS]>, - depth_stencil: PlaneStates, -} - -impl PendingTransition { - pub fn to_states(&self) -> Range { - conv::map_texture_state(self.usage.start, self.selector.aspects) .. - conv::map_texture_state(self.usage.end, self.selector.aspects) - } -} - -impl ResourceState for TextureStates { - type Id = TextureId; - type Selector = hal::image::SubresourceRange; - type Usage = TextureUsage; - - fn query( - &self, - selector: Self::Selector, - ) -> Option { - let mut usage = None; - if selector.aspects.contains(hal::format::Aspects::COLOR) { - let num_levels = self.color_mips.len(); - let layer_start = num_levels.min(selector.levels.start as usize); - let layer_end = num_levels.min(selector.levels.end as usize); - for layer in self.color_mips[layer_start .. layer_end].iter() { - for &(ref range, ref unit) in layer.iter() { - if range.end > selector.layers.start && range.start < selector.layers.end { - let old = usage.replace(unit.last); - if old.is_some() && old != usage { - return None - } - } - } - } - } - if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - for &(ref range, ref ds) in self.depth_stencil.iter() { - if range.end > selector.layers.start && range.start < selector.layers.end { - if selector.aspects.contains(hal::format::Aspects::DEPTH) { - let old = usage.replace(ds.depth.last); - if old.is_some() && old != usage { - return None - } - } - if selector.aspects.contains(hal::format::Aspects::STENCIL) { - let old = usage.replace(ds.stencil.last); - if old.is_some() && old != usage { - return None - } - } - } - } - } - usage - } - - fn change( - &mut self, - id: Self::Id, - selector: Self::Selector, - usage: Self::Usage, - mut output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - if selector.aspects.contains(hal::format::Aspects::COLOR) { - while self.color_mips.len() < selector.levels.end as usize { - self.color_mips.push(PlaneStates::default()); - } - for level in selector.levels.clone() { - let layers = self - .color_mips[level as usize] - .isolate(&selector.layers, Unit::new(usage)); - for &mut (ref range, ref mut unit) in layers { - let old = unit.last; - if old == usage { - continue - } - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: old .. usage, - }; - unit.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } - }; - } - } - } - if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - for level in selector.levels.clone() { - let ds_state = DepthStencilState { - depth: Unit::new(usage), - stencil: Unit::new(usage), - }; - for &mut (ref range, ref mut unit) in self.depth_stencil - .isolate(&selector.layers, ds_state) - { - //TODO: check if anything needs to be done when only one of the depth/stencil - // is selected? - if unit.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { - let old = unit.depth.last; - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: old .. usage, - }; - unit.depth.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } - }; - } - if unit.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { - let old = unit.stencil.last; - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: old .. usage, - }; - unit.stencil.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } - }; - } - } - } - } - Ok(()) - } - - fn merge( - &mut self, - id: Self::Id, - other: &Self, - stitch: Stitch, - mut output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let mut temp_color = Vec::new(); - while self.color_mips.len() < other.color_mips.len() { - self.color_mips.push(PlaneStates::default()); - } - for (mip_id, (mip_self, mip_other)) in self.color_mips - .iter_mut() - .zip(&other.color_mips) - .enumerate() - { - temp_color.extend(mip_self.merge(mip_other, 0)); - mip_self.clear(); - for (layers, states) in temp_color.drain(..) { - let color_usage = states.start.last .. states.end.select(stitch); - if let Some(out) = output.as_mut() { - if color_usage.start != color_usage.end { - let level = mip_id as hal::image::Level; - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: layers.clone(), - }, - usage: color_usage.clone(), - }); - } - } - mip_self.append(layers, Unit { - init: states.start.init, - last: color_usage.end, - }); - } - } - - let mut temp_ds = Vec::new(); - temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); - self.depth_stencil.clear(); - for (layers, states) in temp_ds.drain(..) { - let usage_depth = states.start.depth.last .. states.end.depth.select(stitch); - let usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); - if let Some(out) = output.as_mut() { - if usage_depth.start != usage_depth.end { - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_depth.clone(), - }); - } - if usage_stencil.start != usage_stencil.end { - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_stencil.clone(), - }); - } - } - self.depth_stencil.append(layers, DepthStencilState { - depth: Unit { - init: states.start.depth.init, - last: usage_depth.end, - }, - stencil: Unit { - init: states.start.stencil.init, - last: usage_stencil.end, - }, - }); - } - - Ok(()) - } -} - impl ResourceState for PhantomData { type Id = I; type Selector = (); diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs new file mode 100644 index 0000000000..cdb88443ff --- /dev/null +++ b/wgpu-native/src/track/texture.rs @@ -0,0 +1,276 @@ +use crate::{ + conv, + device::MAX_MIP_LEVELS, + resource::TextureUsage, + TextureId, +}; +use super::{range::RangedStates, PendingTransition, ResourceState, Stitch, Unit}; + +use arrayvec::ArrayVec; + +use std::ops::Range; + + +type PlaneStates = RangedStates; + +//TODO: store `hal::image::State` here to avoid extra conversions +#[derive(Clone, Copy, Debug, PartialEq)] +struct DepthStencilState { + depth: Unit, + stencil: Unit, +} + +#[derive(Clone, Debug, Default)] +pub struct TextureStates { + color_mips: ArrayVec<[PlaneStates>; MAX_MIP_LEVELS]>, + depth_stencil: PlaneStates, +} + +impl PendingTransition { + pub fn to_states(&self) -> Range { + conv::map_texture_state(self.usage.start, self.selector.aspects) .. + conv::map_texture_state(self.usage.end, self.selector.aspects) + } +} + +impl ResourceState for TextureStates { + type Id = TextureId; + type Selector = hal::image::SubresourceRange; + type Usage = TextureUsage; + + fn query( + &self, + selector: Self::Selector, + ) -> Option { + let mut usage = None; + if selector.aspects.contains(hal::format::Aspects::COLOR) { + let num_levels = self.color_mips.len(); + let layer_start = num_levels.min(selector.levels.start as usize); + let layer_end = num_levels.min(selector.levels.end as usize); + for layer in self.color_mips[layer_start .. layer_end].iter() { + for &(ref range, ref unit) in layer.iter() { + if range.end > selector.layers.start && range.start < selector.layers.end { + let old = usage.replace(unit.last); + if old.is_some() && old != usage { + return None + } + } + } + } + } + if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { + for &(ref range, ref ds) in self.depth_stencil.iter() { + if range.end > selector.layers.start && range.start < selector.layers.end { + if selector.aspects.contains(hal::format::Aspects::DEPTH) { + let old = usage.replace(ds.depth.last); + if old.is_some() && old != usage { + return None + } + } + if selector.aspects.contains(hal::format::Aspects::STENCIL) { + let old = usage.replace(ds.stencil.last); + if old.is_some() && old != usage { + return None + } + } + } + } + } + usage + } + + fn change( + &mut self, + id: Self::Id, + selector: Self::Selector, + usage: Self::Usage, + mut output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + if selector.aspects.contains(hal::format::Aspects::COLOR) { + while self.color_mips.len() < selector.levels.end as usize { + self.color_mips.push(PlaneStates::default()); + } + for level in selector.levels.clone() { + let layers = self + .color_mips[level as usize] + .isolate(&selector.layers, Unit::new(usage)); + for &mut (ref range, ref mut unit) in layers { + let old = unit.last; + if old == usage { + continue + } + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + } + } + if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { + for level in selector.levels.clone() { + let ds_state = DepthStencilState { + depth: Unit::new(usage), + stencil: Unit::new(usage), + }; + for &mut (ref range, ref mut unit) in self.depth_stencil + .isolate(&selector.layers, ds_state) + { + //TODO: check if anything needs to be done when only one of the depth/stencil + // is selected? + if unit.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { + let old = unit.depth.last; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.depth.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + if unit.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { + let old = unit.stencil.last; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: level .. level + 1, + layers: range.clone(), + }, + usage: old .. usage, + }; + unit.stencil.last = match output.as_mut() { + Some(out) => { + out.push(pending); + usage + } + None => { + if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { + return Err(pending); + } + old | usage + } + }; + } + } + } + } + Ok(()) + } + + fn merge( + &mut self, + id: Self::Id, + other: &Self, + stitch: Stitch, + mut output: Option<&mut Vec>>, + ) -> Result<(), PendingTransition> { + let mut temp_color = Vec::new(); + while self.color_mips.len() < other.color_mips.len() { + self.color_mips.push(PlaneStates::default()); + } + for (mip_id, (mip_self, mip_other)) in self.color_mips + .iter_mut() + .zip(&other.color_mips) + .enumerate() + { + temp_color.extend(mip_self.merge(mip_other, 0)); + mip_self.clear(); + for (layers, states) in temp_color.drain(..) { + let color_usage = states.start.last .. states.end.select(stitch); + if let Some(out) = output.as_mut() { + if color_usage.start != color_usage.end { + let level = mip_id as hal::image::Level; + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: layers.clone(), + }, + usage: color_usage.clone(), + }); + } + } + mip_self.append(layers, Unit { + init: states.start.init, + last: color_usage.end, + }); + } + } + + let mut temp_ds = Vec::new(); + temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); + self.depth_stencil.clear(); + for (layers, states) in temp_ds.drain(..) { + let usage_depth = states.start.depth.last .. states.end.depth.select(stitch); + let usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); + if let Some(out) = output.as_mut() { + if usage_depth.start != usage_depth.end { + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_depth.clone(), + }); + } + if usage_stencil.start != usage_stencil.end { + out.push(PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_stencil.clone(), + }); + } + } + self.depth_stencil.append(layers, DepthStencilState { + depth: Unit { + init: states.start.depth.init, + last: usage_depth.end, + }, + stencil: Unit { + init: states.start.stencil.init, + last: usage_stencil.end, + }, + }); + } + + Ok(()) + } +} From e0f315801ad7fd68f777fe5c71986ba5423b3ad3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 11:47:42 -0400 Subject: [PATCH 15/25] Texture query() unit tests --- wgpu-native/src/track/range.rs | 7 ++++ wgpu-native/src/track/texture.rs | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index cd4b089094..63fadd6420 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -20,6 +20,13 @@ impl Default for RangedStates { } impl RangedStates { + #[cfg(test)] + pub fn new(values: &[(Range, T)]) -> Self { + RangedStates { + ranges: values.to_vec(), + } + } + pub fn clear(&mut self) { self.ranges.clear(); } diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index cdb88443ff..bf55e9b0cd 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -274,3 +274,71 @@ impl ResourceState for TextureStates { Ok(()) } } + + +#[cfg(test)] +mod test { + //TODO: change() and merge() tests + //use crate::TypedId; + use super::*; + use hal::{ + format::Aspects, + image::SubresourceRange, + }; + + #[test] + fn query() { + let mut ts = TextureStates::default(); + ts.color_mips.push(PlaneStates::default()); + ts.color_mips.push(PlaneStates::new(&[ + (1..3, Unit::new(TextureUsage::SAMPLED)), + (3..5, Unit::new(TextureUsage::SAMPLED)), + (5..6, Unit::new(TextureUsage::STORAGE)), + ])); + assert_eq!( + ts.query(SubresourceRange { + aspects: Aspects::COLOR, + levels: 1..2, + layers: 2..5, + }), + // level 1 matches + Some(TextureUsage::SAMPLED), + ); + assert_eq!( + ts.query(SubresourceRange { + aspects: Aspects::DEPTH, + levels: 1..2, + layers: 2..5, + }), + // no depth found + None, + ); + assert_eq!( + ts.query(SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..2, + layers: 2..5, + }), + // level 0 is empty, level 1 matches + Some(TextureUsage::SAMPLED), + ); + assert_eq!( + ts.query(SubresourceRange { + aspects: Aspects::COLOR, + levels: 1..2, + layers: 1..5, + }), + // level 1 matches with gaps + Some(TextureUsage::SAMPLED), + ); + assert_eq!( + ts.query(SubresourceRange { + aspects: Aspects::COLOR, + levels: 1..2, + layers: 4..6, + }), + // level 1 doesn't match + None, + ); + } +} From 86521abfc20a5ca84004970da2e1bab153421d79 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 12:31:36 -0400 Subject: [PATCH 16/25] Document the tracking and adjust the API a bit to return less errors --- wgpu-native/src/command/mod.rs | 30 ++++----- wgpu-native/src/command/transfer.rs | 89 ++++++++++---------------- wgpu-native/src/device.rs | 6 +- wgpu-native/src/swap_chain.rs | 1 - wgpu-native/src/track/mod.rs | 96 +++++++++++++++++++++++++---- wgpu-native/src/track/range.rs | 24 ++++++++ wgpu-native/src/track/texture.rs | 1 + 7 files changed, 155 insertions(+), 92 deletions(-) diff --git a/wgpu-native/src/command/mod.rs b/wgpu-native/src/command/mod.rs index 2fdeb72577..f7027dcf7f 100644 --- a/wgpu-native/src/command/mod.rs +++ b/wgpu-native/src/command/mod.rs @@ -110,7 +110,6 @@ impl CommandBufferHandle { let buffer_barriers = base.buffers .merge_replace(&head.buffers, stitch) - .unwrap() .map(|pending| { trace!("transit buffer {:?}", pending); hal::memory::Barrier::Buffer { @@ -123,7 +122,6 @@ impl CommandBufferHandle { let texture_barriers = base .textures .merge_replace(&head.textures, stitch) - .unwrap() .map(|pending| { trace!("transit texture {:?}", pending); hal::memory::Barrier::Image { @@ -214,14 +212,12 @@ pub fn command_encoder_begin_render_pass( None => { // Required sub-resources have inconsistent states, we need to // issue individual barriers instead of relying on the render pass. - let (texture, pending) = trackers.textures - .use_replace( - &*texture_guard, - view.texture_id.value, - view.range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ) - .unwrap(); + let (texture, pending) = trackers.textures.use_replace( + &*texture_guard, + view.texture_id.value, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); barriers.extend(pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &texture.raw, @@ -272,14 +268,12 @@ pub fn command_encoder_begin_render_pass( None => { // Required sub-resources have inconsistent states, we need to // issue individual barriers instead of relying on the render pass. - let (texture, pending) = trackers.textures - .use_replace( - &*texture_guard, - view.texture_id.value, - view.range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ) - .unwrap(); + let (texture, pending) = trackers.textures.use_replace( + &*texture_guard, + view.texture_id.value, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); barriers.extend(pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &texture.raw, diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index c08659a40e..7fa93c292a 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -79,8 +79,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( let (src_buffer, src_pending) = cmb .trackers .buffers - .use_replace(&*buffer_guard, src, (), BufferUsage::TRANSFER_SRC) - .unwrap(); + .use_replace(&*buffer_guard, src, (), BufferUsage::TRANSFER_SRC); barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Buffer { states: pending.to_states(), target: &src_buffer.raw, @@ -91,8 +90,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_buffer( let (dst_buffer, dst_pending) = cmb .trackers .buffers - .use_replace(&*buffer_guard, dst, (), BufferUsage::TRANSFER_DST) - .unwrap(); + .use_replace(&*buffer_guard, dst, (), BufferUsage::TRANSFER_DST); barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Buffer { states: pending.to_states(), target: &dst_buffer.raw, @@ -131,8 +129,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( let (src_buffer, src_pending) = cmb .trackers .buffers - .use_replace(&*buffer_guard, source.buffer, (), BufferUsage::TRANSFER_SRC) - .unwrap(); + .use_replace(&*buffer_guard, source.buffer, (), BufferUsage::TRANSFER_SRC); let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { states: pending.to_states(), target: &src_buffer.raw, @@ -140,16 +137,12 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( range: None .. None, }); - let (dst_texture, dst_pending) = cmb - .trackers - .textures - .use_replace( - &*texture_guard, - destination.texture, - destination.to_selector(), - TextureUsage::TRANSFER_DST, - ) - .unwrap(); + let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + destination.texture, + destination.to_selector(), + TextureUsage::TRANSFER_DST, + ); let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &dst_texture.raw, @@ -208,16 +201,12 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); - let (src_texture, src_pending) = cmb - .trackers - .textures - .use_replace( - &*texture_guard, - source.texture, - source.to_selector(), - TextureUsage::TRANSFER_SRC, - ) - .unwrap(); + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + source.texture, + source.to_selector(), + TextureUsage::TRANSFER_SRC, + ); let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &src_texture.raw, @@ -230,16 +219,12 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( TexturePlacement::Memory(_) => (), } - let (dst_buffer, dst_barriers) = cmb - .trackers - .buffers - .use_replace( - &*buffer_guard, - destination.buffer, - (), - BufferUsage::TRANSFER_DST, - ) - .unwrap(); + let (dst_buffer, dst_barriers) = cmb.trackers.buffers.use_replace( + &*buffer_guard, + destination.buffer, + (), + BufferUsage::TRANSFER_DST, + ); let dst_barrier = dst_barriers.map(|pending| hal::memory::Barrier::Buffer { states: pending.to_states(), target: &dst_buffer.raw, @@ -292,16 +277,12 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( // borrow the buffer tracker mutably... let mut barriers = Vec::new(); - let (src_texture, src_pending) = cmb - .trackers - .textures - .use_replace( - &*texture_guard, - source.texture, - source.to_selector(), - TextureUsage::TRANSFER_SRC, - ) - .unwrap(); + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + source.texture, + source.to_selector(), + TextureUsage::TRANSFER_SRC, + ); barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &src_texture.raw, @@ -309,16 +290,12 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( range: pending.selector, })); - let (dst_texture, dst_pending) = cmb - .trackers - .textures - .use_replace( - &*texture_guard, - destination.texture, - destination.to_selector(), - TextureUsage::TRANSFER_DST, - ) - .unwrap(); + let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + destination.texture, + destination.to_selector(), + TextureUsage::TRANSFER_DST, + ); barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { states: pending.to_states(), target: &dst_texture.raw, diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 415bbcd31d..b5f1dee0dd 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -1918,8 +1918,7 @@ pub extern "C" fn wgpu_buffer_map_read_async( .trackers .lock() .buffers - .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_READ) - .unwrap(); + .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_READ); device.pending.lock().map(buffer_id, ref_count); } @@ -1954,8 +1953,7 @@ pub extern "C" fn wgpu_buffer_map_write_async( .trackers .lock() .buffers - .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_WRITE) - .unwrap(); + .change_replace(buffer_id, &ref_count, (), resource::BufferUsage::MAP_WRITE); device.pending.lock().map(buffer_id, ref_count); } diff --git a/wgpu-native/src/swap_chain.rs b/wgpu-native/src/swap_chain.rs index f49f81277b..931eee4cd7 100644 --- a/wgpu-native/src/swap_chain.rs +++ b/wgpu-native/src/swap_chain.rs @@ -238,7 +238,6 @@ pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: SwapChainId) { texture.full_range.clone(), resource::TextureUsage::UNINITIALIZED, ) - .unwrap() .map(|pending| hal::memory::Barrier::Image { states: conv::map_texture_state(pending.usage.start, hal::format::Aspects::COLOR) .. ( diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 7481364fc8..37aa58fc14 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -26,7 +26,8 @@ use buffer::BufferState; use texture::TextureStates; -/// A single unit of state tracking. +/// A single unit of state tracking. It keeps an initial +/// usage as well as the last/current one, similar to `Range`. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Unit { init: U, @@ -34,6 +35,7 @@ pub struct Unit { } impl Unit { + /// Create a new unit from a given usage. fn new(usage: U) -> Self { Unit { init: usage, @@ -41,6 +43,12 @@ impl Unit { } } + /// 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, @@ -58,16 +66,38 @@ pub enum Stitch { 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 + Default { + /// Corresponding `HUB` identifier. type Id: Copy + TypedId; + /// A type specifying the sub-resources. type Selector; + /// Usage type for a `Unit` of a sub-resource. type Usage; + /// Check if all the selected sub-resources have the same + /// usage, and return it. + /// + /// Returns `None` if no sub-resources + /// are intersecting with the selector, or their usage + /// isn't consistent. fn query( &self, selector: Self::Selector, ) -> Option; + /// Change the last usage of the selected sub-resources. + /// + /// If `output` is specified, it's filled with the + /// `PendingTransition` objects cooresponding to smaller + /// sub-resource transitions. The old usage is replaced by + /// the new one. + /// + /// If `output` is `None`, the old usage is extended with + /// the new usage. The error is returned if it's not possible, + /// specifying the conflicting transition. Extension can only + /// be done for read-only usages. fn change( &mut self, id: Self::Id, @@ -76,6 +106,14 @@ pub trait ResourceState: Clone + Default { output: Option<&mut Vec>>, ) -> Result<(), PendingTransition>; + /// Merge the state of this resource tracked by a different instance + /// with the current one. + /// + /// Same rules for `output` apply as with `change()`: last usage state + /// is either replaced (when `output` is provided) with a + /// `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). fn merge( &mut self, id: Self::Id, @@ -85,6 +123,8 @@ pub trait ResourceState: Clone + Default { ) -> Result<(), PendingTransition>; } +/// Structure wrapping the abstract tracking state with the relevant resource +/// data, such as the reference count and the epoch. #[derive(Clone, Debug)] struct Resource { ref_count: RefCount, @@ -92,6 +132,9 @@ struct Resource { epoch: Epoch, } +/// 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)] pub struct PendingTransition { pub id: S::Id, @@ -99,6 +142,7 @@ pub struct PendingTransition { pub usage: Range, } +/// A tracker for all resources of a given tyope. pub struct ResourceTracker { /// An association of known resource indices with their tracked states. map: FastHashMap>, @@ -107,6 +151,7 @@ pub struct ResourceTracker { } impl ResourceTracker { + /// Create a new empty tracker. pub fn new() -> Self { ResourceTracker { map: FastHashMap::default(), @@ -132,11 +177,14 @@ impl ResourceTracker { .map(|(&index, resource)| S::Id::new(index, resource.epoch)) } + /// Clear the tracked contents. fn clear(&mut self) { self.map.clear(); } /// Initialize a resource to be used. + /// + /// Returns `false` if the resource is already tracked. pub fn init( &mut self, id: S::Id, @@ -160,8 +208,10 @@ impl ResourceTracker { .is_none() } - /// Query a resource selector. Returns `Some(Usage)` only if - /// this usage is consistent across the given selector. + /// Query the usage of a resource selector. + /// + /// Returns `Some(Usage)` only if this usage is consistent + /// across the given selector. pub fn query( &mut self, id: S::Id, @@ -172,6 +222,8 @@ impl ResourceTracker { res.state.query(selector) } + /// Make sure that a resource is tracked, and return a mutable + /// reference to it. fn grab<'a>( map: &'a mut FastHashMap>, id: S::Id, @@ -193,6 +245,8 @@ impl ResourceTracker { } /// Extend the usage of a specified resource. + /// + /// Returns conflicting transition as an error. pub fn change_extend( &mut self, id: S::Id, @@ -211,10 +265,11 @@ impl ResourceTracker { ref_count: &RefCount, selector: S::Selector, usage: S::Usage, - ) -> Result>, PendingTransition> { + ) -> Drain> { let res = Self::grab(&mut self.map, id, ref_count); - res.state.change(id, selector, usage, Some(&mut self.temp))?; - Ok(self.temp.drain(..)) + res.state.change(id, selector, usage, Some(&mut self.temp)) + .ok(); //TODO: unwrap? + self.temp.drain(..) } /// Merge another tacker into `self` by extending the current states @@ -243,7 +298,7 @@ impl ResourceTracker { &'a mut self, other: &'a Self, stitch: Stitch, - ) -> Result>, PendingTransition> { + ) -> Drain> { for (&index, new) in other.map.iter() { match self.map.entry(index) { Entry::Vacant(e) => { @@ -252,13 +307,20 @@ impl ResourceTracker { Entry::Occupied(e) => { assert_eq!(e.get().epoch, new.epoch); let id = S::Id::new(index, new.epoch); - e.into_mut().state.merge(id, &new.state, stitch, Some(&mut self.temp))?; + e.into_mut().state + .merge(id, &new.state, stitch, Some(&mut self.temp)) + .ok(); //TODO: unwrap? } } } - Ok(self.temp.drain(..)) + self.temp.drain(..) } + /// Use a given resource provided by an `Id` with the specified usage. + /// Combines storage access by 'Id' with the transition that extends + /// the last read-only usage, if possible. + /// + /// Returns the old usage as an error if there is a conflict. pub fn use_extend<'a, T: 'a + Borrow>( &mut self, storage: &'a Storage, @@ -272,17 +334,20 @@ impl ResourceTracker { .map_err(|pending| pending.usage.start) } + /// Use a given resource provided by an `Id` with the specified usage. + /// Combines storage access by 'Id' with the transition that replaces + /// the last usage with a new one, returning an iterator over these + /// transitions. pub fn use_replace<'a, T: 'a + Borrow>( &mut self, storage: &'a Storage, id: S::Id, selector: S::Selector, usage: S::Usage, - ) -> Result<(&'a T, Drain>), S::Usage> { + ) -> (&'a T, Drain>) { let item = &storage[id]; - self.change_replace(id, item.borrow(), selector, usage) - .map(|drain| (item, drain)) - .map_err(|pending| pending.usage.start) + let drain = self.change_replace(id, item.borrow(), selector, usage); + (item, drain) } } @@ -321,6 +386,7 @@ impl ResourceState for PhantomData { } +/// A set of trackers for all relevant resources. pub struct TrackerSet { pub buffers: ResourceTracker, pub textures: ResourceTracker, @@ -330,6 +396,7 @@ pub struct TrackerSet { } impl TrackerSet { + /// Create an emtpy set. pub fn new() -> Self { TrackerSet { buffers: ResourceTracker::new(), @@ -339,6 +406,7 @@ impl TrackerSet { } } + /// Clear all the trackers. pub fn clear(&mut self) { self.buffers.clear(); self.textures.clear(); @@ -346,6 +414,8 @@ impl TrackerSet { self.bind_groups.clear(); } + /// Merge all the trackers of another instance by extending + /// the usage. Panics on a conflict. pub fn merge_extend(&mut self, other: &Self) { self.buffers.merge_extend(&other.buffers).unwrap(); self.textures.merge_extend(&other.textures).unwrap(); diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 63fadd6420..6a985f1e81 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -6,6 +6,9 @@ use std::{ 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)] pub struct RangedStates { ranges: Vec<(Range, T)>, @@ -20,6 +23,7 @@ impl Default for RangedStates { } impl RangedStates { + /// Construct a new instance from a slice of ranges. #[cfg(test)] pub fn new(values: &[(Range, T)]) -> Self { RangedStates { @@ -27,18 +31,26 @@ impl RangedStates { } } + /// Clear all the ranges. pub fn clear(&mut self) { self.ranges.clear(); } + /// Append a range. pub fn append(&mut self, index: Range, value: T) { + if let Some(last) = self.ranges.last() { + debug_assert!(last.0.end <= index.start); + } self.ranges.push((index, value)); } + /// Iterate through the stored ranges immutably. pub fn iter(&self) -> Iter<(Range, T)> { self.ranges.iter() } + /// Check that all the ranges are non-intersecting and ordered. + /// Panics otherwise. #[cfg(test)] fn check_sanity(&self) { for a in self.ranges.iter() { @@ -49,6 +61,7 @@ impl RangedStates { } } + /// Merge the neighboring ranges together, where possible. #[cfg(test)] fn coalesce(&mut self) { let mut num_removed = 0; @@ -71,6 +84,10 @@ impl RangedStates { } } + /// Split the storage ranges in such a way that there is a linear subset of + /// them occuping exactly `index` range, which is returned mutably. + /// + /// Gaps in the ranges are filled with `default` value. pub fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { //TODO: implement this in 2 passes: // 1. scan the ranges to figure out how many extra ones need to be inserted @@ -131,6 +148,7 @@ impl RangedStates { } + /// Helper method for isolation that checks the sanity of the results. #[cfg(test)] pub fn sanely_isolated(&self, index: Range, default: T) -> Vec<(Range, T)> { let mut clone = self.clone(); @@ -139,6 +157,10 @@ impl RangedStates { result } + /// Produce an iterator that merges two instances together. + /// + /// Each range in the returned iterator is a subset of a range in either + /// `self` or `other`, and the value returned as a `Range` from `self` to `other`. pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> { Merge { base, @@ -148,6 +170,8 @@ impl RangedStates { } } + +/// A custom iterator that goes through two `RangedStates` and proces a merge. pub struct Merge<'a, I, T> { base: I, sa: Peekable, T)>>, diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index bf55e9b0cd..9d8a830486 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -195,6 +195,7 @@ impl ResourceState for TextureStates { stitch: Stitch, mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { + //TODO: extend the logic to error out properly let mut temp_color = Vec::new(); while self.color_mips.len() < other.color_mips.len() { self.color_mips.push(PlaneStates::default()); From 965750b237dbb1c19c7fec969481c6c39a325fd9 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 12:49:03 -0400 Subject: [PATCH 17/25] Improve texture state merging with proper error handling --- wgpu-native/src/track/buffer.rs | 1 + wgpu-native/src/track/range.rs | 1 - wgpu-native/src/track/texture.rs | 150 ++++++++++++++----------------- 3 files changed, 68 insertions(+), 84 deletions(-) diff --git a/wgpu-native/src/track/buffer.rs b/wgpu-native/src/track/buffer.rs index f22a371e37..9d4eac3daf 100644 --- a/wgpu-native/src/track/buffer.rs +++ b/wgpu-native/src/track/buffer.rs @@ -10,6 +10,7 @@ use std::ops::Range; pub type BufferState = Unit; impl PendingTransition { + /// Produce the gfx-hal buffer states corresponding to the transition. pub fn to_states(&self) -> Range { conv::map_buffer_state(self.usage.start) .. conv::map_buffer_state(self.usage.end) diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 6a985f1e81..ced7e4247a 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -181,7 +181,6 @@ pub struct Merge<'a, I, T> { impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { type Item = (Range, Range); fn next(&mut self) -> Option { - println!("Peek {:?} {:?} base = {:?}", self.sa.peek(), self.sb.peek(), self.base); match (self.sa.peek(), self.sb.peek()) { // we have both streams (Some(&(ref ra, va)), Some(&(ref rb, vb))) => { diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index 9d8a830486..c35c2549e1 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -27,10 +27,32 @@ pub struct TextureStates { } impl PendingTransition { + /// Produce the gfx-hal image states corresponding to the transition. pub fn to_states(&self) -> Range { conv::map_texture_state(self.usage.start, self.selector.aspects) .. conv::map_texture_state(self.usage.end, self.selector.aspects) } + + /// Check for the validity of `self` with regards to the presense 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. + fn record(self, output: Option<&mut &mut Vec>) -> Result { + let u = self.usage.clone(); + match output { + Some(out) => { + out.push(self); + Ok(u.end) + } + None => { + if !u.start.is_empty() && TextureUsage::WRITE_ALL.intersects(u.start | u.end) { + Err(self) + } else { + Ok(u.start | u.end) + } + } + } + } } impl ResourceState for TextureStates { @@ -95,8 +117,7 @@ impl ResourceState for TextureStates { .color_mips[level as usize] .isolate(&selector.layers, Unit::new(usage)); for &mut (ref range, ref mut unit) in layers { - let old = unit.last; - if old == usage { + if unit.last == usage { continue } let pending = PendingTransition { @@ -106,20 +127,9 @@ impl ResourceState for TextureStates { levels: level .. level + 1, layers: range.clone(), }, - usage: old .. usage, - }; - unit.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } + usage: unit.last .. usage, }; + unit.last = pending.record(output.as_mut())?; } } } @@ -135,7 +145,6 @@ impl ResourceState for TextureStates { //TODO: check if anything needs to be done when only one of the depth/stencil // is selected? if unit.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { - let old = unit.depth.last; let pending = PendingTransition { id, selector: hal::image::SubresourceRange { @@ -143,23 +152,11 @@ impl ResourceState for TextureStates { levels: level .. level + 1, layers: range.clone(), }, - usage: old .. usage, - }; - unit.depth.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } + usage: unit.depth.last .. usage, }; + unit.depth.last = pending.record(output.as_mut())?; } if unit.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { - let old = unit.stencil.last; let pending = PendingTransition { id, selector: hal::image::SubresourceRange { @@ -167,20 +164,9 @@ impl ResourceState for TextureStates { levels: level .. level + 1, layers: range.clone(), }, - usage: old .. usage, - }; - unit.stencil.last = match output.as_mut() { - Some(out) => { - out.push(pending); - usage - } - None => { - if !old.is_empty() && TextureUsage::WRITE_ALL.intersects(old | usage) { - return Err(pending); - } - old | usage - } + usage: unit.stencil.last .. usage, }; + unit.stencil.last = pending.record(output.as_mut())?; } } } @@ -195,7 +181,6 @@ impl ResourceState for TextureStates { stitch: Stitch, mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { - //TODO: extend the logic to error out properly let mut temp_color = Vec::new(); while self.color_mips.len() < other.color_mips.len() { self.color_mips.push(PlaneStates::default()); @@ -208,20 +193,19 @@ impl ResourceState for TextureStates { temp_color.extend(mip_self.merge(mip_other, 0)); mip_self.clear(); for (layers, states) in temp_color.drain(..) { - let color_usage = states.start.last .. states.end.select(stitch); - if let Some(out) = output.as_mut() { - if color_usage.start != color_usage.end { - let level = mip_id as hal::image::Level; - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: layers.clone(), - }, - usage: color_usage.clone(), - }); - } + let mut color_usage = states.start.last .. states.end.select(stitch); + if color_usage.start != color_usage.end { + let level = mip_id as hal::image::Level; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: layers.clone(), + }, + usage: color_usage.clone(), + }; + color_usage.end = pending.record(output.as_mut())?; } mip_self.append(layers, Unit { init: states.start.init, @@ -234,31 +218,31 @@ impl ResourceState for TextureStates { temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); self.depth_stencil.clear(); for (layers, states) in temp_ds.drain(..) { - let usage_depth = states.start.depth.last .. states.end.depth.select(stitch); - let usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); - if let Some(out) = output.as_mut() { - if usage_depth.start != usage_depth.end { - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_depth.clone(), - }); - } - if usage_stencil.start != usage_stencil.end { - out.push(PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_stencil.clone(), - }); - } + let mut usage_depth = states.start.depth.last .. states.end.depth.select(stitch); + let mut usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); + if usage_depth.start != usage_depth.end { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_depth.clone(), + }; + usage_depth.end = pending.record(output.as_mut())?; + } + if usage_stencil.start != usage_stencil.end { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: usage_stencil.clone(), + }; + usage_stencil.end = pending.record(output.as_mut())?; } self.depth_stencil.append(layers, DepthStencilState { depth: Unit { From ef1c3738872f5094352acee8dfb8399bb6223b4c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 15:23:36 -0400 Subject: [PATCH 18/25] Fix texture transfer aspects --- wgpu-native/src/command/transfer.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index 7fa93c292a..21c2603d0d 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -44,16 +44,19 @@ impl TextureCopyView { let level = self.mip_level as hal::image::Level; let layer = self.array_layer as hal::image::Layer; hal::image::SubresourceRange { - aspects: hal::format::Aspects::all(), //TODO + //TODO: detect the aspects in transfer ops + aspects: hal::format::Aspects::COLOR, levels: level .. level + 1, layers: layer .. layer + 1, } } - fn to_sub_layers(&self) -> hal::image::SubresourceLayers { + fn to_sub_layers( + &self, aspects: hal::format::Aspects + ) -> hal::image::SubresourceLayers { let layer = self.array_layer as hal::image::Layer; hal::image::SubresourceLayers { - aspects: hal::format::Aspects::all(), //TODO + aspects, level: self.mip_level as hal::image::Level, layers: layer .. layer + 1, } @@ -158,6 +161,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( }); } + let aspects = dst_texture.full_range.aspects; let bytes_per_texel = conv::map_texture_format(dst_texture.format) .surface_desc() .bits as u32 @@ -168,7 +172,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( buffer_offset: source.offset, buffer_width, buffer_height: source.image_height, - image_layers: destination.to_sub_layers(), + image_layers: destination.to_sub_layers(aspects), image_offset: conv::map_origin(destination.origin), image_extent: conv::map_extent(copy_size), }; @@ -232,6 +236,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( range: None .. None, }); + let aspects = src_texture.full_range.aspects; let bytes_per_texel = conv::map_texture_format(src_texture.format) .surface_desc() .bits as u32 @@ -242,7 +247,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( buffer_offset: destination.offset, buffer_width, buffer_height: destination.image_height, - image_layers: source.to_sub_layers(), + image_layers: source.to_sub_layers(aspects), image_offset: conv::map_origin(source.origin), image_extent: conv::map_extent(copy_size), }; @@ -311,10 +316,11 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( }); } + let aspects = src_texture.full_range.aspects & dst_texture.full_range.aspects; let region = hal::command::ImageCopy { - src_subresource: source.to_sub_layers(), + src_subresource: source.to_sub_layers(aspects), src_offset: conv::map_origin(source.origin), - dst_subresource: destination.to_sub_layers(), + dst_subresource: destination.to_sub_layers(aspects), dst_offset: conv::map_origin(destination.origin), extent: conv::map_extent(copy_size), }; From 7c647a7d3c721dae8e67c7fed822e7eccd5eb5b5 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 13 Jun 2019 23:11:09 -0400 Subject: [PATCH 19/25] Improve texture state merging --- wgpu-native/src/track/range.rs | 58 ++++++------ wgpu-native/src/track/texture.rs | 148 ++++++++++++++++++------------- 2 files changed, 115 insertions(+), 91 deletions(-) diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index ced7e4247a..6ae7d1d058 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -179,7 +179,7 @@ pub struct Merge<'a, I, T> { } impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { - type Item = (Range, Range); + type Item = (Range, Range>); fn next(&mut self) -> Option { match (self.sa.peek(), self.sb.peek()) { // we have both streams @@ -187,27 +187,27 @@ impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { let (range, usage) = if ra.start < self.base { // in the middle of the left stream if self.base == rb.start { // right stream is starting debug_assert!(self.base < ra.end); - (self.base .. ra.end.min(rb.end), *va .. *vb) + (self.base .. ra.end.min(rb.end), Some(*va) .. Some(*vb)) } else { // right hasn't started yet debug_assert!(self.base < rb.start); - (self.base .. rb.start, *va .. *va) + (self.base .. rb.start, Some(*va) .. None) } } else if rb.start < self.base { // in the middle of the right stream if self.base == ra.start { // left stream is starting debug_assert!(self.base < rb.end); - (self.base .. ra.end.min(rb.end), *va .. *vb) + (self.base .. ra.end.min(rb.end), Some(*va) .. Some(*vb)) } else { // left hasn't started yet debug_assert!(self.base < ra.start); - (self.base .. ra.start, *vb .. *vb) + (self.base .. ra.start, None .. Some(*vb)) } } else { // no active streams match ra.start.cmp(&rb.start) { // both are starting - Ordering::Equal => (ra.start .. ra.end.min(rb.end), *va .. *vb), + Ordering::Equal => (ra.start .. ra.end.min(rb.end), Some(*va) .. Some(*vb)), // only left is starting - Ordering::Less => (ra.start .. rb.start.min(ra.end), *va .. *va), + Ordering::Less => (ra.start .. rb.start.min(ra.end), Some(*va) .. None), // only right is starting - Ordering::Greater => (rb.start .. ra.start.min(rb.end), *vb .. *vb), + Ordering::Greater => (rb.start .. ra.start.min(rb.end), None .. Some(*vb)), } }; self.base = range.end; @@ -224,14 +224,14 @@ impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { let range = self.base.max(rb.start) .. rb.end; self.base = rb.end; let _ = self.sb.next(); - Some((range, *vb .. *vb)) + Some((range, None .. Some(*vb))) } // only left stream (Some(&(ref ra, va)), None) => { let range = self.base.max(ra.start) .. ra.end; self.base = ra.end; let _ = self.sa.next(); - Some((range, *va .. *va)) + Some((range, Some(*va) .. None)) } // done (None, None) => None, @@ -247,7 +247,7 @@ mod test { fn easy_merge( ra: Vec<(Range, T)>, rb: Vec<(Range, T)> - ) -> Vec<(Range, Range)> { + ) -> Vec<(Range, Range>)> { RangedStates { ranges: ra }.merge(&RangedStates { ranges: rb }, 0).collect() } @@ -337,7 +337,7 @@ mod test { ], ), vec![ - (1..4, 0..2), + (1..4, Some(0)..Some(2)), ] ); } @@ -353,7 +353,7 @@ mod test { ], ), vec![ - (1..2, 0..0), + (1..2, Some(0)..None), ] ); assert_eq!( @@ -365,7 +365,7 @@ mod test { ], ), vec![ - (3..4, 1..1), + (3..4, None..Some(1)), ] ); } @@ -383,9 +383,9 @@ mod test { ], ), vec![ - (1..2, 0..0), - (2..4, 2..2), - (5..6, 1..1), + (1..2, Some(0)..None), + (2..4, None..Some(2)), + (5..6, Some(1)..None), ] ); } @@ -402,9 +402,9 @@ mod test { ], ), vec![ - (1..2, 0..0), - (2..4, 0..2), - (4..6, 0..0), + (1..2, Some(0)..None), + (2..4, Some(0)..Some(2)), + (4..6, Some(0)..None), ] ); assert_eq!( @@ -417,8 +417,8 @@ mod test { ], ), vec![ - (1..2, 2..2), - (2..4, 0..2), + (1..2, None..Some(2)), + (2..4, Some(0)..Some(2)), ] ); } @@ -437,13 +437,13 @@ mod test { ], ), vec![ - (1..2, 0..0), - (2..4, 0..2), - (4..5, 2..2), - (5..6, 1..2), - (6..7, 1..1), - (7..8, 1..3), - (8..9, 3..3), + (1..2, Some(0)..None), + (2..4, Some(0)..Some(2)), + (4..5, None..Some(2)), + (5..6, Some(1)..Some(2)), + (6..7, Some(1)..None), + (7..8, Some(1)..Some(3)), + (8..9, None..Some(3)), ] ); } diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index c35c2549e1..97d8a15eab 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -135,16 +135,21 @@ impl ResourceState for TextureStates { } if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { for level in selector.levels.clone() { + //TODO: improve this, it's currently not tested enough and not sound let ds_state = DepthStencilState { - depth: Unit::new(usage), - stencil: Unit::new(usage), + depth: Unit::new( + if selector.aspects.contains(hal::format::Aspects::DEPTH) { usage } else { TextureUsage::empty() } + ), + stencil: Unit::new( + if selector.aspects.contains(hal::format::Aspects::STENCIL) { usage } else { TextureUsage::empty() } + ), }; - for &mut (ref range, ref mut unit) in self.depth_stencil + for &mut (ref range, ref mut ds) in self.depth_stencil .isolate(&selector.layers, ds_state) { //TODO: check if anything needs to be done when only one of the depth/stencil // is selected? - if unit.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { + if ds.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { let pending = PendingTransition { id, selector: hal::image::SubresourceRange { @@ -152,11 +157,11 @@ impl ResourceState for TextureStates { levels: level .. level + 1, layers: range.clone(), }, - usage: unit.depth.last .. usage, + usage: ds.depth.last .. usage, }; - unit.depth.last = pending.record(output.as_mut())?; + ds.depth.last = pending.record(output.as_mut())?; } - if unit.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { + if ds.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { let pending = PendingTransition { id, selector: hal::image::SubresourceRange { @@ -164,9 +169,9 @@ impl ResourceState for TextureStates { levels: level .. level + 1, layers: range.clone(), }, - usage: unit.stencil.last .. usage, + usage: ds.stencil.last .. usage, }; - unit.stencil.last = pending.record(output.as_mut())?; + ds.stencil.last = pending.record(output.as_mut())?; } } } @@ -193,24 +198,32 @@ impl ResourceState for TextureStates { temp_color.extend(mip_self.merge(mip_other, 0)); mip_self.clear(); for (layers, states) in temp_color.drain(..) { - let mut color_usage = states.start.last .. states.end.select(stitch); - if color_usage.start != color_usage.end { - let level = mip_id as hal::image::Level; - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: layers.clone(), - }, - usage: color_usage.clone(), - }; - color_usage.end = pending.record(output.as_mut())?; - } - mip_self.append(layers, Unit { - init: states.start.init, - last: color_usage.end, - }); + let unit = match states { + Range { start: None, end: None } => unreachable!(), + Range { start: Some(start), end: None } => start, + Range { start: None, end: Some(end) } => Unit::new(end.select(stitch)), + Range { start: Some(start), end: Some(end) } => { + let mut final_usage = end.select(stitch); + if start.last != final_usage { + let level = mip_id as hal::image::Level; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: level .. level + 1, + layers: layers.clone(), + }, + usage: start.last .. final_usage, + }; + final_usage = pending.record(output.as_mut())?; + } + Unit { + init: start.init, + last: final_usage, + } + } + }; + mip_self.append(layers, unit); } } @@ -218,42 +231,53 @@ impl ResourceState for TextureStates { temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); self.depth_stencil.clear(); for (layers, states) in temp_ds.drain(..) { - let mut usage_depth = states.start.depth.last .. states.end.depth.select(stitch); - let mut usage_stencil = states.start.stencil.last .. states.end.stencil.select(stitch); - if usage_depth.start != usage_depth.end { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_depth.clone(), - }; - usage_depth.end = pending.record(output.as_mut())?; - } - if usage_stencil.start != usage_stencil.end { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: usage_stencil.clone(), - }; - usage_stencil.end = pending.record(output.as_mut())?; - } - self.depth_stencil.append(layers, DepthStencilState { - depth: Unit { - init: states.start.depth.init, - last: usage_depth.end, + let ds = match states { + Range { start: None, end: None } => unreachable!(), + Range { start: Some(start), end: None } => start, + Range { start: None, end: Some(end) } => DepthStencilState { + depth: Unit::new(end.depth.select(stitch)), + stencil: Unit::new(end.stencil.select(stitch)), }, - stencil: Unit { - init: states.start.stencil.init, - last: usage_stencil.end, - }, - }); + Range { start: Some(start), end: Some(end) } => { + let mut final_depth = end.depth.select(stitch); + let mut final_stencil = end.stencil.select(stitch); + if start.depth.last != final_depth { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::DEPTH, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: start.depth.last .. final_depth, + }; + final_depth = pending.record(output.as_mut())?; + } + if start.stencil.last != final_stencil { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::STENCIL, + levels: 0 .. 1, + layers: layers.clone(), + }, + usage: start.stencil.last .. final_stencil, + }; + final_stencil = pending.record(output.as_mut())?; + } + DepthStencilState { + depth: Unit { + init: start.depth.init, + last: final_depth, + }, + stencil: Unit { + init: start.stencil.init, + last: final_stencil, + }, + } + } + }; + self.depth_stencil.append(layers, ds); } Ok(()) From 3da6c9f65b10e96e070176d65830bb10f0a4aafd Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 08:52:15 -0400 Subject: [PATCH 20/25] Fix texture view extent, improve docs according to the review --- wgpu-native/src/device.rs | 2 +- wgpu-native/src/track/mod.rs | 6 +++--- wgpu-native/src/track/range.rs | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index b5f1dee0dd..225c56caa0 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -800,7 +800,7 @@ pub fn texture_create_view( ref_count: texture.life_guard.ref_count.clone(), }, format: texture.format, - extent: texture.kind.extent(), + extent: texture.kind.extent().at_level(range.levels.start), samples: texture.kind.num_samples(), range, is_owned_by_swap_chain: false, diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 37aa58fc14..4e01ad4a63 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -90,7 +90,7 @@ pub trait ResourceState: Clone + Default { /// Change the last usage of the selected sub-resources. /// /// If `output` is specified, it's filled with the - /// `PendingTransition` objects cooresponding to smaller + /// `PendingTransition` objects corresponding to smaller /// sub-resource transitions. The old usage is replaced by /// the new one. /// @@ -142,7 +142,7 @@ pub struct PendingTransition { pub usage: Range, } -/// A tracker for all resources of a given tyope. +/// A tracker for all resources of a given type. pub struct ResourceTracker { /// An association of known resource indices with their tracked states. map: FastHashMap>, @@ -396,7 +396,7 @@ pub struct TrackerSet { } impl TrackerSet { - /// Create an emtpy set. + /// Create an empty set. pub fn new() -> Self { TrackerSet { buffers: ResourceTracker::new(), diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 6ae7d1d058..60be87145a 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -11,6 +11,8 @@ use std::{ /// are often grouped together linearly. #[derive(Clone, Debug)] pub struct RangedStates { + /// List of ranges, each associated with a singe value. + /// Ranges of keys have to be non-intersecting and ordered. ranges: Vec<(Range, T)>, } @@ -37,6 +39,9 @@ impl RangedStates { } /// Append a range. + /// + /// Assumes that the object is being constructed from a set of + /// ranges, and they are given in the ascending order of their keys. pub fn append(&mut self, index: Range, value: T) { if let Some(last) = self.ranges.last() { debug_assert!(last.0.end <= index.start); @@ -171,7 +176,7 @@ impl RangedStates { } -/// A custom iterator that goes through two `RangedStates` and proces a merge. +/// A custom iterator that goes through two `RangedStates` and process a merge. pub struct Merge<'a, I, T> { base: I, sa: Peekable, T)>>, From 3ed1215d2f7e8a53909fb005ce7da31f5c53842d Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 11:44:51 -0400 Subject: [PATCH 21/25] Rewrite texture tracker to support multiple depth/stencil mips --- wgpu-native/src/track/mod.rs | 4 +- wgpu-native/src/track/range.rs | 35 +++- wgpu-native/src/track/texture.rs | 276 +++++++++++-------------------- 3 files changed, 127 insertions(+), 188 deletions(-) diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 4e01ad4a63..b8281c45ab 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -23,7 +23,7 @@ use std::{ }; use buffer::BufferState; -use texture::TextureStates; +use texture::TextureState; /// A single unit of state tracking. It keeps an initial @@ -389,7 +389,7 @@ impl ResourceState for PhantomData { /// A set of trackers for all relevant resources. pub struct TrackerSet { pub buffers: ResourceTracker, - pub textures: ResourceTracker, + pub textures: ResourceTracker, pub views: ResourceTracker>, pub bind_groups: ResourceTracker>, //TODO: samplers diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 60be87145a..a503700937 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -49,11 +49,6 @@ impl RangedStates { self.ranges.push((index, value)); } - /// Iterate through the stored ranges immutably. - pub fn iter(&self) -> Iter<(Range, T)> { - self.ranges.iter() - } - /// Check that all the ranges are non-intersecting and ordered. /// Panics otherwise. #[cfg(test)] @@ -89,6 +84,25 @@ impl RangedStates { } } + /// Check if all intersecting ranges have the same value, which is returned. + /// + /// Returns `None` if no intersections are detected. + /// Returns `Some(Err)` if the intersected values are inconsistent. + pub fn query( + &self, index: &Range, fun: impl Fn(&T) -> U + ) -> Option> { + let mut result = None; + for &(ref range, ref value) in self.ranges.iter() { + if range.end > index.start && range.start < index.end { + let old = result.replace(fun(value)); + if old.is_some() && old != result { + return Some(Err(())) + } + } + } + result.map(Ok) + } + /// Split the storage ranges in such a way that there is a linear subset of /// them occuping exactly `index` range, which is returned mutably. /// @@ -302,6 +316,17 @@ mod test { ]); } + #[test] + fn query() { + let rs = RangedStates { ranges: vec![ + (1..4, 1u8), + (5..7, 2), + ]}; + assert_eq!(rs.query(&(0..1), |v| *v), None); + assert_eq!(rs.query(&(1..3), |v| *v), Some(Ok(1))); + assert_eq!(rs.query(&(1..6), |v| *v), Some(Err(()))); + } + #[test] fn isolate() { let rs = RangedStates { ranges: vec![ diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index 97d8a15eab..07b4322b24 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -11,22 +11,23 @@ use arrayvec::ArrayVec; use std::ops::Range; -type PlaneStates = RangedStates; +type PlaneStates = RangedStates>; + //TODO: store `hal::image::State` here to avoid extra conversions -#[derive(Clone, Copy, Debug, PartialEq)] -struct DepthStencilState { - depth: Unit, - stencil: Unit, +#[derive(Clone, Debug, Default)] +struct MipState { + color: PlaneStates, + depth: PlaneStates, + stencil: PlaneStates, } #[derive(Clone, Debug, Default)] -pub struct TextureStates { - color_mips: ArrayVec<[PlaneStates>; MAX_MIP_LEVELS]>, - depth_stencil: PlaneStates, +pub struct TextureState { + mips: ArrayVec<[MipState; MAX_MIP_LEVELS]>, } -impl PendingTransition { +impl PendingTransition { /// Produce the gfx-hal image states corresponding to the transition. pub fn to_states(&self) -> Range { conv::map_texture_state(self.usage.start, self.selector.aspects) .. @@ -55,7 +56,7 @@ impl PendingTransition { } } -impl ResourceState for TextureStates { +impl ResourceState for TextureState { type Id = TextureId; type Selector = hal::image::SubresourceRange; type Usage = TextureUsage; @@ -64,41 +65,31 @@ impl ResourceState for TextureStates { &self, selector: Self::Selector, ) -> Option { - let mut usage = None; - if selector.aspects.contains(hal::format::Aspects::COLOR) { - let num_levels = self.color_mips.len(); - let layer_start = num_levels.min(selector.levels.start as usize); - let layer_end = num_levels.min(selector.levels.end as usize); - for layer in self.color_mips[layer_start .. layer_end].iter() { - for &(ref range, ref unit) in layer.iter() { - if range.end > selector.layers.start && range.start < selector.layers.end { - let old = usage.replace(unit.last); - if old.is_some() && old != usage { - return None - } - } + let mut result = None; + let num_levels = self.mips.len(); + let mip_start = num_levels.min(selector.levels.start as usize); + let mip_end = num_levels.min(selector.levels.end as usize); + for mip in self.mips[mip_start .. mip_end].iter() { + for &(aspect, plane_states) in &[ + (hal::format::Aspects::COLOR, &mip.color), + (hal::format::Aspects::DEPTH, &mip.depth), + (hal::format::Aspects::STENCIL, &mip.stencil), + ] { + if !selector.aspects.contains(aspect) { + continue } - } - } - if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - for &(ref range, ref ds) in self.depth_stencil.iter() { - if range.end > selector.layers.start && range.start < selector.layers.end { - if selector.aspects.contains(hal::format::Aspects::DEPTH) { - let old = usage.replace(ds.depth.last); - if old.is_some() && old != usage { - return None - } - } - if selector.aspects.contains(hal::format::Aspects::STENCIL) { - let old = usage.replace(ds.stencil.last); - if old.is_some() && old != usage { - return None - } + match plane_states.query(&selector.layers, |unit| unit.last) { + None => {} + Some(Ok(usage)) if result == Some(usage) => {} + Some(Ok(usage)) if result.is_none() => { + result = Some(usage); } + Some(Ok(_)) | + Some(Err(())) => return None, } } } - usage + result } fn change( @@ -108,14 +99,24 @@ impl ResourceState for TextureStates { usage: Self::Usage, mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { - if selector.aspects.contains(hal::format::Aspects::COLOR) { - while self.color_mips.len() < selector.levels.end as usize { - self.color_mips.push(PlaneStates::default()); - } - for level in selector.levels.clone() { - let layers = self - .color_mips[level as usize] - .isolate(&selector.layers, Unit::new(usage)); + while self.mips.len() < selector.levels.end as usize { + self.mips.push(MipState::default()); + } + 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 hal::image::Level; + for &mut (aspect, ref mut plane_states) in &mut [ + (hal::format::Aspects::COLOR, &mut mip.color), + (hal::format::Aspects::DEPTH, &mut mip.depth), + (hal::format::Aspects::STENCIL, &mut mip.stencil), + ] { + if !selector.aspects.contains(aspect) { + continue + } + let layers = plane_states.isolate(&selector.layers, Unit::new(usage)); for &mut (ref range, ref mut unit) in layers { if unit.last == usage { continue @@ -133,49 +134,6 @@ impl ResourceState for TextureStates { } } } - if selector.aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - for level in selector.levels.clone() { - //TODO: improve this, it's currently not tested enough and not sound - let ds_state = DepthStencilState { - depth: Unit::new( - if selector.aspects.contains(hal::format::Aspects::DEPTH) { usage } else { TextureUsage::empty() } - ), - stencil: Unit::new( - if selector.aspects.contains(hal::format::Aspects::STENCIL) { usage } else { TextureUsage::empty() } - ), - }; - for &mut (ref range, ref mut ds) in self.depth_stencil - .isolate(&selector.layers, ds_state) - { - //TODO: check if anything needs to be done when only one of the depth/stencil - // is selected? - if ds.depth.last != usage && selector.aspects.contains(hal::format::Aspects::DEPTH) { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: ds.depth.last .. usage, - }; - ds.depth.last = pending.record(output.as_mut())?; - } - if ds.stencil.last != usage && selector.aspects.contains(hal::format::Aspects::STENCIL) { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: ds.stencil.last .. usage, - }; - ds.stencil.last = pending.record(output.as_mut())?; - } - } - } - } Ok(()) } @@ -186,98 +144,53 @@ impl ResourceState for TextureStates { stitch: Stitch, mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { - let mut temp_color = Vec::new(); - while self.color_mips.len() < other.color_mips.len() { - self.color_mips.push(PlaneStates::default()); + let mut temp = Vec::new(); + while self.mips.len() < other.mips.len() as usize { + self.mips.push(MipState::default()); } - for (mip_id, (mip_self, mip_other)) in self.color_mips + + for (mip_id, (mip_self, mip_other)) in self.mips .iter_mut() - .zip(&other.color_mips) + .zip(&other.mips) .enumerate() { - temp_color.extend(mip_self.merge(mip_other, 0)); - mip_self.clear(); - for (layers, states) in temp_color.drain(..) { - let unit = match states { - Range { start: None, end: None } => unreachable!(), - Range { start: Some(start), end: None } => start, - Range { start: None, end: Some(end) } => Unit::new(end.select(stitch)), - Range { start: Some(start), end: Some(end) } => { - let mut final_usage = end.select(stitch); - if start.last != final_usage { - let level = mip_id as hal::image::Level; - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: level .. level + 1, - layers: layers.clone(), - }, - usage: start.last .. final_usage, - }; - final_usage = pending.record(output.as_mut())?; - } - Unit { - init: start.init, - last: final_usage, - } - } - }; - mip_self.append(layers, unit); - } - } + let level = mip_id as hal::image::Level; + for &mut (aspects, ref mut planes_self, planes_other) in &mut [ + (hal::format::Aspects::COLOR, &mut mip_self.color, &mip_other.color), + (hal::format::Aspects::DEPTH, &mut mip_self.depth, &mip_other.depth), + (hal::format::Aspects::STENCIL, &mut mip_self.stencil, &mip_other.stencil), + ] { + temp.extend(planes_self.merge(planes_other, 0)); + planes_self.clear(); - let mut temp_ds = Vec::new(); - temp_ds.extend(self.depth_stencil.merge(&other.depth_stencil, 0)); - self.depth_stencil.clear(); - for (layers, states) in temp_ds.drain(..) { - let ds = match states { - Range { start: None, end: None } => unreachable!(), - Range { start: Some(start), end: None } => start, - Range { start: None, end: Some(end) } => DepthStencilState { - depth: Unit::new(end.depth.select(stitch)), - stencil: Unit::new(end.stencil.select(stitch)), - }, - Range { start: Some(start), end: Some(end) } => { - let mut final_depth = end.depth.select(stitch); - let mut final_stencil = end.stencil.select(stitch); - if start.depth.last != final_depth { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::DEPTH, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: start.depth.last .. final_depth, - }; - final_depth = pending.record(output.as_mut())?; - } - if start.stencil.last != final_stencil { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects: hal::format::Aspects::STENCIL, - levels: 0 .. 1, - layers: layers.clone(), - }, - usage: start.stencil.last .. final_stencil, - }; - final_stencil = pending.record(output.as_mut())?; - } - DepthStencilState { - depth: Unit { - init: start.depth.init, - last: final_depth, - }, - stencil: Unit { - init: start.stencil.init, - last: final_stencil, - }, - } + for (layers, states) in temp.drain(..) { + let unit = match states { + Range { start: None, end: None } => unreachable!(), + Range { start: Some(start), end: None } => start, + Range { start: None, end: Some(end) } => Unit::new(end.select(stitch)), + Range { start: Some(start), end: Some(end) } => { + let mut final_usage = end.select(stitch); + if start.last != final_usage { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects, + levels: level .. level + 1, + layers: layers.clone(), + }, + usage: start.last .. final_usage, + }; + final_usage = pending.record(output.as_mut())?; + } + Unit { + init: start.init, + last: final_usage, + } + } + }; + planes_self.append(layers, unit); } - }; - self.depth_stencil.append(layers, ds); + } } Ok(()) @@ -297,13 +210,14 @@ mod test { #[test] fn query() { - let mut ts = TextureStates::default(); - ts.color_mips.push(PlaneStates::default()); - ts.color_mips.push(PlaneStates::new(&[ + let mut ts = TextureState::default(); + ts.mips.push(MipState::default()); + ts.mips.push(MipState::default()); + ts.mips[1].color = PlaneStates::new(&[ (1..3, Unit::new(TextureUsage::SAMPLED)), (3..5, Unit::new(TextureUsage::SAMPLED)), (5..6, Unit::new(TextureUsage::STORAGE)), - ])); + ]); assert_eq!( ts.query(SubresourceRange { aspects: Aspects::COLOR, From c22d8f86cc7a58290359007d2452e735d5febe6d Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 11:47:42 -0400 Subject: [PATCH 22/25] Support depth-stencil texture transfers --- wgpu-native/src/command/transfer.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index 21c2603d0d..95e71b6328 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -40,12 +40,13 @@ pub struct TextureCopyView { } impl TextureCopyView { - fn to_selector(&self) -> hal::image::SubresourceRange { + //TODO: we currently access each texture twice for a transer, + // once only to get the aspect flags, which is unfortunate. + fn to_selector(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceRange { let level = self.mip_level as hal::image::Level; let layer = self.array_layer as hal::image::Layer; hal::image::SubresourceRange { - //TODO: detect the aspects in transfer ops - aspects: hal::format::Aspects::COLOR, + aspects, levels: level .. level + 1, layers: layer .. layer + 1, } @@ -128,6 +129,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( let cmb = &mut cmb_guard[command_buffer_id]; let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); + let aspects = texture_guard[destination.texture].full_range.aspects; let (src_buffer, src_pending) = cmb .trackers @@ -143,7 +145,7 @@ pub extern "C" fn wgpu_command_buffer_copy_buffer_to_texture( let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( &*texture_guard, destination.texture, - destination.to_selector(), + destination.to_selector(aspects), TextureUsage::TRANSFER_DST, ); let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { @@ -204,11 +206,12 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_buffer( let cmb = &mut cmb_guard[command_buffer_id]; let buffer_guard = HUB.buffers.read(); let texture_guard = HUB.textures.read(); + let aspects = texture_guard[source.texture].full_range.aspects; let (src_texture, src_pending) = cmb.trackers.textures.use_replace( &*texture_guard, source.texture, - source.to_selector(), + source.to_selector(aspects), TextureUsage::TRANSFER_SRC, ); let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { @@ -281,11 +284,13 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( // we can't hold both src_pending and dst_pending in scope because they // borrow the buffer tracker mutably... let mut barriers = Vec::new(); + let aspects = texture_guard[source.texture].full_range.aspects & + texture_guard[destination.texture].full_range.aspects; let (src_texture, src_pending) = cmb.trackers.textures.use_replace( &*texture_guard, source.texture, - source.to_selector(), + source.to_selector(aspects), TextureUsage::TRANSFER_SRC, ); barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { @@ -298,7 +303,7 @@ pub extern "C" fn wgpu_command_buffer_copy_texture_to_texture( let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( &*texture_guard, destination.texture, - destination.to_selector(), + destination.to_selector(aspects), TextureUsage::TRANSFER_DST, ); barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { From 917154a49dc43cda5fca047cde731a89a5417944 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 12:10:23 -0400 Subject: [PATCH 23/25] Debug tracking derives and regular optimization --- .travis.yml | 1 + wgpu-native/src/command/render.rs | 1 + wgpu-native/src/device.rs | 3 +++ wgpu-native/src/track/buffer.rs | 3 +++ wgpu-native/src/track/mod.rs | 35 ++++++++++++++++++++++++++----- wgpu-native/src/track/range.rs | 3 +-- wgpu-native/src/track/texture.rs | 8 +++++++ 7 files changed, 47 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f53d30f70..33399f8494 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,6 +57,7 @@ before_install: script: - cargo test + - if [[ $TRAVIS_OS_NAME == "linux" ]]; then cargo check --release; fi - if [[ $TRAVIS_RUST_VERSION == "nightly" ]]; then cargo +nightly install cbindgen; fi - if [[ $TRAVIS_RUST_VERSION == "nightly" ]] && [[ $TRAVIS_OS_NAME == "windows" ]]; then wget -nc -O glfw.zip https://github.com/glfw/glfw/archive/3.3.zip && diff --git a/wgpu-native/src/command/render.rs b/wgpu-native/src/command/render.rs index cfa15b0735..820c5cb532 100644 --- a/wgpu-native/src/command/render.rs +++ b/wgpu-native/src/command/render.rs @@ -176,6 +176,7 @@ pub extern "C" fn wgpu_render_pass_end_pass(pass_id: RenderPassId) -> CommandBuf unsafe { pass.raw.end_render_pass(); } + pass.trackers.optimize(); let cmb = &mut cmb_guard[pass.cmb_id.value]; match cmb.raw.last_mut() { diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 225c56caa0..1edb8fff5c 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -1283,6 +1283,9 @@ pub extern "C" fn wgpu_queue_submit( } } + // optimize the tracked states + comb.trackers.optimize(); + // update submission IDs for id in comb.trackers.buffers.used() { let buffer = &buffer_guard[id]; diff --git a/wgpu-native/src/track/buffer.rs b/wgpu-native/src/track/buffer.rs index 9d4eac3daf..ef0b94a97c 100644 --- a/wgpu-native/src/track/buffer.rs +++ b/wgpu-native/src/track/buffer.rs @@ -78,6 +78,9 @@ impl ResourceState for BufferState { let usage = other.select(stitch); self.change(id, (), usage, output) } + + fn optimize(&mut self) { + } } #[cfg(test)] diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index b8281c45ab..851e16882a 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -17,6 +17,7 @@ use hal::backend::FastHashMap; use std::{ borrow::Borrow, collections::hash_map::Entry, + fmt::Debug, marker::PhantomData, ops::Range, vec::Drain, @@ -70,11 +71,11 @@ pub enum Stitch { /// a particular resource type, like a buffer or a texture. pub trait ResourceState: Clone + Default { /// Corresponding `HUB` identifier. - type Id: Copy + TypedId; + type Id: Copy + Debug + TypedId; /// A type specifying the sub-resources. - type Selector; + type Selector: Debug; /// Usage type for a `Unit` of a sub-resource. - type Usage; + type Usage: Debug; /// Check if all the selected sub-resources have the same /// usage, and return it. @@ -121,11 +122,15 @@ pub trait ResourceState: Clone + Default { stitch: Stitch, output: Option<&mut Vec>>, ) -> Result<(), PendingTransition>; + + /// Try to optimize the internal representation. + fn optimize(&mut self); } /// Structure wrapping the abstract tracking state with the relevant resource /// data, such as the reference count and the epoch. -#[derive(Clone, Debug)] +#[derive(Clone)] +#[cfg_attr(debug_assertions, derive(Debug))] struct Resource { ref_count: RefCount, state: S, @@ -143,6 +148,7 @@ pub struct PendingTransition { } /// A tracker for all resources of a given type. +#[cfg_attr(debug_assertions, derive(Debug))] pub struct ResourceTracker { /// An association of known resource indices with their tracked states. map: FastHashMap>, @@ -170,6 +176,13 @@ impl ResourceTracker { } } + /// Try to optimize the internal representation. + pub fn optimize(&mut self) { + for resource in self.map.values_mut() { + resource.state.optimize(); + } + } + /// Return an iterator over used resources keys. pub fn used<'a>(&'a self) -> impl 'a + Iterator { self.map @@ -352,7 +365,7 @@ impl ResourceTracker { } -impl ResourceState for PhantomData { +impl ResourceState for PhantomData { type Id = I; type Selector = (); type Usage = (); @@ -383,10 +396,14 @@ impl ResourceState for PhantomData { ) -> Result<(), PendingTransition> { Ok(()) } + + fn optimize(&mut self) { + } } /// A set of trackers for all relevant resources. +#[cfg_attr(debug_assertions, derive(Debug))] pub struct TrackerSet { pub buffers: ResourceTracker, pub textures: ResourceTracker, @@ -414,6 +431,14 @@ impl TrackerSet { self.bind_groups.clear(); } + /// Try to optimize the tracking representation. + pub fn optimize(&mut self) { + self.buffers.optimize(); + self.textures.optimize(); + self.views.optimize(); + self.bind_groups.optimize(); + } + /// Merge all the trackers of another instance by extending /// the usage. Panics on a conflict. pub fn merge_extend(&mut self, other: &Self) { diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index a503700937..9a91650fe0 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -62,8 +62,7 @@ impl RangedStates { } /// Merge the neighboring ranges together, where possible. - #[cfg(test)] - fn coalesce(&mut self) { + pub fn coalesce(&mut self) { let mut num_removed = 0; let mut iter = self.ranges.iter_mut(); let mut cur = match iter.next() { diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index 07b4322b24..063772e44c 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -195,6 +195,14 @@ impl ResourceState for TextureState { Ok(()) } + + fn optimize(&mut self) { + for mip in self.mips.iter_mut() { + mip.color.coalesce(); + mip.depth.coalesce(); + mip.stencil.coalesce(); + } + } } From 57494d63948f1cf2eb6643b6b1a0294406c42c16 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 14:04:32 -0400 Subject: [PATCH 24/25] Crucial fix to merge-replace semantics - always use the last destination state --- wgpu-native/src/track/buffer.rs | 26 ++++++++++++++++++++++++-- wgpu-native/src/track/mod.rs | 4 ++++ wgpu-native/src/track/texture.rs | 13 +++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/wgpu-native/src/track/buffer.rs b/wgpu-native/src/track/buffer.rs index ef0b94a97c..b484f3fab3 100644 --- a/wgpu-native/src/track/buffer.rs +++ b/wgpu-native/src/track/buffer.rs @@ -75,8 +75,30 @@ impl ResourceState for BufferState { stitch: Stitch, output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { - let usage = other.select(stitch); - self.change(id, (), usage, output) + let old = self.last; + let new = other.select(stitch); + self.last = if old == new { + other.last + } else { + let pending = PendingTransition { + id, + selector: (), + usage: old .. new, + }; + match output { + Some(transitions) => { + transitions.push(pending); + other.last + } + None => { + if !old.is_empty() && BufferUsage::WRITE_ALL.intersects(old | new) { + return Err(pending); + } + old | new + } + } + }; + Ok(()) } fn optimize(&mut self) { diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index 851e16882a..c446aac2f8 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -115,6 +115,10 @@ pub trait ResourceState: Clone + Default { /// `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, diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index 063772e44c..a1a86231f9 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -34,16 +34,21 @@ impl PendingTransition { 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 presense 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. - fn record(self, output: Option<&mut &mut Vec>) -> Result { + /// + /// When a transition is generated, returns the specified `replace` usage. + fn record( + self, output: Option<&mut &mut Vec>, replace: TextureUsage + ) -> Result { let u = self.usage.clone(); match output { Some(out) => { out.push(self); - Ok(u.end) + Ok(replace) } None => { if !u.start.is_empty() && TextureUsage::WRITE_ALL.intersects(u.start | u.end) { @@ -130,7 +135,7 @@ impl ResourceState for TextureState { }, usage: unit.last .. usage, }; - unit.last = pending.record(output.as_mut())?; + unit.last = pending.record(output.as_mut(), usage)?; } } } @@ -180,7 +185,7 @@ impl ResourceState for TextureState { }, usage: start.last .. final_usage, }; - final_usage = pending.record(output.as_mut())?; + final_usage = pending.record(output.as_mut(), end.last)?; } Unit { init: start.init, From 3753309b658dd365ee6ef05fcd974e65a5520cad Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 16 Jun 2019 07:25:30 -0700 Subject: [PATCH 25/25] Last comments and renames in the tracking module, according to the review --- wgpu-native/src/command/transfer.rs | 2 +- wgpu-native/src/track/mod.rs | 10 +++++----- wgpu-native/src/track/range.rs | 2 +- wgpu-native/src/track/texture.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs index 95e71b6328..a6f686a8ce 100644 --- a/wgpu-native/src/command/transfer.rs +++ b/wgpu-native/src/command/transfer.rs @@ -40,7 +40,7 @@ pub struct TextureCopyView { } impl TextureCopyView { - //TODO: we currently access each texture twice for a transer, + //TODO: we currently access each texture twice for a transfer, // once only to get the aspect flags, which is unfortunate. fn to_selector(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceRange { let level = self.mip_level as hal::image::Level; diff --git a/wgpu-native/src/track/mod.rs b/wgpu-native/src/track/mod.rs index c446aac2f8..d3cb84ef5a 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-native/src/track/mod.rs @@ -241,7 +241,7 @@ impl ResourceTracker { /// Make sure that a resource is tracked, and return a mutable /// reference to it. - fn grab<'a>( + fn get_or_insert<'a>( map: &'a mut FastHashMap>, id: S::Id, ref_count: &RefCount, @@ -271,7 +271,7 @@ impl ResourceTracker { selector: S::Selector, usage: S::Usage, ) -> Result<(), PendingTransition> { - Self::grab(&mut self.map, id, ref_count) + Self::get_or_insert(&mut self.map, id, ref_count) .state.change(id, selector, usage, None) } @@ -283,13 +283,13 @@ impl ResourceTracker { selector: S::Selector, usage: S::Usage, ) -> Drain> { - let res = Self::grab(&mut self.map, id, ref_count); + let res = Self::get_or_insert(&mut self.map, id, ref_count); res.state.change(id, selector, usage, Some(&mut self.temp)) .ok(); //TODO: unwrap? self.temp.drain(..) } - /// Merge another tacker into `self` by extending the current states + /// Merge another tracker into `self` by extending the current states /// without any transitions. pub fn merge_extend( &mut self, other: &Self, @@ -309,7 +309,7 @@ impl ResourceTracker { Ok(()) } - /// Merge another tacker, adding it's transitions to `self`. + /// Merge another tracker, adding it's transitions to `self`. /// Transitions the current usage to the new one. pub fn merge_replace<'a>( &'a mut self, diff --git a/wgpu-native/src/track/range.rs b/wgpu-native/src/track/range.rs index 9a91650fe0..c948ea6479 100644 --- a/wgpu-native/src/track/range.rs +++ b/wgpu-native/src/track/range.rs @@ -103,7 +103,7 @@ impl RangedStates { } /// Split the storage ranges in such a way that there is a linear subset of - /// them occuping exactly `index` range, which is returned mutably. + /// them occupying exactly `index` range, which is returned mutably. /// /// Gaps in the ranges are filled with `default` value. pub fn isolate(&mut self, index: &Range, default: T) -> &mut [(Range, T)] { diff --git a/wgpu-native/src/track/texture.rs b/wgpu-native/src/track/texture.rs index a1a86231f9..d64186e36b 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-native/src/track/texture.rs @@ -35,7 +35,7 @@ impl PendingTransition { } //TODO: make this less awkward! - /// Check for the validity of `self` with regards to the presense of `output`. + /// 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.