Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation for various things. #2566

Merged
merged 1 commit into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions wgpu-core/src/device/life.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,24 @@ impl<A: hal::Api> LifetimeTracker<A> {
/// Sort out the consequences of completed submissions.
///
/// Assume that all submissions up through `last_done` have completed.
/// Buffers they used are now ready to map. Resources for which they were
/// the final use are now ready to free.
///
/// Return a list of `SubmittedWorkDoneClosure`s to run.
/// - Buffers used by those submissions are now ready to map, if
/// requested. Add any buffers in the submission's [`mapped`] list to
/// [`self.ready_to_map`], where [`LifetimeTracker::handle_mapping`] will find
/// them.
///
/// - Resources whose final use was in those submissions are now ready to
/// free. Add any resources in the submission's [`last_resources`] table
/// to [`self.free_resources`], where [`LifetimeTracker::cleanup`] will find
/// them.
///
/// Return a list of [`SubmittedWorkDoneClosure`]s to run.
///
/// [`mapped`]: ActiveSubmission::mapped
/// [`self.ready_to_map`]: LifetimeTracker::ready_to_map
/// [`last_resources`]: ActiveSubmission::last_resources
/// [`self.free_resources`]: LifetimeTracker::free_resources
/// [`SubmittedWorkDoneClosure`]: crate::device::queue::SubmittedWorkDoneClosure
#[must_use]
pub fn triage_submissions(
&mut self,
Expand Down Expand Up @@ -430,6 +444,45 @@ impl<A: hal::Api> LifetimeTracker<A> {
}

impl<A: HalApi> LifetimeTracker<A> {
/// Identify resources to free, according to `trackers` and `self.suspected_resources`.
///
/// Given `trackers`, the [`TrackerSet`] belonging to same [`Device`] as
/// `self`, and `hub`, the [`Hub`] to which that `Device` belongs:
///
/// Remove from `trackers` each resource mentioned in
/// [`self.suspected_resources`]. If `trackers` held the final reference to
/// that resource, add it to the appropriate free list, to be destroyed by
/// the hal:
///
/// - Add resources used by queue submissions still in flight to the
/// [`last_resources`] table of the last such submission's entry in
/// [`self.active`]. When that submission has finished execution. the
/// [`triage_submissions`] method will move them to
/// [`self.free_resources`].
///
/// - Add resources that can be freed right now to [`self.free_resources`]
/// directly. [`LifetimeTracker::cleanup`] will take care of them as
/// part of this poll.
///
/// ## Entrained resources
///
/// This function finds resources that are used only by other resources
/// ready to be freed, and adds those to the free lists as well. For
/// example, if there's some texture `T` used only by some texture view
/// `TV`, then if `TV` can be freed, `T` gets added to the free lists too.
///
/// Since `wgpu-core` resource ownership patterns are acyclic, we can visit
/// each type that can be owned after all types that could possibly own
/// it. This way, we can detect all free-able objects in a single pass,
/// simply by starting with types that are roots of the ownership DAG (like
/// render bundles) and working our way towards leaf types (like buffers).
///
/// [`Device`]: super::Device
/// [`self.suspected_resources`]: LifetimeTracker::suspected_resources
/// [`last_resources`]: ActiveSubmission::last_resources
/// [`self.active`]: LifetimeTracker::active
/// [`triage_submissions`]: LifetimeTracker::triage_submissions
/// [`self.free_resources`]: LifetimeTracker::free_resources
pub(super) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
&mut self,
hub: &Hub<A, G>,
Expand Down
3 changes: 3 additions & 0 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ pub struct Device<A: hal::Api> {
command_allocator: Mutex<CommandAllocator<A>>,
pub(crate) active_submission_index: SubmissionIndex,
fence: A::Fence,

/// All live resources allocated with this [`Device`].
///
/// Has to be locked temporarily only (locked last)
pub(crate) trackers: Mutex<TrackerSet>,
// Life tracker should be locked right after the device and before anything else.
Expand Down
20 changes: 17 additions & 3 deletions wgpu-core/src/hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ use std::{fmt::Debug, marker::PhantomData, mem, ops};
/// Use `IdentityManager::default` to construct new instances.
///
/// `IdentityManager` returns `Id`s whose index values are suitable for use as
/// indices into a vector of information (which you maintain elsewhere) about
/// those ids' referents:
/// indices into a `Storage<T>` that holds those ids' referents:
///
/// - Every live id has a distinct index value. Each live id's index selects a
/// distinct element in the vector.
Expand Down Expand Up @@ -88,10 +87,20 @@ impl IdentityManager {
}
}

/// An entry in a `Storage::map` table.
#[derive(Debug)]
enum Element<T> {
/// There are no live ids with this index.
Vacant,

/// There is one live id with this index, allocated at the given
/// epoch.
Occupied(T, Epoch),

/// Like `Occupied`, but an error occurred when creating the
/// resource.
///
/// The given `String` is the resource's descriptor label.
Error(Epoch, String),
}

Expand All @@ -112,6 +121,11 @@ impl StorageReport {
#[derive(Clone, Debug)]
pub(crate) struct InvalidId;

/// A table of `T` values indexed by the id type `I`.
///
/// The table is represented as a vector indexed by the ids' index
/// values, so you should use an id allocator like `IdentityManager`
/// that keeps the index values dense and close to zero.
#[derive(Debug)]
pub struct Storage<T, I: id::TypedId> {
map: Vec<Element<T>>,
Expand Down Expand Up @@ -269,7 +283,7 @@ impl<T, I: id::TypedId> Storage<T, I> {
/// If type A implements `Access<B>`, that means we are allowed to proceed
/// with locking resource `B` after we lock `A`.
///
/// The implenentations basically describe the edges in a directed graph
/// The implementations basically describe the edges in a directed graph
/// of lock transitions. As long as it doesn't have loops, we can have
/// multiple concurrent paths on this graph (from multiple threads) without
/// deadlocks, i.e. there is always a path whose next resource is not locked
Expand Down
38 changes: 32 additions & 6 deletions wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,17 @@ impl<S: ResourceState> ResourceTracker<S> {
}
}

/// Remove the resource from the tracker if it is holding the last reference.
/// Remove the resource `id`, only if `self` is holding the last reference to it.
///
/// Return `true` if we did remove the resource.
/// Return `true` if we did remove the resource; the underlying hal resource
/// is ready to be freed.
///
/// This is generally only meaningful to apply to members of
/// [`Device::trackers`], which holds all resources allocated with that
/// [`Device`]. Other trackers should never be the final reference.
///
/// [`Device`]: crate::device::Device
/// [`Device::trackers`]: crate::device::Device::trackers
pub(crate) fn remove_abandoned(&mut self, id: Valid<S::Id>) -> bool {
let (index, epoch, backend) = id.0.unzip();
debug_assert_eq!(backend, self.backend);
Expand Down Expand Up @@ -287,7 +295,9 @@ impl<S: ResourceState> ResourceTracker<S> {
self.map.clear();
}

/// Initialize a resource to be used.
/// Begin tracking a new resource `id` in state `state`.
///
/// Hold `ref_count` in the tracker.
///
/// Returns false if the resource is already registered.
pub(crate) fn init(
Expand Down Expand Up @@ -324,8 +334,18 @@ impl<S: ResourceState> ResourceTracker<S> {
res.state.query(selector)
}

/// Make sure that a resource is tracked, and return a mutable
/// reference to it.
/// Make sure that a resource is tracked, and return a mutable reference to it.
///
/// If the resource isn't tracked, start it in the default state, and take a
/// clone of `ref_count`.
///
/// The `self_backend` and `map` arguments should be the `backend` and `map`
/// fields of a `ResourceTracker`. Ideally this function would just take
/// `&mut self` and access those from there, but that would upset the borrow
/// checker in some callers, who want to borrow `ResourceTracker::temp`
/// alongside our return value. The approach taken here has the caller
/// borrow both `map` and `temp`, so the borrow checker can see that they
/// don't alias.
fn get_or_insert<'a>(
self_backend: wgt::Backend,
map: &'a mut FastHashMap<Index, Resource<S>>,
Expand All @@ -347,6 +367,7 @@ impl<S: ResourceState> ResourceTracker<S> {
}
}

/// Return a mutable reference to `id`'s state.
fn get<'a>(
self_backend: wgt::Backend,
map: &'a mut FastHashMap<Index, Resource<S>>,
Expand All @@ -359,7 +380,7 @@ impl<S: ResourceState> ResourceTracker<S> {
e
}

/// Extend the usage of a specified resource.
/// Extend the usage of `id`, tracking it if necessary.
///
/// Returns conflicting transition as an error.
pub(crate) fn change_extend(
Expand Down Expand Up @@ -553,6 +574,11 @@ pub enum UsageConflict {
}

/// A set of trackers for all relevant resources.
///
/// `Device` uses this to track all resources allocated from that device.
/// Resources like `BindGroup`, `CommandBuffer`, and so on that may own a
/// variety of other resources also use a value of this type to keep track of
/// everything they're depending on.
#[derive(Debug)]
pub(crate) struct TrackerSet {
pub buffers: ResourceTracker<BufferState>,
Expand Down