//! Buffer Trackers //! //! Buffers are represented by a single state for the whole resource, //! a 16 bit bitflag of buffer usages. Because there is only ever //! one subresource, they have no selector.
/// Optimize the buffer bind group state by sorting it by ID. /// /// When this list of states is merged into a tracker, the memory /// accesses will be in a constant ascending order. pub(crate) fn optimize(&mutself) { self.buffers
.sort_unstable_by_key(|(b, _)| b.tracker_index());
}
/// Returns a list of all buffers tracked. May contain duplicates. pubfn used_tracker_indices(&self) -> impl Iterator<Item = TrackerIndex> + '_ { self.buffers
.iter()
.map(|(b, _)| b.tracker_index())
.collect::<Vec<_>>()
.into_iter()
}
/// Adds the given resource with the given state. pubfn insert_single(&mutself, buffer: Arc<Buffer>, state: BufferUses) { self.buffers.push((buffer, state));
}
}
/// Stores all buffer state within a single usage scope. #[derive(Debug)] pub(crate) struct BufferUsageScope {
state: Vec<BufferUses>,
metadata: ResourceMetadata<Arc<Buffer>>,
}
/// Sets the size of all the vectors inside the tracker. /// /// Must be called with the highest possible Buffer ID before /// all unsafe functions are called. pubfn set_size(&mutself, size: usize) { self.state.resize(size, BufferUses::empty()); self.metadata.set_size(size);
}
/// Extend the vectors to let the given index be valid. fn allow_index(&mutself, index: usize) { if index >= self.state.len() { self.set_size(index + 1);
}
}
/// Merge the list of buffer states in the given bind group into this usage scope. /// /// If any of the resulting states is invalid, stops the merge and returns a usage /// conflict with the details of the invalid state. /// /// Because bind groups do not check if the union of all their states is valid, /// this method is allowed to return Err on the first bind group bound. /// /// # Safety /// /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this /// method is called. pubunsafefn merge_bind_group(
&mutself,
bind_group: &BufferBindGroupState,
) -> Result<(), ResourceUsageCompatibilityError> { for &(ref resource, state) in bind_group.buffers.iter() { let index = resource.tracker_index().as_usize();
unsafe { self.insert_or_merge(
index as _,
index,
BufferStateProvider::Direct { state },
ResourceMetadataProvider::Direct { resource },
)?
};
}
Ok(())
}
/// Merge the list of buffer states in the given usage scope into this UsageScope. /// /// If any of the resulting states is invalid, stops the merge and returns a usage /// conflict with the details of the invalid state. /// /// If the given tracker uses IDs higher than the length of internal vectors, /// the vectors will be extended. A call to set_size is not needed. pubfn merge_usage_scope(
&mutself,
scope: &Self,
) -> Result<(), ResourceUsageCompatibilityError> { let incoming_size = scope.state.len(); if incoming_size > self.state.len() { self.set_size(incoming_size);
}
for index in scope.metadata.owned_indices() { self.tracker_assert_in_bounds(index);
scope.tracker_assert_in_bounds(index);
unsafe { self.insert_or_merge(
index as u32,
index,
BufferStateProvider::Indirect {
state: &scope.state,
},
ResourceMetadataProvider::Indirect {
metadata: &scope.metadata,
},
)?;
};
}
Ok(())
}
/// Merge a single state into the UsageScope. /// /// If the resulting state is invalid, returns a usage /// conflict with the details of the invalid state. /// /// If the ID is higher than the length of internal vectors, /// the vectors will be extended. A call to set_size is not needed. pubfn merge_single(
&mutself,
buffer: &Arc<Buffer>,
new_state: BufferUses,
) -> Result<(), ResourceUsageCompatibilityError> { let index = buffer.tracker_index().as_usize();
self.allow_index(index);
self.tracker_assert_in_bounds(index);
unsafe { self.insert_or_merge(
index as _,
index,
BufferStateProvider::Direct { state: new_state },
ResourceMetadataProvider::Direct { resource: buffer },
)?;
}
Ok(())
}
/// Does an insertion operation if the index isn't tracked /// in the current metadata, otherwise merges the given state /// with the current state. If the merging would cause /// a conflict, returns that usage conflict. /// /// # Safety /// /// Indexes must be valid indexes into all arrays passed in /// to this function, either directly or via metadata or provider structs. #[inline(always)] unsafefn insert_or_merge(
&mutself,
index32: u32,
index: usize,
state_provider: BufferStateProvider<'_>,
metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,
) -> Result<(), ResourceUsageCompatibilityError> { let currently_owned = unsafe { self.metadata.contains_unchecked(index) };
/// Sets the size of all the vectors inside the tracker. /// /// Must be called with the highest possible Buffer ID before /// all unsafe functions are called. pubfn set_size(&mutself, size: usize) { self.start.resize(size, BufferUses::empty()); self.end.resize(size, BufferUses::empty());
self.metadata.set_size(size);
}
/// Extend the vectors to let the given index be valid. fn allow_index(&mutself, index: usize) { if index >= self.start.len() { self.set_size(index + 1);
}
}
/// Returns true if the given buffer is tracked. pubfn contains(&self, buffer: &Buffer) -> bool { self.metadata.contains(buffer.tracker_index().as_usize())
}
/// Returns a list of all buffers tracked. pubfn used_resources(&self) -> impl Iterator<Item = &Arc<Buffer>> + '_ { self.metadata.owned_resources()
}
/// Drains all currently pending transitions. pubfn drain_transitions<'a, 'b: 'a>(
&'b mut self,
snatch_guard: &'a SnatchGuard<'a>,
) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> { let buffer_barriers = self.temp.drain(..).map(|pending| { let buf = unsafe { self.metadata.get_resource_unchecked(pending.id as _) };
pending.into_hal(buf, snatch_guard)
});
buffer_barriers
}
/// Sets the state of a single buffer. /// /// If a transition is needed to get the buffer into the given state, that transition /// is returned. No more than one transition is needed. /// /// If the ID is higher than the length of internal vectors, /// the vectors will be extended. A call to set_size is not needed. pubfn set_single(
&mutself,
buffer: &Arc<Buffer>,
state: BufferUses,
) -> Option<PendingTransition<BufferUses>> { let index: usize = buffer.tracker_index().as_usize();
/// Sets the given state for all buffers in the given tracker. /// /// If a transition is needed to get the buffers into the needed state, /// those transitions are stored within the tracker. A subsequent /// call to [`Self::drain_transitions`] is needed to get those transitions. /// /// If the ID is higher than the length of internal vectors, /// the vectors will be extended. A call to set_size is not needed. pubfn set_from_tracker(&mutself, tracker: &Self) { let incoming_size = tracker.start.len(); if incoming_size > self.start.len() { self.set_size(incoming_size);
}
for index in tracker.metadata.owned_indices() { self.tracker_assert_in_bounds(index);
tracker.tracker_assert_in_bounds(index); unsafe { self.insert_or_barrier_update(
index,
BufferStateProvider::Indirect {
state: &tracker.start,
},
Some(BufferStateProvider::Indirect {
state: &tracker.end,
}),
ResourceMetadataProvider::Indirect {
metadata: &tracker.metadata,
},
)
}
}
}
/// Sets the given state for all buffers in the given UsageScope. /// /// If a transition is needed to get the buffers into the needed state, /// those transitions are stored within the tracker. A subsequent /// call to [`Self::drain_transitions`] is needed to get those transitions. /// /// If the ID is higher than the length of internal vectors, /// the vectors will be extended. A call to set_size is not needed. pubfn set_from_usage_scope(&mutself, scope: &BufferUsageScope) { let incoming_size = scope.state.len(); if incoming_size > self.start.len() { self.set_size(incoming_size);
}
for index in scope.metadata.owned_indices() { self.tracker_assert_in_bounds(index);
scope.tracker_assert_in_bounds(index); unsafe { self.insert_or_barrier_update(
index,
BufferStateProvider::Indirect {
state: &scope.state,
},
None,
ResourceMetadataProvider::Indirect {
metadata: &scope.metadata,
},
)
}
}
}
/// Iterates through all buffers in the given bind group and adopts /// the state given for those buffers in the UsageScope. It also /// removes all touched buffers from the usage scope. /// /// If a transition is needed to get the buffers into the needed state, /// those transitions are stored within the tracker. A subsequent /// call to [`Self::drain_transitions`] is needed to get those transitions. /// /// This is a really funky method used by Compute Passes to generate /// barriers after a call to dispatch without needing to iterate /// over all elements in the usage scope. We use each the /// a given iterator of ids as a source of which IDs to look at. /// All the IDs must have first been added to the usage scope. /// /// # Safety /// /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this /// method is called. pubunsafefn set_and_remove_from_usage_scope_sparse(
&mutself,
scope: &mut BufferUsageScope,
index_source: impl IntoIterator<Item = TrackerIndex>,
) { let incoming_size = scope.state.len(); if incoming_size > self.start.len() { self.set_size(incoming_size);
}
for index in index_source { let index = index.as_usize();
/// If the resource isn't tracked /// - Inserts the given resource. /// - Uses the `start_state_provider` to populate `start_states` /// - Uses either `end_state_provider` or `start_state_provider` /// to populate `current_states`. /// /// If the resource is tracked /// - Inserts barriers from the state in `current_states` /// to the state provided by `start_state_provider`. /// - Updates the `current_states` with either the state from /// `end_state_provider` or `start_state_provider`. /// /// Any barriers are added to the barrier vector. /// /// # Safety /// /// Indexes must be valid indexes into all arrays passed in /// to this function, either directly or via metadata or provider structs. #[inline(always)] unsafefn insert_or_barrier_update(
&mutself,
index: usize,
start_state_provider: BufferStateProvider<'_>,
end_state_provider: Option<BufferStateProvider<'_>>,
metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,
) { let currently_owned = unsafe { self.metadata.contains_unchecked(index) };
/// Stores all buffer state within a device. pub(crate) struct DeviceBufferTracker {
current_states: Vec<BufferUses>,
metadata: ResourceMetadata<Weak<Buffer>>,
temp: Vec<PendingTransition<BufferUses>>,
}
/// Extend the vectors to let the given index be valid. fn allow_index(&mutself, index: usize) { if index >= self.current_states.len() { self.current_states.resize(index + 1, BufferUses::empty()); self.metadata.set_size(index + 1);
}
}
/// Returns a list of all buffers tracked. pubfn used_resources(&self) -> impl Iterator<Item = &Weak<Buffer>> + '_ { self.metadata.owned_resources()
}
/// Inserts a single buffer and its state into the resource tracker. /// /// If the resource already exists in the tracker, it will be overwritten. pubfn insert_single(&mutself, buffer: &Arc<Buffer>, state: BufferUses) { let index = buffer.tracker_index().as_usize();
/// Sets the state of a single buffer. /// /// If a transition is needed to get the buffer into the given state, that transition /// is returned. No more than one transition is needed. pubfn set_single(
&mutself,
buffer: &Arc<Buffer>,
state: BufferUses,
) -> Option<PendingTransition<BufferUses>> { let index: usize = buffer.tracker_index().as_usize();
self.tracker_assert_in_bounds(index);
let start_state_provider = BufferStateProvider::Direct { state };
/// Sets the given state for all buffers in the given tracker. /// /// If a transition is needed to get the buffers into the needed state, /// those transitions are returned. pubfn set_from_tracker_and_drain_transitions<'a, 'b: 'a>(
&'a mut self,
tracker: &'a BufferTracker,
snatch_guard: &'b SnatchGuard<'b>,
) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> { for index in tracker.metadata.owned_indices() { self.tracker_assert_in_bounds(index);
self.temp.drain(..).map(|pending| { let buf = unsafe { tracker.metadata.get_resource_unchecked(pending.id as _) };
pending.into_hal(buf, snatch_guard)
})
}
}
/// Source of Buffer State. #[derive(Debug, Clone)] enum BufferStateProvider<'a> { /// Get a state that was provided directly.
Direct { state: BufferUses }, /// Get a state from an an array of states.
Indirect { state: &'a [BufferUses] },
} impl BufferStateProvider<'_> { /// Gets the state from the provider, given a resource ID index. /// /// # Safety /// /// Index must be in bounds for the indirect source iff this is in the indirect state. #[inline(always)] unsafefn get_state(&self, index: usize) -> BufferUses { match *self {
BufferStateProvider::Direct { state } => state,
BufferStateProvider::Indirect { state } => {
strict_assert!(index < state.len());
*unsafe { state.get_unchecked(index) }
}
}
}
}
// This should only ever happen with a wgpu bug, but let's just double // check that resource states don't have any conflicts.
strict_assert_eq!(invalid_resource_state(new_start_state), false);
strict_assert_eq!(invalid_resource_state(new_end_state), false);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.