Skip to content

Commit

Permalink
temp28 - document tracker and usages
Browse files Browse the repository at this point in the history
  • Loading branch information
cwfitzgerald committed May 17, 2022
1 parent aa85535 commit 6519241
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 206 deletions.
85 changes: 85 additions & 0 deletions wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,88 @@
/*! Resource State and Lifetime Trackers
*
* These structures are responsible for keeping track of resource state,
* generating barriers where needed, and making sure resources are kept
* alive until the trackers die.
*
* ## General Architecture
*
* Tracking is some of the hottest code in the entire codebase, so the trackers
* are designed to be as cache efficient as possible. They store resource state
* in flat vectors, storing metadata SOA style, one vector per type of metadata.
*
* In wgpu, resource IDs are allocated and re-used, so will always be as low
* as reasonably possible. This allows us to use the ID as an index into an array.
*
* ## Statefulness
*
* There are two main types of trackers, stateful and stateless.
*
* Stateful trackers are for buffers and textures. They both have
* resource state attached to them which needs to be used to generate
* automatic synchronization. Because of the different requirements of
* buffers and textures, they have two separate tracking structures.
*
* Stateless trackers only store metadata and own the given resource.
*
* ## Use Case
*
* Within each type of tracker, the trackers are further split into 3 different
* use cases, Bind Group, Usage Scope, and a full Tracker.
*
* Bind Group trackers are just a list of different resources, their refcount,
* and how they are used. Textures are used via a selector and a usage type.
* Buffers by just a usage type. Stateless resources don't have a usage type.
*
* Usage Scope trackers are only for stateful resources. These trackers represent
* a single [`UsageScope`] in the spec. When a use is added to a usage scope,
* it is merged with all other uses of that resource in that scope. If there
* is a usage conflict, merging will fail and an error will be reported.
*
* Full trackers represent a before and after state of a resource. These
* are used for tracking on the device and on command buffers. The before
* state represents the state the resource is first used as in the command buffer,
* the after state is the state the command buffer leaves the resource in.
* These double ended buffers can then be used to generate the needed transitions
* between command buffers.
*
* ## Dense Datastructure with Sparse Data
*
* This tracking system is based on having completely dense data, but trackers do
* not always contain every resource. Some resources (or even most resources) go
* unused in any given command buffer. So to help speed up the process of iterating
* through possibly thousands of resources, we use a bit vector to represent if
* a resource is in the buffer or not. This allows us extremely efficient memory
* utilization, as well as being able to bail out of whole blocks of 32-64 resources
* with a single usize comparison with zero. In practice this means that merging
* partially resident buffers is extremely quick.
*
* The main advantage of this dense datastructure is that we can do merging
* of trackers in an extremely efficient fashion that results in us doing linear
* scans down a couple of buffers. CPUs and their caches absolutely eat this up.
*
* ## Stateful Resource Operations
*
* All operations on stateful trackers boil down to one of four operations:
* - `insert(tracker, new_state)` adds a resource with a given state to the tracker
* for the first time.
* - `merge(tracker, new_state)` merges this new state with the previous state, checking
* for usage conflicts.
* - `barrier(tracker, new_state)` compares the given state to the existing state and
* generates the needed barriers.
* - `update(tracker, new_state)` takes the given new state and overrides the old state.
*
* This allows us to compose the operations to form the various kinds of tracker merges
* that need to happen in the codebase. For each resource in the given merger, the following
* operation applies:
*
* UsageScope <- Resource = insert(scope, usage) OR merge(scope, usage)
* UsageScope <- UsageScope = insert(scope, scope) OR merge(scope, scope)
* CommandBuffer <- UsageScope = insert(buffer.start, buffer.end, scope) OR barrier(buffer.end, scope) + update(buffer.end, scope)
* Deivce <- CommandBuffer = insert(device.start, device.end, buffer.start, buffer.end) OR barrier(device.end, buffer.start) + update(device.end, buffer.end)
*
* [`UsageScope`]: https://gpuweb.github.io/gpuweb/#programming-model-synchronization
!*/

mod buffer;
mod range;
mod stateless;
Expand Down
185 changes: 0 additions & 185 deletions wgpu-core/src/track/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1225,188 +1225,3 @@ unsafe fn update(
}
}
}

// #[cfg(test)]
// mod test {
// use super::*;

// struct TrackingTest {
// life_guard: LifeGuard,
// selector: TextureSelector,
// set: TextureStateSet,
// metadata: ResourceMetadata<hal::api::Empty>,
// }

// fn setup(mips: Range<u32>, layers: Range<u32>, states: &[TextureUses]) -> TrackingTest {
// let life_guard = LifeGuard::new("test");
// let selector = TextureSelector { mips, layers };

// let mut set = TextureStateSet::new();
// let mut metadata = ResourceMetadata::<hal::api::Empty>::new();

// set.set_size(states.len());
// metadata.set_size(states.len());

// for &state in states {
// set.simple[0] = state;

// metadata.owned.set(0, true);
// metadata.ref_counts[0] = None; // Okay because we only read this through metadata provider
// metadata.epochs[0] = 1;
// }

// TrackingTest {
// life_guard,
// selector,
// set,
// metadata,
// }
// }

// #[test]
// fn simple_transition() {
// let mut test = setup(0..1, 0..1, &[TextureUses::UNINITIALIZED]);

// let mut barriers = Vec::new();
// unsafe {
// let _ = state_combine(
// Some((&test.life_guard, &test.selector)),
// None,
// &mut test.set,
// &mut test.metadata,
// 0,
// 0,
// LayeredStateProvider::KnownSingle {
// state: TextureUses::RESOURCE,
// },
// ResourceMetadataProvider::Direct {
// epoch: 1,
// ref_count: Cow::Borrowed(test.life_guard.ref_count.as_ref().unwrap()),
// },
// Some(&mut barriers),
// );
// };
// assert_eq!(test.set.simple, &[TextureUses::RESOURCE]);
// assert_eq!(
// barriers,
// &[PendingTransition {
// id: 0,
// selector: test.selector,
// usage: TextureUses::UNINITIALIZED..TextureUses::RESOURCE
// }]
// );
// }

// #[test]
// fn simple_merger() {
// let mut test = setup(0..1, 0..1, &[TextureUses::COPY_SRC]);

// unsafe {
// let res = state_combine(
// Some((&test.life_guard, &test.selector)),
// None,
// &mut test.set,
// &mut test.metadata,
// 0,
// 0,
// LayeredStateProvider::KnownSingle {
// state: TextureUses::RESOURCE,
// },
// ResourceMetadataProvider::Direct {
// epoch: 1,
// ref_count: Cow::Borrowed(test.life_guard.ref_count.as_ref().unwrap()),
// },
// None,
// );
// assert_eq!(res, Ok(()));
// };
// assert_eq!(
// test.set.simple,
// &[TextureUses::COPY_SRC | TextureUses::RESOURCE]
// );
// }

// #[test]
// fn simple_to_complex_transition() {
// let mut test = setup(0..1, 0..2, &[TextureUses::RESOURCE]);

// let transition_selector = TextureSelector {
// mips: 0..1,
// layers: 1..2,
// };

// let mut barriers = Vec::new();
// unsafe {
// let _ = state_combine(
// Some((&test.life_guard, &test.selector)),
// None,
// &mut test.set,
// &mut test.metadata,
// 0,
// 0,
// LayeredStateProvider::Selector {
// selector: transition_selector.clone(),
// state: TextureUses::STORAGE_WRITE,
// },
// ResourceMetadataProvider::Direct {
// epoch: 1,
// ref_count: Cow::Borrowed(test.life_guard.ref_count.as_ref().unwrap()),
// },
// Some(&mut barriers),
// );
// };
// assert_eq!(test.set.simple, &[TextureUses::COMPLEX]);
// assert_eq!(
// barriers,
// &[PendingTransition {
// id: 0,
// selector: transition_selector,
// usage: TextureUses::RESOURCE..TextureUses::STORAGE_WRITE
// }]
// );
// }

// #[test]
// fn simple_to_complex_merger() {
// let mut test = setup(0..1, 0..2, &[TextureUses::RESOURCE]);

// let transition_selector = TextureSelector {
// mips: 0..1,
// layers: 1..2,
// };

// unsafe {
// let res = state_combine(
// Some((&test.life_guard, &test.selector)),
// None,
// &mut test.set,
// &mut test.metadata,
// 0,
// 0,
// LayeredStateProvider::Selector {
// selector: transition_selector.clone(),
// state: TextureUses::DEPTH_STENCIL_READ,
// },
// ResourceMetadataProvider::Direct {
// epoch: 1,
// ref_count: Cow::Borrowed(test.life_guard.ref_count.as_ref().unwrap()),
// },
// None,
// );
// assert_eq!(res, Ok(()));
// };
// assert_eq!(test.set.simple, &[TextureUses::COMPLEX]);
// let complex = test.set.complex.get(&0).unwrap();
// let mip0: Vec<_> = complex.mips[0].iter().cloned().collect();
// assert_eq!(
// mip0,
// &[
// (0..1, TextureUses::RESOURCE),
// (
// 1..2,
// TextureUses::RESOURCE | TextureUses::DEPTH_STENCIL_READ
// )
// ]
// );
// }
// }
68 changes: 47 additions & 21 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,53 +627,79 @@ bitflags!(
bitflags::bitflags! {
/// Similar to `wgt::BufferUsages` but for internal use.
pub struct BufferUses: u16 {
/// The argument to a read-only mapping.
const MAP_READ = 1 << 0;
/// The argument to a write-only mapping.
const MAP_WRITE = 1 << 1;
/// The source of a hardware copy.
const COPY_SRC = 1 << 2;
/// The destination of a hardware copy.
const COPY_DST = 1 << 3;
/// The index buffer used for drawing.
const INDEX = 1 << 4;
/// A vertex buffer used for drawing.
const VERTEX = 1 << 5;
/// A uniform buffer bound in a bind group.
const UNIFORM = 1 << 6;
/// A read-only storage buffer used in a bind group.
///
/// Can be combined with STORAGE_WRITE to create a read-write usage.
const STORAGE_READ = 1 << 7;
/// A read-write or write only buffer used for binding
///
/// Can be combined with STORAGE_READ to create a read-write usage.
const STORAGE_WRITE = 1 << 8;
/// The indirect or count buffer in a indirect draw or dispatch.
const INDIRECT = 1 << 9;
/// The combination of usages that can be used together (read-only).
/// The combination of states that a buffer may be in _at the same time_.
const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits |
Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits |
Self::STORAGE_READ.bits | Self::INDIRECT.bits;
/// The combination of exclusive usages (write-only and read-write).
/// These usages may still show up with others, but can't automatically be combined.
/// The combination of states that a buffer must exclusively be in.
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits;
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization.
/// If a usage is ordered, then if the buffer state doesn't change between draw calls, there
/// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits;
}
}

bitflags::bitflags! {
/// Similar to `wgt::TextureUsages` but for internal use.
pub struct TextureUses: u16 {
const PRESENT = 1 << 0;
const COPY_SRC = 1 << 1;
const COPY_DST = 1 << 2;
const RESOURCE = 1 << 3;
const COLOR_TARGET = 1 << 4;
const DEPTH_STENCIL_READ = 1 << 5;
const DEPTH_STENCIL_WRITE = 1 << 6;
const STORAGE_READ = 1 << 7;
const STORAGE_WRITE = 1 << 8;
/// The combination of usages that can be used together (read-only).
/// The texture is in unknown state.
const UNINITIALIZED = 1 << 0;
/// Ready to present image to the surface.
const PRESENT = 1 << 1;
/// The source of a hardware copy.
const COPY_SRC = 1 << 2;
/// The destination of a hardware copy.
const COPY_DST = 1 << 3;
/// Read-only sampled or fetched resource.
const RESOURCE = 1 << 4;
/// The color target of a renderpass.
const COLOR_TARGET = 1 << 5;
/// Read-only depth stencil usage.
const DEPTH_STENCIL_READ = 1 << 6;
/// Read-write depth stencil usage
const DEPTH_STENCIL_WRITE = 1 << 7;
/// Read-only storage buffer usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only.
const STORAGE_READ = 1 << 8;
/// Read-write storage buffer usage.
const STORAGE_WRITE = 1 << 9;
/// The combination of states that a texture may be in _at the same time_.
const INCLUSIVE = Self::COPY_SRC.bits | Self::RESOURCE.bits | Self::DEPTH_STENCIL_READ.bits;
/// The combination of exclusive usages (write-only and read-write).
/// These usages may still show up with others, but can't automatically be combined.
/// The combination of states that a texture must exclusively be in.
const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_WRITE.bits | Self::PRESENT.bits;
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization.
/// If a usage is ordered, then if the texture state doesn't change between draw calls, there
/// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits;
const COMPLEX = 1 << 9;
const UNINITIALIZED = 1 << 10;

/// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource
const COMPLEX = 1 << 10;
/// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource.
/// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized.
const UNKNOWN = 1 << 11;
}
}
Expand Down

0 comments on commit 6519241

Please sign in to comment.