Refactor usage tracking to be truly sparse

This commit is contained in:
Dzmitry Malyshau 2020-01-06 21:20:35 -05:00
parent ba0acc94a1
commit 017a54ff97
9 changed files with 322 additions and 351 deletions

View File

@ -174,12 +174,7 @@ impl<F> Global<F> {
);
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(

View File

@ -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<B: hal::Backend> {
@ -128,25 +136,15 @@ impl<B: GfxBackend> CommandBuffer<B> {
.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<F: IdentityFilter<RenderPassId>> Global<F> {
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<F: IdentityFilter<RenderPassId>> Global<F> {
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<F: IdentityFilter<RenderPassId>> Global<F> {
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<F: IdentityFilter<RenderPassId>> Global<F> {
&source_id.ref_count,
view_range.clone(),
TextureUsage::OUTPUT_ATTACHMENT,
&texture.full_range,
);
};
}

View File

@ -85,13 +85,7 @@ impl<F> Global<F> {
.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<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
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),

View File

@ -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<F: IdentityFilter<id::BufferId>> Global<F> {
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<F: IdentityFilter<id::BufferId>> Global<F> {
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<F: IdentityFilter<id::BufferId>> Global<F> {
.buffers.init(
id,
ref_count,
&(),
BufferState::from_selector(&range),
)
.unwrap()
.set((), resource::BufferUsage::MAP_WRITE);
@ -632,7 +639,7 @@ impl<F: IdentityFilter<id::TextureId>> Global<F> {
.textures.init(
id,
ref_count,
&range,
TextureState::from_selector(&range),
)
.unwrap()
.set(range, resource::TextureUsage::UNINITIALIZED);
@ -686,15 +693,7 @@ impl<F: IdentityFilter<id::TextureViewId>> Global<F> {
(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<F: IdentityFilter<id::TextureViewId>> Global<F> {
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<F: IdentityFilter<id::SamplerId>> Global<F> {
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<F: IdentityFilter<id::BindGroupId>> Global<F> {
&source_id.ref_count,
view.range.clone(),
usage,
&texture.full_range,
)
.unwrap();
assert!(texture.usage.contains(usage));
@ -1130,7 +1128,7 @@ impl<F: IdentityFilter<id::BindGroupId>> Global<F> {
.trackers
.lock()
.bind_groups
.init(id, ref_count, &())
.init(id, ref_count, PhantomData)
.unwrap();
id
}
@ -1886,7 +1884,7 @@ impl<F> Global<F> {
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<F> Global<F> {
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<F> Global<F> {
.trackers
.lock()
.buffers
.change_replace(buffer_id, &ref_count, (), usage, &full_range);
.change_replace(buffer_id, &ref_count, (), usage);
device
.lock_life(&mut token)

View File

@ -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<BufferUsage>;
impl PendingTransition<BufferState> {
/// Produce the gfx-hal buffer states corresponding to the transition.
pub fn to_states(&self) -> Range<hal::buffer::State> {
conv::map_buffer_state(self.usage.start) .. conv::map_buffer_state(self.usage.end)
}
fn collapse(self) -> Result<BufferUsage, Self> {
if self.usage.start.is_empty()
|| self.usage.start == self.usage.end
@ -27,17 +21,25 @@ impl PendingTransition<BufferState> {
}
}
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<Self::Usage> {
Some(self.last)

View File

@ -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<U: Copy> Unit<U> {
/// 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<S> {
pub struct PendingTransition<S: ResourceState> {
pub id: S::Id,
pub selector: S::Selector,
pub usage: Range<S::Usage>,
pub usage: ops::Range<S::Usage>,
}
impl PendingTransition<BufferState> {
/// Produce the gfx-hal barrier corresponding to the transition.
pub fn into_hal<'a, B: hal::Backend>(
self,
buf: &'a resource::Buffer<B>,
) -> 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<TextureState> {
/// Produce the gfx-hal barrier corresponding to the transition.
pub fn into_hal<'a, B: hal::Backend>(
self,
tex: &'a resource::Texture<B>,
) -> 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<S: ResourceState> ResourceTracker<S> {
&mut self,
id: S::Id,
ref_count: RefCount,
full_selector: &S::Selector,
state: S,
) -> Option<Initializer<S>> {
let (index, epoch, backend) = id.unzip();
debug_assert_eq!(backend, self.backend);
@ -246,7 +280,7 @@ impl<S: ResourceState> ResourceTracker<S> {
Entry::Vacant(e) => {
let res = e.insert(Resource {
ref_count,
state: S::new(full_selector),
state,
epoch,
});
Some(Initializer {
@ -277,14 +311,13 @@ impl<S: ResourceState> ResourceTracker<S> {
map: &'a mut FastHashMap<Index, Resource<S>>,
id: S::Id,
ref_count: &RefCount,
full_selector: &S::Selector,
) -> &'a mut Resource<S> {
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<S: ResourceState> ResourceTracker<S> {
ref_count: &RefCount,
selector: S::Selector,
usage: S::Usage,
full_selector: &S::Selector,
) -> Result<(), PendingTransition<S>> {
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<S: ResourceState> ResourceTracker<S> {
ref_count: &RefCount,
selector: S::Selector,
usage: S::Usage,
full_selector: &S::Selector,
) -> Drain<PendingTransition<S>> {
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<S: ResourceState> ResourceTracker<S> {
/// 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<RefCount> + Borrow<S::Selector>>(
pub fn use_extend<'a, T: 'a + Borrow<RefCount>>(
&mut self,
storage: &'a Storage<T, S::Id>,
id: S::Id,
@ -384,7 +415,7 @@ impl<S: ResourceState> ResourceTracker<S> {
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<S: ResourceState> ResourceTracker<S> {
/// 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<RefCount> + Borrow<S::Selector>>(
pub fn use_replace<'a, T: 'a + Borrow<RefCount>>(
&mut self,
storage: &'a Storage<T, S::Id>,
id: S::Id,
@ -401,7 +432,7 @@ impl<S: ResourceState> ResourceTracker<S> {
usage: S::Usage,
) -> (&'a T, Drain<PendingTransition<S>>) {
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<I: Copy + fmt::Debug + TypedId> ResourceState for PhantomData<I> {
type Selector = ();
type Usage = ();
fn new(_full_selector: &Self::Selector) -> Self {
PhantomData
}
fn query(&self, _selector: Self::Selector) -> Option<Self::Usage> {
Some(())
}

View File

@ -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<I, T> {
/// List of ranges, each associated with a singe value.
/// Ranges of keys have to be non-intersecting and ordered.
ranges: Vec<(Range<I>, T)>,
}
impl<I, T> Default for RangedStates<I, T> {
fn default() -> Self {
RangedStates { ranges: Vec::new() }
}
ranges: SmallVec<[(Range<I>, T); 1]>,
}
impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
pub fn empty() -> Self {
RangedStates {
ranges: SmallVec::new(),
}
}
pub fn from_range(range: Range<I>, 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<I>, T)]) -> Self {
pub fn from_slice(values: &[(Range<I>, T)]) -> Self {
RangedStates {
ranges: values.to_vec(),
ranges: values.iter().cloned().collect(),
}
}
@ -189,8 +203,8 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
#[derive(Debug)]
pub struct Merge<'a, I, T> {
base: I,
sa: Peekable<Iter<'a, (Range<I>, T)>>,
sb: Peekable<Iter<'a, (Range<I>, T)>>,
sa: iter::Peekable<Iter<'a, (Range<I>, T)>>,
sb: iter::Peekable<Iter<'a, (Range<I>, 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<T: PartialEq + Copy + Debug>(
ra: Vec<(Range<usize>, T)>,
rb: Vec<(Range<usize>, T)>,
ra: &[(Range<usize>, T)],
rb: &[(Range<usize>, T)],
) -> Vec<(Range<usize>, Range<Option<T>>)> {
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)),

View File

@ -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<hal::image::Layer, Unit<TextureUsage>>;
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<TextureState> {
/// Produce the gfx-hal image states corresponding to the transition.
pub fn to_states(&self) -> Range<hal::image::State> {
conv::map_texture_state(self.usage.start, self.selector.aspects)
.. conv::map_texture_state(self.usage.end, self.selector.aspects)
}
fn collapse(self) -> Result<TextureUsage, Self> {
if self.usage.start.is_empty()
|| self.usage.start == self.usage.end
@ -38,47 +33,49 @@ impl PendingTransition<TextureState> {
}
}
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<Self::Usage> {
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<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
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<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
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,

View File

@ -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 {