Skip to content

Commit

Permalink
Simplify the ID allocation in IdentityValues (gfx-rs#5229)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Fitzgerald <[email protected]>
  • Loading branch information
nical and cwfitzgerald committed Feb 29, 2024
1 parent 8bda25c commit 08f235c
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Bottom level categories:

#### General
- Device lost callbacks are invoked when replaced and when global is dropped. By @bradwerth in [#5168](https://github.com/gfx-rs/wgpu/pull/5168)
- Fix performance regression when allocating a large amount of resources of the same type. By @nical in [#5229](https://github.com/gfx-rs/wgpu/pull/5229)

#### DX12
- Fix `panic!` when dropping `Instance` without `InstanceFlags::VALIDATION`. By @hakolao in [#5134](https://github.com/gfx-rs/wgpu/pull/5134)
Expand Down
48 changes: 21 additions & 27 deletions wgpu-core/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use parking_lot::Mutex;
use wgt::Backend;

use crate::{
id::{self},
Epoch, FastHashMap, Index,
id::{self, TypedId},
Epoch, Index,
};
use std::{fmt::Debug, marker::PhantomData, sync::Arc};

Expand Down Expand Up @@ -34,11 +34,10 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc};
/// [`Backend`]: wgt::Backend;
/// [`alloc`]: IdentityManager::alloc
/// [`free`]: IdentityManager::free
#[derive(Debug, Default)]
#[derive(Debug)]
pub(super) struct IdentityValues {
free: Vec<(Index, Epoch)>,
//sorted by Index
used: FastHashMap<Epoch, Vec<Index>>,
next_index: Index,
count: usize,
}

Expand All @@ -47,34 +46,26 @@ impl IdentityValues {
///
/// The backend is incorporated into the id, so that ids allocated with
/// different `backend` values are always distinct.
pub fn alloc<I: id::TypedId>(&mut self, backend: Backend) -> I {
pub fn alloc<I: TypedId>(&mut self, backend: Backend) -> I {
self.count += 1;
match self.free.pop() {
Some((index, epoch)) => I::zip(index, epoch + 1, backend),
None => {
let index = self.next_index;
self.next_index += 1;
let epoch = 1;
let used = self.used.entry(epoch).or_insert_with(Default::default);
let index = if let Some(i) = used.iter().max_by_key(|v| *v) {
i + 1
} else {
0
};
used.push(index);
I::zip(index, epoch, backend)
}
}
}

pub fn mark_as_used<I: id::TypedId>(&mut self, id: I) -> I {
pub fn mark_as_used<I: TypedId>(&mut self, id: I) -> I {
self.count += 1;
let (index, epoch, _backend) = id.unzip();
let used = self.used.entry(epoch).or_insert_with(Default::default);
used.push(index);
id
}

/// Free `id`. It will never be returned from `alloc` again.
pub fn release<I: id::TypedId>(&mut self, id: I) {
pub fn release<I: TypedId>(&mut self, id: I) {
let (index, epoch, _backend) = id.unzip();
self.free.push((index, epoch));
self.count -= 1;
Expand All @@ -86,12 +77,12 @@ impl IdentityValues {
}

#[derive(Debug)]
pub struct IdentityManager<I: id::TypedId> {
pub struct IdentityManager<I: TypedId> {
pub(super) values: Mutex<IdentityValues>,
_phantom: PhantomData<I>,
}

impl<I: id::TypedId> IdentityManager<I> {
impl<I: TypedId> IdentityManager<I> {
pub fn process(&self, backend: Backend) -> I {
self.values.lock().alloc(backend)
}
Expand All @@ -103,10 +94,14 @@ impl<I: id::TypedId> IdentityManager<I> {
}
}

impl<I: id::TypedId> IdentityManager<I> {
impl<I: TypedId> IdentityManager<I> {
pub fn new() -> Self {
Self {
values: Mutex::new(IdentityValues::default()),
values: Mutex::new(IdentityValues {
free: Vec::new(),
next_index: 0,
count: 0,
}),
_phantom: PhantomData,
}
}
Expand All @@ -115,7 +110,7 @@ impl<I: id::TypedId> IdentityManager<I> {
/// A type that can produce [`IdentityManager`] filters for ids of type `I`.
///
/// See the module-level documentation for details.
pub trait IdentityHandlerFactory<I: id::TypedId> {
pub trait IdentityHandlerFactory<I: TypedId> {
type Input: Copy;
/// Create an [`IdentityManager<I>`] implementation that can
/// transform proto-ids into ids of type `I`.
Expand All @@ -138,7 +133,7 @@ pub trait IdentityHandlerFactory<I: id::TypedId> {
#[derive(Debug)]
pub struct IdentityManagerFactory;

impl<I: id::TypedId> IdentityHandlerFactory<I> for IdentityManagerFactory {
impl<I: TypedId> IdentityHandlerFactory<I> for IdentityManagerFactory {
type Input = ();
fn autogenerate_ids() -> bool {
true
Expand Down Expand Up @@ -183,10 +178,9 @@ fn test_epoch_end_of_life() {
let forced_id = man.mark_as_used(id::BufferId::zip(0, 1, Backend::Empty));
assert_eq!(forced_id.unzip().0, 0);
let id1 = man.process(Backend::Empty);
assert_eq!(id1.unzip().0, 1);
assert_eq!(id1.unzip(), (0, 1, Backend::Empty));
man.free(id1);
let id2 = man.process(Backend::Empty);
// confirm that the epoch 1 is no longer re-used
assert_eq!(id2.unzip().0, 1);
assert_eq!(id2.unzip().1, 2);
assert_eq!(id2.unzip(), (0, 2, Backend::Empty));
}

0 comments on commit 08f235c

Please sign in to comment.