From 017a54ff97de1c32d220d3de358a50d3df51f546 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 6 Jan 2020 21:20:35 -0500 Subject: [PATCH] Refactor usage tracking to be truly sparse --- wgpu-core/src/command/compute.rs | 7 +- wgpu-core/src/command/mod.rs | 47 +++-- wgpu-core/src/command/transfer.rs | 64 +------ wgpu-core/src/device/mod.rs | 44 +++-- wgpu-core/src/track/buffer.rs | 28 +-- wgpu-core/src/track/mod.rs | 79 ++++++--- wgpu-core/src/track/range.rs | 125 ++++++++------ wgpu-core/src/track/texture.rs | 275 ++++++++++++++---------------- wgpu-native/src/device.rs | 4 +- 9 files changed, 322 insertions(+), 351 deletions(-) diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index f2bba759c..a4da85373 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -174,12 +174,7 @@ impl Global { ); assert!(src_buffer.usage.contains(BufferUsage::INDIRECT)); - let barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &src_buffer.raw, - families: None, - range: None .. None, - }); + let barriers = src_pending.map(|pending| pending.into_hal(src_buffer)); unsafe { pass.raw.pipeline_barrier( diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index c88319c56..e5401e7f5 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -44,7 +44,15 @@ use crate::{ use arrayvec::ArrayVec; use hal::{adapter::PhysicalDevice as _, command::CommandBuffer as _, device::Device as _}; -use std::{borrow::Borrow, collections::hash_map::Entry, iter, mem, slice, thread::ThreadId}; +use std::{ + borrow::Borrow, + collections::hash_map::Entry, + iter, + marker::PhantomData, + mem, + slice, + thread::ThreadId, +}; pub struct RenderBundle { @@ -128,25 +136,15 @@ impl CommandBuffer { .buffers .merge_replace(&head.buffers) .map(|pending| { - log::trace!("\tbuffer -> {:?}", pending); - hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &buffer_guard[pending.id].raw, - range: None .. None, - families: None, - } + let buf = &buffer_guard[pending.id]; + pending.into_hal(buf) }); let texture_barriers = base .textures .merge_replace(&head.textures) .map(|pending| { - log::trace!("\ttexture -> {:?}", pending); - hal::memory::Barrier::Image { - states: pending.to_states(), - target: &texture_guard[pending.id].raw, - range: pending.selector, - families: None, - } + let tex = &texture_guard[pending.id]; + pending.into_hal(tex) }); base.views.merge_extend(&head.views).unwrap(); base.bind_groups.merge_extend(&head.bind_groups).unwrap(); @@ -328,7 +326,7 @@ impl> Global { let first_use = cmb.trackers.views.init( at.attachment, view.life_guard.add_ref(), - &(), + PhantomData, ).is_some(); let layouts = match view.inner { @@ -388,7 +386,7 @@ impl> Global { let first_use = cmb.trackers.views.init( resolve_target, view.life_guard.add_ref(), - &(), + PhantomData, ).is_some(); let layouts = match view.inner { @@ -451,15 +449,13 @@ impl> Global { assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); let usage = consistent_usage.unwrap_or(TextureUsage::OUTPUT_ATTACHMENT); - match trackers.textures.init( + // this is important to record the `first` state. + let _ = trackers.textures.change_replace( source_id.value, - source_id.ref_count.clone(), - &texture.full_range, - ) { - Some(mut init) => init.set(view_range.clone(), usage), - None => panic!("Your texture {:?} is in the another attachment!", source_id.value), - }; - + &source_id.ref_count, + view_range.clone(), + usage, + ); if consistent_usage.is_some() { // If we expect the texture to be transited to a new state by the // render pass configuration, make the tracker aware of that. @@ -468,7 +464,6 @@ impl> Global { &source_id.ref_count, view_range.clone(), TextureUsage::OUTPUT_ATTACHMENT, - &texture.full_range, ); }; } diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index 41220fcd0..0b5a4b4be 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -85,13 +85,7 @@ impl Global { .buffers .use_replace(&*buffer_guard, source, (), BufferUsage::COPY_SRC); assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); - - barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &src_buffer.raw, - families: None, - range: None .. None, - })); + barriers.extend(src_pending.map(|pending| pending.into_hal(src_buffer))); let (dst_buffer, dst_pending) = cmb.trackers.buffers.use_replace( &*buffer_guard, @@ -100,13 +94,7 @@ impl Global { BufferUsage::COPY_DST, ); assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); - - barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &dst_buffer.raw, - families: None, - range: None .. None, - })); + barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_buffer))); let region = hal::command::BufferCopy { src: source_offset, @@ -146,13 +134,7 @@ impl Global { BufferUsage::COPY_SRC, ); assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); - - 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 src_barriers = src_pending.map(|pending| pending.into_hal(src_buffer)); let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( &*texture_guard, @@ -161,13 +143,7 @@ impl Global { TextureUsage::COPY_DST, ); assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); - - let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &dst_texture.raw, - families: None, - range: pending.selector, - }); + let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture)); let bytes_per_texel = conv::map_texture_format(dst_texture.format, cmb.features) .surface_desc() @@ -222,13 +198,7 @@ impl Global { TextureUsage::COPY_SRC, ); assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); - - let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &src_texture.raw, - families: None, - range: pending.selector, - }); + let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture)); let (dst_buffer, dst_barriers) = cmb.trackers.buffers.use_replace( &*buffer_guard, @@ -237,13 +207,7 @@ impl Global { BufferUsage::COPY_DST, ); assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); - - let dst_barrier = dst_barriers.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &dst_buffer.raw, - families: None, - range: None .. None, - }); + let dst_barrier = dst_barriers.map(|pending| pending.into_hal(dst_buffer)); let bytes_per_texel = conv::map_texture_format(src_texture.format, cmb.features) .surface_desc() @@ -303,13 +267,7 @@ impl Global { TextureUsage::COPY_SRC, ); assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); - - barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &src_texture.raw, - families: None, - range: pending.selector, - })); + barriers.extend(src_pending.map(|pending| pending.into_hal(src_texture))); let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( &*texture_guard, @@ -318,13 +276,7 @@ impl Global { TextureUsage::COPY_DST, ); assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); - - barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &dst_texture.raw, - families: None, - range: pending.selector, - })); + barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture))); let region = hal::command::ImageCopy { src_subresource: source.to_sub_layers(aspects), diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index d061f504a..40170c1f4 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -11,7 +11,7 @@ use crate::{ pipeline, resource, swap_chain, - track::{SEPARATE_DEPTH_STENCIL_STATES, TrackerSet}, + track::{BufferState, TextureState, TrackerSet}, BufferAddress, FastHashMap, Features, @@ -36,6 +36,7 @@ use std::{ collections::hash_map::Entry, ffi, iter, + marker::PhantomData, ops, ptr, slice, @@ -473,13 +474,18 @@ impl> Global { let device = &device_guard[device_id]; let buffer = device.create_buffer(device_id, desc); let ref_count = buffer.life_guard.add_ref(); + let range = buffer.full_range; let id = hub.buffers.register_identity(id_in, buffer, &mut token); device .trackers .lock() .buffers - .init(id, ref_count, &()) + .init( + id, + ref_count, + BufferState::from_selector(&range), + ) .unwrap(); id } @@ -499,6 +505,7 @@ impl> Global { let device = &device_guard[device_id]; let mut buffer = device.create_buffer(device_id, &desc); let ref_count = buffer.life_guard.add_ref(); + let range = buffer.full_range; let pointer = match map_buffer(&device.raw, &mut buffer, 0 .. desc.size, HostMap::Write) { Ok(ptr) => ptr, @@ -514,7 +521,7 @@ impl> Global { .buffers.init( id, ref_count, - &(), + BufferState::from_selector(&range), ) .unwrap() .set((), resource::BufferUsage::MAP_WRITE); @@ -632,7 +639,7 @@ impl> Global { .textures.init( id, ref_count, - &range, + TextureState::from_selector(&range), ) .unwrap() .set(range, resource::TextureUsage::UNINITIALIZED); @@ -686,15 +693,7 @@ impl> Global { (desc.base_array_layer + desc.array_layer_count) as u16 }; let range = hal::image::SubresourceRange { - aspects: if SEPARATE_DEPTH_STENCIL_STATES { - match desc.aspect { - resource::TextureAspect::All => texture.full_range.aspects, - resource::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, - resource::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, - } - } else { - texture.full_range.aspects - }, + aspects: texture.full_range.aspects, levels: desc.base_mip_level as u8 .. end_level, layers: desc.base_array_layer as u16 .. end_layer, }; @@ -744,7 +743,7 @@ impl> Global { let id = hub.texture_views.register_identity(id_in, view, &mut token); device.trackers .lock() - .views.init(id, ref_count, &()) + .views.init(id, ref_count, PhantomData) .unwrap(); id } @@ -822,7 +821,7 @@ impl> Global { let id = hub.samplers.register_identity(id_in, sampler, &mut token); device.trackers .lock() - .samplers.init(id, ref_count, &()) + .samplers.init(id, ref_count, PhantomData) .unwrap(); id } @@ -1080,7 +1079,6 @@ impl> Global { &source_id.ref_count, view.range.clone(), usage, - &texture.full_range, ) .unwrap(); assert!(texture.usage.contains(usage)); @@ -1130,7 +1128,7 @@ impl> Global { .trackers .lock() .bind_groups - .init(id, ref_count, &()) + .init(id, ref_count, PhantomData) .unwrap(); id } @@ -1886,7 +1884,7 @@ impl Global { let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); - let (device_id, ref_count, full_range) = { + let (device_id, ref_count) = { let (mut buffer_guard, _) = hub.buffers.write(&mut token); let buffer = &mut buffer_guard[buffer_id]; @@ -1906,13 +1904,9 @@ impl Global { buffer.pending_mapping = Some(resource::BufferPendingMapping { range, op: operation, - parent_ref_count: buffer.life_guard.add_ref() + parent_ref_count: buffer.life_guard.add_ref(), }); - ( - buffer.device_id.value, - buffer.life_guard.add_ref(), - buffer.full_range, - ) + (buffer.device_id.value, buffer.life_guard.add_ref()) }; let device = &device_guard[device_id]; @@ -1921,7 +1915,7 @@ impl Global { .trackers .lock() .buffers - .change_replace(buffer_id, &ref_count, (), usage, &full_range); + .change_replace(buffer_id, &ref_count, (), usage); device .lock_life(&mut token) diff --git a/wgpu-core/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs index 9a4feee77..83a9bb2ef 100644 --- a/wgpu-core/src/track/buffer.rs +++ b/wgpu-core/src/track/buffer.rs @@ -3,18 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::{PendingTransition, ResourceState, Unit}; -use crate::{conv, id::BufferId, resource::BufferUsage}; -use std::ops::Range; +use crate::{id::BufferId, resource::BufferUsage}; //TODO: store `hal::buffer::State` here to avoid extra conversions 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) - } - fn collapse(self) -> Result { if self.usage.start.is_empty() || self.usage.start == self.usage.end @@ -27,17 +21,25 @@ impl PendingTransition { } } -impl ResourceState for BufferState { - type Id = BufferId; - type Selector = (); - type Usage = BufferUsage; - - fn new(_full_selector: &Self::Selector) -> Self { +impl Default for BufferState { + fn default() -> Self { BufferState { first: None, last: BufferUsage::empty(), } } +} + +impl BufferState { + pub fn from_selector(_full_selector: &()) -> Self { + BufferState::default() + } +} + +impl ResourceState for BufferState { + type Id = BufferId; + type Selector = (); + type Usage = BufferUsage; fn query(&self, _selector: Self::Selector) -> Option { Some(self.last) diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index 454d62ee6..cbec25aa2 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -7,8 +7,10 @@ mod range; mod texture; use crate::{ + conv, hub::Storage, id::{BindGroupId, SamplerId, TextureViewId, TypedId}, + resource, Backend, Epoch, FastHashMap, @@ -21,16 +23,14 @@ use std::{ collections::hash_map::Entry, fmt, marker::PhantomData, - ops::Range, + ops, vec::Drain, }; -use buffer::BufferState; -use texture::TextureState; +pub use buffer::BufferState; +pub use texture::TextureState; -pub const SEPARATE_DEPTH_STENCIL_STATES: bool = false; - /// 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)] @@ -56,7 +56,7 @@ impl Unit { /// The main trait that abstracts away the tracking logic of /// a particular resource type, like a buffer or a texture. -pub trait ResourceState: Clone { +pub trait ResourceState: Clone + Default { /// Corresponding `HUB` identifier. type Id: Copy + fmt::Debug + TypedId; /// A type specifying the sub-resources. @@ -64,9 +64,6 @@ pub trait ResourceState: Clone { /// Usage type for a `Unit` of a sub-resource. type Usage: fmt::Debug; - /// Create a new resource state to track the specified subresources. - fn new(full_selector: &Self::Selector) -> Self; - /// Check if all the selected sub-resources have the same /// usage, and return it. /// @@ -129,7 +126,44 @@ struct Resource { pub struct PendingTransition { pub id: S::Id, pub selector: S::Selector, - pub usage: Range, + pub usage: ops::Range, +} + +impl PendingTransition { + /// Produce the gfx-hal barrier corresponding to the transition. + pub fn into_hal<'a, B: hal::Backend>( + self, + buf: &'a resource::Buffer, + ) -> hal::memory::Barrier<'a, B> { + log::trace!("\tbuffer -> {:?}", self); + hal::memory::Barrier::Buffer { + states: conv::map_buffer_state(self.usage.start) .. conv::map_buffer_state(self.usage.end), + target: &buf.raw, + range: None .. None, + families: None, + } + } +} + +impl PendingTransition { + /// Produce the gfx-hal barrier corresponding to the transition. + pub fn into_hal<'a, B: hal::Backend>( + self, + tex: &'a resource::Texture, + ) -> hal::memory::Barrier<'a, B> { + log::trace!("\ttexture -> {:?}", self); + let aspects = tex.full_range.aspects; + hal::memory::Barrier::Image { + states: conv::map_texture_state(self.usage.start, aspects) + .. conv::map_texture_state(self.usage.end, aspects), + target: &tex.raw, + range: hal::image::SubresourceRange { + aspects, + .. self.selector + }, + families: None, + } + } } /// Helper initialization structure that allows setting the usage on @@ -238,7 +272,7 @@ impl ResourceTracker { &mut self, id: S::Id, ref_count: RefCount, - full_selector: &S::Selector, + state: S, ) -> Option> { let (index, epoch, backend) = id.unzip(); debug_assert_eq!(backend, self.backend); @@ -246,7 +280,7 @@ impl ResourceTracker { Entry::Vacant(e) => { let res = e.insert(Resource { ref_count, - state: S::new(full_selector), + state, epoch, }); Some(Initializer { @@ -277,14 +311,13 @@ impl ResourceTracker { map: &'a mut FastHashMap>, id: S::Id, ref_count: &RefCount, - full_selector: &S::Selector, ) -> &'a mut Resource { let (index, epoch, backend) = id.unzip(); debug_assert_eq!(self_backend, backend); match map.entry(index) { Entry::Vacant(e) => e.insert(Resource { ref_count: ref_count.clone(), - state: S::new(full_selector), + state: S::default(), epoch, }), Entry::Occupied(e) => { @@ -303,9 +336,8 @@ impl ResourceTracker { ref_count: &RefCount, selector: S::Selector, usage: S::Usage, - full_selector: &S::Selector, ) -> Result<(), PendingTransition> { - Self::get_or_insert(self.backend, &mut self.map, id, ref_count, full_selector) + Self::get_or_insert(self.backend, &mut self.map, id, ref_count) .state .change(id, selector, usage, None) } @@ -317,9 +349,8 @@ impl ResourceTracker { ref_count: &RefCount, selector: S::Selector, usage: S::Usage, - full_selector: &S::Selector, ) -> Drain> { - let res = Self::get_or_insert(self.backend, &mut self.map, id, ref_count, full_selector); + let res = Self::get_or_insert(self.backend, &mut self.map, id, ref_count); res.state .change(id, selector, usage, Some(&mut self.temp)) .ok(); //TODO: unwrap? @@ -376,7 +407,7 @@ impl ResourceTracker { /// 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 + Borrow>( + pub fn use_extend<'a, T: 'a + Borrow>( &mut self, storage: &'a Storage, id: S::Id, @@ -384,7 +415,7 @@ impl ResourceTracker { usage: S::Usage, ) -> Result<&'a T, S::Usage> { let item = &storage[id]; - self.change_extend(id, item.borrow(), selector, usage, item.borrow()) + self.change_extend(id, item.borrow(), selector, usage) .map(|()| item) .map_err(|pending| pending.usage.start) } @@ -393,7 +424,7 @@ impl ResourceTracker { /// 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 + Borrow>( + pub fn use_replace<'a, T: 'a + Borrow>( &mut self, storage: &'a Storage, id: S::Id, @@ -401,7 +432,7 @@ impl ResourceTracker { usage: S::Usage, ) -> (&'a T, Drain>) { let item = &storage[id]; - let drain = self.change_replace(id, item.borrow(), selector, usage, item.borrow()); + let drain = self.change_replace(id, item.borrow(), selector, usage); (item, drain) } } @@ -412,10 +443,6 @@ impl ResourceState for PhantomData { type Selector = (); type Usage = (); - fn new(_full_selector: &Self::Selector) -> Self { - PhantomData - } - fn query(&self, _selector: Self::Selector) -> Option { Some(()) } diff --git a/wgpu-core/src/track/range.rs b/wgpu-core/src/track/range.rs index fd8634262..2faa55475 100644 --- a/wgpu-core/src/track/range.rs +++ b/wgpu-core/src/track/range.rs @@ -2,7 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{cmp::Ordering, fmt::Debug, iter::Peekable, ops::Range, slice::Iter}; +use smallvec::SmallVec; + +use std::{ + cmp::Ordering, + fmt::Debug, + iter, + ops::Range, + slice::Iter, +}; /// Structure that keeps track of a I -> T mapping, /// optimized for a case where keys of the same values @@ -11,21 +19,27 @@ use std::{cmp::Ordering, fmt::Debug, iter::Peekable, ops::Range, slice::Iter}; 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)>, -} - -impl Default for RangedStates { - fn default() -> Self { - RangedStates { ranges: Vec::new() } - } + ranges: SmallVec<[(Range, T); 1]>, } impl RangedStates { + pub fn empty() -> Self { + RangedStates { + ranges: SmallVec::new(), + } + } + + pub fn from_range(range: Range, value: T) -> Self { + RangedStates { + ranges: iter::once((range, value)).collect(), + } + } + /// Construct a new instance from a slice of ranges. #[cfg(test)] - pub fn new(values: &[(Range, T)]) -> Self { + pub fn from_slice(values: &[(Range, T)]) -> Self { RangedStates { - ranges: values.to_vec(), + ranges: values.iter().cloned().collect(), } } @@ -189,8 +203,8 @@ impl RangedStates { #[derive(Debug)] pub struct Merge<'a, I, T> { base: I, - sa: Peekable, T)>>, - sb: Peekable, T)>>, + sa: iter::Peekable, T)>>, + sb: iter::Peekable, T)>>, } impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { @@ -268,55 +282,58 @@ mod test { use std::{fmt::Debug, ops::Range}; fn easy_merge( - ra: Vec<(Range, T)>, - rb: Vec<(Range, T)>, + ra: &[(Range, T)], + rb: &[(Range, T)], ) -> Vec<(Range, Range>)> { - RangedStates { ranges: ra } - .merge(&RangedStates { ranges: rb }, 0) + RangedStates::from_slice(ra) + .merge(&RangedStates::from_slice(rb), 0) .collect() } #[test] fn sane_good() { - let rs = RangedStates { - ranges: vec![(1 .. 4, 9u8), (4 .. 5, 9)], - }; + let rs = RangedStates::from_slice( + &[(1 .. 4, 9u8), (4 .. 5, 9)], + ); rs.check_sanity(); } #[test] #[should_panic] fn sane_empty() { - let rs = RangedStates { - ranges: vec![(1 .. 4, 9u8), (5 .. 5, 9)], - }; + let rs = RangedStates::from_slice( + &[(1 .. 4, 9u8), (5 .. 5, 9)], + ); rs.check_sanity(); } #[test] #[should_panic] fn sane_intersect() { - let rs = RangedStates { - ranges: vec![(1 .. 4, 9u8), (3 .. 5, 9)], - }; + let rs = RangedStates::from_slice( + &[(1 .. 4, 9u8), (3 .. 5, 9)], + ); rs.check_sanity(); } #[test] fn coalesce() { - let mut rs = RangedStates { - ranges: vec![(1 .. 4, 9u8), (4 .. 5, 9), (5 .. 7, 1), (8 .. 9, 1)], - }; + let mut rs = RangedStates::from_slice( + &[(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),]); + assert_eq!( + rs.ranges.as_slice(), + &[(1 .. 5, 9), (5 .. 7, 1), (8 .. 9, 1),] + ); } #[test] fn query() { - let rs = RangedStates { - ranges: vec![(1 .. 4, 1u8), (5 .. 7, 2)], - }; + let rs = RangedStates::from_slice( + &[(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(()))); @@ -324,9 +341,9 @@ mod test { #[test] fn isolate() { - let rs = RangedStates { - ranges: vec![(1 .. 4, 9u8), (4 .. 5, 9), (5 .. 7, 1), (8 .. 9, 1)], - }; + let rs = RangedStates::from_slice( + &[(1 .. 4, 9u8), (4 .. 5, 9), (5 .. 7, 1), (8 .. 9, 1)], + ); assert_eq!(&rs.sanely_isolated(4 .. 5, 0), &[(4 .. 5, 9u8),]); assert_eq!( &rs.sanely_isolated(0 .. 6, 0), @@ -345,28 +362,31 @@ mod test { #[test] fn merge_same() { assert_eq!( - easy_merge(vec![(1 .. 4, 0u8),], vec![(1 .. 4, 2u8),],), - vec![(1 .. 4, Some(0) .. Some(2)),] + &easy_merge(&[(1 .. 4, 0u8),], &[(1 .. 4, 2u8),],), + &[(1 .. 4, Some(0) .. Some(2)),] ); } #[test] fn merge_empty() { assert_eq!( - easy_merge(vec![(1 .. 2, 0u8),], vec![],), - vec![(1 .. 2, Some(0) .. None),] + &easy_merge(&[(1 .. 2, 0u8),], &[],), + &[(1 .. 2, Some(0) .. None),] ); assert_eq!( - easy_merge(vec![], vec![(3 .. 4, 1u8),],), - vec![(3 .. 4, None .. Some(1)),] + &easy_merge(&[], &[(3 .. 4, 1u8),],), + &[(3 .. 4, None .. Some(1)),] ); } #[test] fn merge_separate() { assert_eq!( - easy_merge(vec![(1 .. 2, 0u8), (5 .. 6, 1u8),], vec![(2 .. 4, 2u8),],), - vec![ + &easy_merge( + &[(1 .. 2, 0u8), (5 .. 6, 1u8),], + &[(2 .. 4, 2u8),], + ), + &[ (1 .. 2, Some(0) .. None), (2 .. 4, None .. Some(2)), (5 .. 6, Some(1) .. None), @@ -377,27 +397,30 @@ mod test { #[test] fn merge_subset() { assert_eq!( - easy_merge(vec![(1 .. 6, 0u8),], vec![(2 .. 4, 2u8),],), - vec![ + &easy_merge( + &[(1 .. 6, 0u8),], + &[(2 .. 4, 2u8),], + ), + &[ (1 .. 2, Some(0) .. None), (2 .. 4, Some(0) .. Some(2)), (4 .. 6, Some(0) .. None), ] ); assert_eq!( - easy_merge(vec![(2 .. 4, 0u8),], vec![(1 .. 4, 2u8),],), - vec![(1 .. 2, None .. Some(2)), (2 .. 4, Some(0) .. Some(2)),] + &easy_merge(&[(2 .. 4, 0u8),], &[(1 .. 4, 2u8),],), + &[(1 .. 2, None .. Some(2)), (2 .. 4, Some(0) .. Some(2)),] ); } #[test] fn merge_all() { assert_eq!( - easy_merge( - vec![(1 .. 4, 0u8), (5 .. 8, 1u8),], - vec![(2 .. 6, 2u8), (7 .. 9, 3u8),], + &easy_merge( + &[(1 .. 4, 0u8), (5 .. 8, 1u8),], + &[(2 .. 6, 2u8), (7 .. 9, 3u8),], ), - vec![ + &[ (1 .. 2, Some(0) .. None), (2 .. 4, Some(0) .. Some(2)), (4 .. 5, None .. Some(2)), diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index 0f9a64291..1cc71bc8e 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -2,30 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use super::{SEPARATE_DEPTH_STENCIL_STATES, range::RangedStates, PendingTransition, ResourceState, Unit}; -use crate::{conv, device::MAX_MIP_LEVELS, id::TextureId, resource::TextureUsage}; +use super::{range::RangedStates, PendingTransition, ResourceState, Unit}; +use crate::{device::MAX_MIP_LEVELS, id::TextureId, resource::TextureUsage}; use arrayvec::ArrayVec; -use std::ops::Range; +use std::{iter, ops::Range}; //TODO: store `hal::image::State` here to avoid extra conversions type PlaneStates = RangedStates>; -type MipState = ArrayVec<[(hal::format::Aspects, PlaneStates); 2]>; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct TextureState { - mips: ArrayVec<[MipState; MAX_MIP_LEVELS]>, + mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS]>, + /// True if we have the information about all the subresources here + full: bool, } 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) - } - fn collapse(self) -> Result { if self.usage.start.is_empty() || self.usage.start == self.usage.end @@ -38,47 +33,49 @@ impl PendingTransition { } } +impl TextureState { + pub fn from_selector(full_selector: &hal::image::SubresourceRange) -> Self { + debug_assert_eq!(full_selector.layers.start, 0); + debug_assert_eq!(full_selector.levels.start, 0); + TextureState { + mips: iter::repeat_with(|| { + PlaneStates::from_range( + 0 .. full_selector.layers.end, + Unit::new(TextureUsage::UNINITIALIZED), + ) + }) + .take(full_selector.levels.end as usize) + .collect(), + full: true, + } + } +} + impl ResourceState for TextureState { type Id = TextureId; type Selector = hal::image::SubresourceRange; type Usage = TextureUsage; - fn new(full_selector: &Self::Selector) -> Self { - TextureState { - mips: (0 .. full_selector.levels.end) - .map(|_| { - let mut slices = ArrayVec::new(); - let aspects_without_stencil = full_selector.aspects & !hal::format::Aspects::STENCIL; - if SEPARATE_DEPTH_STENCIL_STATES && full_selector.aspects != aspects_without_stencil { - slices.push((aspects_without_stencil, PlaneStates::default())); - slices.push((hal::format::Aspects::STENCIL, PlaneStates::default())); - } else { - slices.push((full_selector.aspects, PlaneStates::default())) - } - slices - }) - .collect() - } - } - fn query(&self, selector: Self::Selector) -> Option { let mut result = None; + // Note: we only consider the subresources tracked by `self`. + // If some are not known to `self`, it means the can assume the + // initial state to whatever we need, which we can always make + // to be the same as the query result for the known subresources. let num_levels = self.mips.len(); + if self.full { + assert!(num_levels >= selector.levels.end as usize); + } 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 &(aspects, ref plane_states) in mip { - if !selector.aspects.intersects(aspects) { - continue; - } - 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, + match mip.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, } } result @@ -91,44 +88,44 @@ impl ResourceState for TextureState { usage: Self::Usage, mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { + if self.full { + assert!(self.mips.len() >= selector.levels.end as usize); + } else { + while self.mips.len() < selector.levels.end as usize { + self.mips.push(PlaneStates::empty()); + } + } 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 (mip_aspects, ref mut plane_states) in mip { - let aspects = selector.aspects & mip_aspects; - if aspects.is_empty() { + let layers = mip.isolate(&selector.layers, Unit::new(usage)); + for &mut (ref range, ref mut unit) in layers { + if unit.last == usage && TextureUsage::ORDERED.contains(usage) { continue; } - debug_assert_eq!(aspects, mip_aspects); - let layers = plane_states.isolate(&selector.layers, Unit::new(usage)); - for &mut (ref range, ref mut unit) in layers { - if unit.last == usage && TextureUsage::ORDERED.contains(usage) { - continue; - } - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects, - levels: level .. level + 1, - layers: range.clone(), - }, - usage: unit.last .. usage, - }; + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::empty(), + levels: level .. level + 1, + layers: range.clone(), + }, + usage: unit.last .. usage, + }; - unit.last = match output { - None => pending.collapse()?, - Some(ref mut out) => { - out.push(pending); - if unit.first.is_none() { - unit.first = Some(unit.last); - } - usage + unit.last = match output { + None => pending.collapse()?, + Some(ref mut out) => { + out.push(pending); + if unit.first.is_none() { + unit.first = Some(unit.last); } - }; - } + usage + } + }; } } Ok(()) @@ -141,74 +138,75 @@ impl ResourceState for TextureState { mut output: Option<&mut Vec>>, ) -> Result<(), PendingTransition> { let mut temp = Vec::new(); - while self.mips.len() < other.mips.len() as usize { - self.mips.push(MipState::default()); + if self.full { + assert!(self.mips.len() >= other.mips.len()); + } else { + while self.mips.len() < other.mips.len() { + self.mips.push(PlaneStates::empty()); + } } for (mip_id, (mip_self, mip_other)) in self.mips.iter_mut().zip(&other.mips).enumerate() { let level = mip_id as hal::image::Level; - for (&mut (aspects, ref mut planes_self), &(aspects_other, ref planes_other)) in mip_self.iter_mut().zip(mip_other) { - debug_assert_eq!(aspects, aspects_other); - temp.extend(planes_self.merge(planes_other, 0)); - planes_self.clear(); + temp.extend(mip_self.merge(mip_other, 0)); + mip_self.clear(); - 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), - } => end, - Range { - start: Some(start), - end: Some(end), - } => { - let to_usage = end.port(); - if start.last == to_usage - && TextureUsage::ORDERED.contains(to_usage) - { - Unit { - first: start.first, - last: end.last, - } - } else { - let pending = PendingTransition { - id, - selector: hal::image::SubresourceRange { - aspects, - levels: level .. level+1, - layers: layers.clone(), - }, - usage: start.last .. to_usage, - }; + 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), + } => end, + Range { + start: Some(start), + end: Some(end), + } => { + let to_usage = end.port(); + if start.last == to_usage + && TextureUsage::ORDERED.contains(to_usage) + { + Unit { + first: start.first, + last: end.last, + } + } else { + let pending = PendingTransition { + id, + selector: hal::image::SubresourceRange { + aspects: hal::format::Aspects::empty(), + levels: level .. level+1, + layers: layers.clone(), + }, + usage: start.last .. to_usage, + }; - match output { - None => { - Unit { - first: start.first, - last: pending.collapse()?, - } + match output { + None => { + Unit { + first: start.first, + last: pending.collapse()?, } - Some(ref mut out) => { - out.push(pending); - Unit { - first: Some(start.last), - last: end.last, - } + } + Some(ref mut out) => { + out.push(pending); + Unit { + first: Some(start.last), + last: end.last, } } } } - }; - planes_self.append(layers, unit); - } + } + }; + mip_self.append(layers, unit); } } @@ -217,9 +215,7 @@ impl ResourceState for TextureState { fn optimize(&mut self) { for mip in self.mips.iter_mut() { - for &mut (_, ref mut planes) in mip.iter_mut() { - planes.coalesce(); - } + mip.coalesce(); } } } @@ -234,16 +230,14 @@ mod test { #[test] fn query() { - let mut ts = TextureState::new(&SubresourceRange { - aspects: Aspects::COLOR, - levels: 0 .. 2, - layers: 0 .. 10, - }); - ts.mips[1][0].1 = PlaneStates::new(&[ + let mut ts = TextureState::default(); + ts.mips.push(PlaneStates::empty()); + ts.mips.push(PlaneStates::from_slice(&[ (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, @@ -253,15 +247,6 @@ mod test { // 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, diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index 81962131d..f178b014a 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -8,8 +8,6 @@ use core::{gfx_select, hub::Token, id}; use std::{marker::PhantomData, slice}; -use libc::{c_ulong}; - pub type RequestAdapterCallback = unsafe extern "C" fn(id: id::AdapterId, userdata: *mut std::ffi::c_void); pub type BufferMapReadCallback = @@ -80,7 +78,7 @@ pub fn wgpu_create_surface(raw_handle: raw_window_handle::RawWindowHandle) -> id #[no_mangle] pub extern "C" fn wgpu_create_surface_from_xlib( display: *mut *const std::ffi::c_void, - window: c_ulong, + window: libc::c_ulong, ) -> id::SurfaceId { use raw_window_handle::unix::XlibHandle; wgpu_create_surface(raw_window_handle::RawWindowHandle::Xlib(XlibHandle {