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

Pool tracker vecs #5414

Merged
merged 10 commits into from
Mar 23, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Bottom level categories:
- Fix linking when targeting android. By @ashdnazg in [#5326](https://github.com/gfx-rs/wgpu/pull/5326).
- fix resource leak for buffers/textures dropped while having pending writes. By @robtfm in [#5413](https://github.com/gfx-rs/wgpu/pull/5413)
- Failing to set the device lost closure will call the closure before returning. By @bradwerth in [#5358](https://github.com/gfx-rs/wgpu/pull/5358).
- Use memory pooling for UsageScopes to avoid frequent large allocations. by @robtfm in [#5414](https://github.com/gfx-rs/wgpu/pull/5414)

#### Naga
- In spv-in, remove unnecessary "gl_PerVertex" name check so unused builtins will always be skipped. By @Imberflur in [#5227](https://github.com/gfx-rs/wgpu/pull/5227).
Expand Down
8 changes: 4 additions & 4 deletions wgpu-core/src/command/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,14 +272,14 @@ where
}
}

struct State<A: HalApi> {
struct State<'a, A: HalApi> {
binder: Binder<A>,
pipeline: Option<id::ComputePipelineId>,
scope: UsageScope<A>,
scope: UsageScope<'a, A>,
debug_scope_depth: u32,
}

impl<A: HalApi> State<A> {
impl<'a, A: HalApi> State<'a, A> {
fn is_ready(&self) -> Result<(), DispatchError> {
let bind_mask = self.binder.invalid_mask();
if bind_mask != 0 {
Expand Down Expand Up @@ -407,7 +407,7 @@ impl Global {
let mut state = State {
binder: Binder::new(),
pipeline: None,
scope: UsageScope::new(&device.tracker_indices),
scope: device.new_usage_scope(),
debug_scope_depth: 0,
};
let mut temp_offsets = Vec::new();
Expand Down
12 changes: 6 additions & 6 deletions wgpu-core/src/command/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,9 +739,9 @@ impl<A: HalApi> TextureView<A> {
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;

struct RenderPassInfo<'a, A: HalApi> {
struct RenderPassInfo<'a, 'd, A: HalApi> {
context: RenderPassContext,
usage_scope: UsageScope<A>,
usage_scope: UsageScope<'d, A>,
/// All render attachments, including depth/stencil
render_attachments: AttachmentDataVec<RenderAttachment<'a, A>>,
is_depth_read_only: bool,
Expand All @@ -754,7 +754,7 @@ struct RenderPassInfo<'a, A: HalApi> {
multiview: Option<NonZeroU32>,
}

impl<'a, A: HalApi> RenderPassInfo<'a, A> {
impl<'a, 'd, A: HalApi> RenderPassInfo<'a, 'd, A> {
fn add_pass_texture_init_actions<V>(
channel: &PassChannel<V>,
texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
Expand Down Expand Up @@ -790,7 +790,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
}

fn start(
device: &Device<A>,
device: &'d Device<A>,
label: Option<&str>,
color_attachments: &[Option<RenderPassColorAttachment>],
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
Expand Down Expand Up @@ -1214,7 +1214,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {

Ok(Self {
context,
usage_scope: UsageScope::new(&device.tracker_indices),
usage_scope: device.new_usage_scope(),
render_attachments,
is_depth_read_only,
is_stencil_read_only,
Expand All @@ -1230,7 +1230,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
mut self,
raw: &mut A::CommandEncoder,
snatch_guard: &SnatchGuard,
) -> Result<(UsageScope<A>, SurfacesInDiscardState<A>), RenderPassErrorInner> {
) -> Result<(UsageScope<'d, A>, SurfacesInDiscardState<A>), RenderPassErrorInner> {
profiling::scope!("RenderPassInfo::finish");
unsafe {
raw.end_render_pass();
Expand Down
4 changes: 2 additions & 2 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ impl Global {
+ 1;
let mut active_executions = Vec::new();

let mut used_surface_textures = track::TextureUsageScope::new();
let mut used_surface_textures = track::TextureUsageScope::default();

let snatch_guard = device.snatchable_lock.read();

Expand Down Expand Up @@ -1435,7 +1435,7 @@ impl Global {
baked.encoder.end_encoding().unwrap()
};
baked.list.push(present);
used_surface_textures = track::TextureUsageScope::new();
used_surface_textures = track::TextureUsageScope::default();
}

// done
Expand Down
11 changes: 10 additions & 1 deletion wgpu-core/src/device/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ use crate::{
resource_log,
snatch::{SnatchGuard, SnatchLock, Snatchable},
storage::Storage,
track::{BindGroupStates, TextureSelector, Tracker, TrackerIndexAllocators},
track::{
BindGroupStates, TextureSelector, Tracker, TrackerIndexAllocators, UsageScope,
UsageScopePool,
},
validation::{
self, check_buffer_usage, check_texture_usage, validate_color_attachment_bytes_per_sample,
},
Expand Down Expand Up @@ -135,6 +138,7 @@ pub struct Device<A: HalApi> {
pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy<A>>>,
#[cfg(feature = "trace")]
pub(crate) trace: Mutex<Option<trace::Trace>>,
pub(crate) usage_scopes: UsageScopePool<A>,
}

pub(crate) enum DeferredDestroy<A: HalApi> {
Expand Down Expand Up @@ -296,6 +300,7 @@ impl<A: HalApi> Device<A> {
instance_flags,
pending_writes: Mutex::new(Some(pending_writes)),
deferred_destroy: Mutex::new(Vec::new()),
usage_scopes: Default::default(),
})
}

Expand Down Expand Up @@ -3568,6 +3573,10 @@ impl<A: HalApi> Device<A> {
let _ = texture.destroy();
}
}

pub(crate) fn new_usage_scope(&self) -> UsageScope<'_, A> {
UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
}
}

impl<A: HalApi> Device<A> {
Expand Down
12 changes: 8 additions & 4 deletions wgpu-core/src/track/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,27 @@ impl<A: HalApi> BufferBindGroupState<A> {
#[derive(Debug)]
pub(crate) struct BufferUsageScope<A: HalApi> {
state: Vec<BufferUses>,

metadata: ResourceMetadata<Buffer<A>>,
}

impl<A: HalApi> BufferUsageScope<A> {
pub fn new() -> Self {
impl<A: HalApi> Default for BufferUsageScope<A> {
fn default() -> Self {
Self {
state: Vec::new(),

metadata: ResourceMetadata::new(),
}
}
}

impl<A: HalApi> BufferUsageScope<A> {
fn tracker_assert_in_bounds(&self, index: usize) {
strict_assert!(index < self.state.len());
self.metadata.tracker_assert_in_bounds(index);
}
pub fn clear(&mut self) {
self.state.clear();
self.metadata.clear();
}

/// Sets the size of all the vectors inside the tracker.
///
Expand Down
5 changes: 5 additions & 0 deletions wgpu-core/src/track/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ impl<T: Resource> ResourceMetadata<T> {
resize_bitvec(&mut self.owned, size);
}

pub(super) fn clear(&mut self) {
self.resources.clear();
self.owned.clear();
}

/// Ensures a given index is in bounds for all arrays and does
/// sanity checks of the presence of a refcount.
///
Expand Down
50 changes: 37 additions & 13 deletions wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,8 @@ impl<A: HalApi> RenderBundleScope<A> {
/// Create the render bundle scope and pull the maximum IDs from the hubs.
pub fn new() -> Self {
Self {
buffers: RwLock::new(BufferUsageScope::new()),
textures: RwLock::new(TextureUsageScope::new()),
buffers: RwLock::new(BufferUsageScope::default()),
textures: RwLock::new(TextureUsageScope::default()),
bind_groups: RwLock::new(StatelessTracker::new()),
render_pipelines: RwLock::new(StatelessTracker::new()),
query_sets: RwLock::new(StatelessTracker::new()),
Expand Down Expand Up @@ -512,28 +512,52 @@ impl<A: HalApi> RenderBundleScope<A> {
}
}

/// A pool for storing the memory used by [`UsageScope`]s. We take and store this memory when the
/// scope is dropped to avoid reallocating. The memory required only grows and allocation cost is
/// significant when a large number of resources have been used.
pub(crate) type UsageScopePool<A> = Mutex<Vec<(BufferUsageScope<A>, TextureUsageScope<A>)>>;

/// A usage scope tracker. Only needs to store stateful resources as stateless
/// resources cannot possibly have a usage conflict.
#[derive(Debug)]
pub(crate) struct UsageScope<A: HalApi> {
pub(crate) struct UsageScope<'a, A: HalApi> {
pub pool: &'a UsageScopePool<A>,
pub buffers: BufferUsageScope<A>,
pub textures: TextureUsageScope<A>,
}

impl<A: HalApi> UsageScope<A> {
/// Create the render bundle scope and pull the maximum IDs from the hubs.
pub fn new(tracker_indices: &TrackerIndexAllocators) -> Self {
let mut value = Self {
buffers: BufferUsageScope::new(),
textures: TextureUsageScope::new(),
};
impl<'a, A: HalApi> Drop for UsageScope<'a, A> {
fn drop(&mut self) {
// clear vecs and push into pool
self.buffers.clear();
self.textures.clear();
self.pool.lock().push((
std::mem::take(&mut self.buffers),
std::mem::take(&mut self.textures),
));
}
}

value.buffers.set_size(tracker_indices.buffers.size());
value.textures.set_size(tracker_indices.textures.size());
impl<A: HalApi> UsageScope<'static, A> {
pub fn new_pooled<'d>(
pool: &'d UsageScopePool<A>,
tracker_indices: &TrackerIndexAllocators,
) -> UsageScope<'d, A> {
let pooled = pool.lock().pop().unwrap_or_default();

let mut scope = UsageScope::<'d, A> {
pool,
buffers: pooled.0,
textures: pooled.1,
};

value
scope.buffers.set_size(tracker_indices.buffers.size());
scope.textures.set_size(tracker_indices.textures.size());
scope
}
}

impl<'a, A: HalApi> UsageScope<'a, A> {
/// Merge the inner contents of a bind group into the usage scope.
///
/// Only stateful things are merged in here, all other resources are owned
Expand Down
13 changes: 10 additions & 3 deletions wgpu-core/src/track/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub(crate) struct TextureStateSet {
simple: Vec<TextureUses>,
complex: FastHashMap<usize, ComplexTextureState>,
}

impl TextureStateSet {
fn new() -> Self {
Self {
Expand All @@ -235,15 +236,16 @@ pub(crate) struct TextureUsageScope<A: HalApi> {
metadata: ResourceMetadata<Texture<A>>,
}

impl<A: HalApi> TextureUsageScope<A> {
pub fn new() -> Self {
impl<A: HalApi> Default for TextureUsageScope<A> {
fn default() -> Self {
Self {
set: TextureStateSet::new(),

metadata: ResourceMetadata::new(),
}
}
}

impl<A: HalApi> TextureUsageScope<A> {
fn tracker_assert_in_bounds(&self, index: usize) {
self.metadata.tracker_assert_in_bounds(index);

Expand All @@ -258,6 +260,11 @@ impl<A: HalApi> TextureUsageScope<A> {
});
}

pub fn clear(&mut self) {
self.set.clear();
self.metadata.clear();
}

/// Sets the size of all the vectors inside the tracker.
///
/// Must be called with the highest possible Texture ID before
Expand Down
Loading