introduce DeviceTextureTracker which holds weak references to textures

This commit is contained in:
teoxoy 2024-07-04 22:05:46 +02:00 committed by Teodor Tanasoaia
parent aa9cb71a54
commit 425526828f
6 changed files with 270 additions and 79 deletions

View File

@ -16,7 +16,7 @@ use crate::{
Texture, TextureClearMode, Texture, TextureClearMode,
}, },
snatch::SnatchGuard, snatch::SnatchGuard,
track::{TextureSelector, TextureTracker}, track::{TextureSelector, TextureTrackerSetSingle},
}; };
use hal::CommandEncoder as _; use hal::CommandEncoder as _;
@ -269,11 +269,11 @@ impl Global {
} }
} }
pub(crate) fn clear_texture<A: HalApi>( pub(crate) fn clear_texture<A: HalApi, T: TextureTrackerSetSingle<A>>(
dst_texture: &Arc<Texture<A>>, dst_texture: &Arc<Texture<A>>,
range: TextureInitRange, range: TextureInitRange,
encoder: &mut A::CommandEncoder, encoder: &mut A::CommandEncoder,
texture_tracker: &mut TextureTracker<A>, texture_tracker: &mut T,
alignments: &hal::Alignments, alignments: &hal::Alignments,
zero_buffer: &A::Buffer, zero_buffer: &A::Buffer,
snatch_guard: &SnatchGuard<'_>, snatch_guard: &SnatchGuard<'_>,

View File

@ -434,12 +434,9 @@ impl<A: HalApi> CommandBuffer<A> {
.buffers .buffers
.set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard); .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard);
base.textures.set_from_tracker(&head.textures); let texture_barriers = base
let (transitions, textures) = base.textures.drain_transitions(snatch_guard); .textures
let texture_barriers = transitions .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard);
.into_iter()
.enumerate()
.map(|(i, p)| p.into_hal(textures[i].unwrap().raw().unwrap()));
unsafe { unsafe {
raw.transition_buffers(buffer_barriers); raw.transition_buffers(buffer_barriers);

View File

@ -1343,15 +1343,12 @@ impl Global {
)) ))
.map_err(DeviceError::from)? .map_err(DeviceError::from)?
}; };
trackers let texture_barriers = trackers
.textures .textures
.set_from_usage_scope(&used_surface_textures); .set_from_usage_scope_and_drain_transitions(
let (transitions, textures) = &used_surface_textures,
trackers.textures.drain_transitions(&snatch_guard); &snatch_guard,
let texture_barriers = transitions );
.into_iter()
.enumerate()
.map(|(i, p)| p.into_hal(textures[i].unwrap().raw().unwrap()));
let present = unsafe { let present = unsafe {
baked.encoder.transition_textures(texture_barriers); baked.encoder.transition_textures(texture_barriers);
baked.encoder.end_encoding().unwrap() baked.encoder.end_encoding().unwrap()
@ -1401,15 +1398,12 @@ impl Global {
if !used_surface_textures.is_empty() { if !used_surface_textures.is_empty() {
let mut trackers = device.trackers.lock(); let mut trackers = device.trackers.lock();
trackers let texture_barriers = trackers
.textures .textures
.set_from_usage_scope(&used_surface_textures); .set_from_usage_scope_and_drain_transitions(
let (transitions, textures) = &used_surface_textures,
trackers.textures.drain_transitions(&snatch_guard); &snatch_guard,
let texture_barriers = transitions );
.into_iter()
.enumerate()
.map(|(i, p)| p.into_hal(textures[i].unwrap().raw().unwrap()));
unsafe { unsafe {
pending_writes pending_writes
.command_encoder .command_encoder

View File

@ -3554,7 +3554,9 @@ impl<A: HalApi> Device<A> {
} }
} }
for texture in trackers.textures.used_resources() { for texture in trackers.textures.used_resources() {
let _ = texture.destroy(); if let Some(texture) = Weak::upgrade(&texture) {
let _ = texture.destroy();
}
} }
} }

View File

@ -119,7 +119,8 @@ pub(crate) use buffer::{
use metadata::{ResourceMetadata, ResourceMetadataProvider}; use metadata::{ResourceMetadata, ResourceMetadataProvider};
pub(crate) use stateless::{StatelessBindGroupState, StatelessTracker}; pub(crate) use stateless::{StatelessBindGroupState, StatelessTracker};
pub(crate) use texture::{ pub(crate) use texture::{
TextureBindGroupState, TextureSelector, TextureTracker, TextureUsageScope, DeviceTextureTracker, TextureBindGroupState, TextureSelector, TextureTracker,
TextureTrackerSetSingle, TextureUsageScope,
}; };
use wgt::strict_assert_ne; use wgt::strict_assert_ne;
@ -603,14 +604,14 @@ impl<'a, A: HalApi> UsageScope<'a, A> {
/// A tracker used by Device. /// A tracker used by Device.
pub(crate) struct DeviceTracker<A: HalApi> { pub(crate) struct DeviceTracker<A: HalApi> {
pub buffers: DeviceBufferTracker<A>, pub buffers: DeviceBufferTracker<A>,
pub textures: TextureTracker<A>, pub textures: DeviceTextureTracker<A>,
} }
impl<A: HalApi> DeviceTracker<A> { impl<A: HalApi> DeviceTracker<A> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
buffers: DeviceBufferTracker::new(), buffers: DeviceBufferTracker::new(),
textures: TextureTracker::new(), textures: DeviceTextureTracker::new(),
} }
} }
} }

View File

@ -30,14 +30,19 @@ use crate::{
ResourceUsageCompatibilityError, ResourceUses, ResourceUsageCompatibilityError, ResourceUses,
}, },
}; };
use hal::TextureUses; use hal::{TextureBarrier, TextureUses};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use naga::FastHashMap; use naga::FastHashMap;
use wgt::{strict_assert, strict_assert_eq}; use wgt::{strict_assert, strict_assert_eq};
use std::{iter, ops::Range, sync::Arc, vec::Drain}; use std::{
iter,
ops::Range,
sync::{Arc, Weak},
vec::Drain,
};
/// Specifies a particular set of subresources in a texture. /// Specifies a particular set of subresources in a texture.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -370,7 +375,16 @@ impl<A: HalApi> TextureUsageScope<A> {
} }
} }
/// Stores all texture state within a command buffer or device. pub(crate) trait TextureTrackerSetSingle<A: HalApi> {
fn set_single(
&mut self,
texture: &Arc<Texture<A>>,
selector: TextureSelector,
new_state: TextureUses,
) -> Drain<'_, PendingTransition<TextureUses>>;
}
/// Stores all texture state within a command buffer.
pub(crate) struct TextureTracker<A: HalApi> { pub(crate) struct TextureTracker<A: HalApi> {
start_set: TextureStateSet, start_set: TextureStateSet,
end_set: TextureStateSet, end_set: TextureStateSet,
@ -455,39 +469,6 @@ impl<A: HalApi> TextureTracker<A> {
(transitions, textures) (transitions, textures)
} }
/// Inserts a single texture and a state into the resource tracker.
///
/// If the resource already exists in the tracker, this will panic.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn insert_single(&mut self, resource: &Arc<Texture<A>>, usage: TextureUses) {
let index = resource.tracker_index().as_usize();
self.allow_index(index);
self.tracker_assert_in_bounds(index);
unsafe {
let currently_owned = self.metadata.contains_unchecked(index);
if currently_owned {
panic!("Tried to insert texture already tracked");
}
insert(
None,
Some(&mut self.start_set),
&mut self.end_set,
&mut self.metadata,
index,
TextureStateProvider::KnownSingle { state: usage },
None,
ResourceMetadataProvider::Direct { resource },
)
};
}
/// Sets the state of a single texture. /// Sets the state of a single texture.
/// ///
/// If a transition is needed to get the texture into the given state, that transition /// If a transition is needed to get the texture into the given state, that transition
@ -659,12 +640,218 @@ impl<A: HalApi> TextureTracker<A> {
unsafe { scope.metadata.remove(index) }; unsafe { scope.metadata.remove(index) };
} }
} }
}
impl<A: HalApi> TextureTrackerSetSingle<A> for TextureTracker<A> {
fn set_single(
&mut self,
texture: &Arc<Texture<A>>,
selector: TextureSelector,
new_state: TextureUses,
) -> Drain<'_, PendingTransition<TextureUses>> {
self.set_single(texture, selector, new_state)
}
}
/// Stores all texture state within a device.
pub(crate) struct DeviceTextureTracker<A: HalApi> {
current_state_set: TextureStateSet,
metadata: ResourceMetadata<Weak<Texture<A>>>,
temp: Vec<PendingTransition<TextureUses>>,
}
impl<A: HalApi> DeviceTextureTracker<A> {
pub fn new() -> Self {
Self {
current_state_set: TextureStateSet::new(),
metadata: ResourceMetadata::new(),
temp: Vec::new(),
}
}
fn tracker_assert_in_bounds(&self, index: usize) {
self.metadata.tracker_assert_in_bounds(index);
strict_assert!(index < self.current_state_set.simple.len());
strict_assert!(if self.metadata.contains(index)
&& self.current_state_set.simple[index] == TextureUses::COMPLEX
{
self.current_state_set.complex.contains_key(&index)
} else {
true
});
}
/// Extend the vectors to let the given index be valid.
fn allow_index(&mut self, index: usize) {
if index >= self.current_state_set.simple.len() {
self.current_state_set.set_size(index + 1);
self.metadata.set_size(index + 1);
}
}
/// Returns a list of all textures tracked.
pub fn used_resources(&self) -> impl Iterator<Item = Weak<Texture<A>>> + '_ {
self.metadata.owned_resources()
}
/// Inserts a single texture and a state into the resource tracker.
///
/// If the resource already exists in the tracker, it will be overwritten.
pub fn insert_single(&mut self, texture: &Arc<Texture<A>>, usage: TextureUses) {
let index = texture.tracker_index().as_usize();
self.allow_index(index);
self.tracker_assert_in_bounds(index);
unsafe {
insert(
None,
None,
&mut self.current_state_set,
&mut self.metadata,
index,
TextureStateProvider::KnownSingle { state: usage },
None,
ResourceMetadataProvider::Direct {
resource: &Arc::downgrade(texture),
},
)
};
}
/// Sets the state of a single texture.
///
/// If a transition is needed to get the texture into the given state, that transition
/// is returned.
pub fn set_single(
&mut self,
texture: &Arc<Texture<A>>,
selector: TextureSelector,
new_state: TextureUses,
) -> Drain<'_, PendingTransition<TextureUses>> {
let index = texture.tracker_index().as_usize();
self.allow_index(index);
self.tracker_assert_in_bounds(index);
let start_state_provider = TextureStateProvider::Selector {
selector,
state: new_state,
};
unsafe {
barrier(
&texture.full_range,
&self.current_state_set,
index,
start_state_provider.clone(),
&mut self.temp,
)
};
unsafe {
update(
&texture.full_range,
None,
&mut self.current_state_set,
index,
start_state_provider,
)
};
self.temp.drain(..)
}
/// Sets the given state for all texture in the given tracker.
///
/// If a transition is needed to get the texture into the needed state,
/// those transitions are returned.
pub fn set_from_tracker_and_drain_transitions<'a, 'b: 'a>(
&'a mut self,
tracker: &'a TextureTracker<A>,
snatch_guard: &'b SnatchGuard<'b>,
) -> impl Iterator<Item = TextureBarrier<'a, A>> {
for index in tracker.metadata.owned_indices() {
self.tracker_assert_in_bounds(index);
let start_state_provider = TextureStateProvider::TextureSet {
set: &tracker.start_set,
};
let end_state_provider = TextureStateProvider::TextureSet {
set: &tracker.end_set,
};
unsafe {
let texture_selector = &tracker.metadata.get_resource_unchecked(index).full_range;
barrier(
texture_selector,
&self.current_state_set,
index,
start_state_provider,
&mut self.temp,
);
update(
texture_selector,
None,
&mut self.current_state_set,
index,
end_state_provider,
);
}
}
self.temp.drain(..).map(|pending| {
let tex = unsafe { tracker.metadata.get_resource_unchecked(pending.id as _) };
let tex = tex.try_raw(snatch_guard).unwrap();
pending.into_hal(tex)
})
}
/// Sets the given state for all textures in the given UsageScope.
///
/// If a transition is needed to get the textures into the needed state,
/// those transitions are returned.
pub fn set_from_usage_scope_and_drain_transitions<'a, 'b: 'a>(
&'a mut self,
scope: &'a TextureUsageScope<A>,
snatch_guard: &'b SnatchGuard<'b>,
) -> impl Iterator<Item = TextureBarrier<'a, A>> {
for index in scope.metadata.owned_indices() {
self.tracker_assert_in_bounds(index);
let start_state_provider = TextureStateProvider::TextureSet { set: &scope.set };
unsafe {
let texture_selector = &scope.metadata.get_resource_unchecked(index).full_range;
barrier(
texture_selector,
&self.current_state_set,
index,
start_state_provider.clone(),
&mut self.temp,
);
update(
texture_selector,
None,
&mut self.current_state_set,
index,
start_state_provider,
);
}
}
self.temp.drain(..).map(|pending| {
let tex = unsafe { scope.metadata.get_resource_unchecked(pending.id as _) };
let tex = tex.try_raw(snatch_guard).unwrap();
pending.into_hal(tex)
})
}
/// Unconditionally removes the given resource from the tracker. /// Unconditionally removes the given resource from the tracker.
/// ///
/// Returns true if the resource was removed. /// Returns true if the resource was removed.
/// ///
/// If the ID is higher than the length of internal vectors, /// If the index is higher than the length of internal vectors,
/// false will be returned. /// false will be returned.
pub fn remove(&mut self, index: TrackerIndex) -> bool { pub fn remove(&mut self, index: TrackerIndex) -> bool {
let index = index.as_usize(); let index = index.as_usize();
@ -677,8 +864,7 @@ impl<A: HalApi> TextureTracker<A> {
unsafe { unsafe {
if self.metadata.contains_unchecked(index) { if self.metadata.contains_unchecked(index) {
self.start_set.complex.remove(&index); self.current_state_set.complex.remove(&index);
self.end_set.complex.remove(&index);
self.metadata.remove(index); self.metadata.remove(index);
return true; return true;
} }
@ -688,6 +874,17 @@ impl<A: HalApi> TextureTracker<A> {
} }
} }
impl<A: HalApi> TextureTrackerSetSingle<A> for DeviceTextureTracker<A> {
fn set_single(
&mut self,
texture: &Arc<Texture<A>>,
selector: TextureSelector,
new_state: TextureUses,
) -> Drain<'_, PendingTransition<TextureUses>> {
self.set_single(texture, selector, new_state)
}
}
/// An iterator adapter that can store two different iterator types. /// An iterator adapter that can store two different iterator types.
#[derive(Clone)] #[derive(Clone)]
enum EitherIter<L, R> { enum EitherIter<L, R> {
@ -893,12 +1090,10 @@ unsafe fn insert_or_barrier_update<A: HalApi>(
barriers, barriers,
) )
}; };
let start_state_set = start_state.unwrap();
unsafe { unsafe {
update( update(
texture_selector, texture_selector,
start_state_set, start_state,
current_state_set, current_state_set,
index, index,
update_state_provider, update_state_provider,
@ -907,15 +1102,15 @@ unsafe fn insert_or_barrier_update<A: HalApi>(
} }
#[inline(always)] #[inline(always)]
unsafe fn insert<A: HalApi>( unsafe fn insert<T: Clone>(
texture_selector: Option<&TextureSelector>, texture_selector: Option<&TextureSelector>,
start_state: Option<&mut TextureStateSet>, start_state: Option<&mut TextureStateSet>,
end_state: &mut TextureStateSet, end_state: &mut TextureStateSet,
resource_metadata: &mut ResourceMetadata<Arc<Texture<A>>>, resource_metadata: &mut ResourceMetadata<T>,
index: usize, index: usize,
start_state_provider: TextureStateProvider<'_>, start_state_provider: TextureStateProvider<'_>,
end_state_provider: Option<TextureStateProvider<'_>>, end_state_provider: Option<TextureStateProvider<'_>>,
metadata_provider: ResourceMetadataProvider<'_, Arc<Texture<A>>>, metadata_provider: ResourceMetadataProvider<'_, T>,
) { ) {
let start_layers = unsafe { start_state_provider.get_state(texture_selector, index) }; let start_layers = unsafe { start_state_provider.get_state(texture_selector, index) };
match start_layers { match start_layers {
@ -1273,19 +1468,21 @@ unsafe fn barrier(
#[inline(always)] #[inline(always)]
unsafe fn update( unsafe fn update(
texture_selector: &TextureSelector, texture_selector: &TextureSelector,
start_state_set: &mut TextureStateSet, start_state_set: Option<&mut TextureStateSet>,
current_state_set: &mut TextureStateSet, current_state_set: &mut TextureStateSet,
index: usize, index: usize,
state_provider: TextureStateProvider<'_>, state_provider: TextureStateProvider<'_>,
) { ) {
let start_simple = unsafe { *start_state_set.simple.get_unchecked(index) };
// We only ever need to update the start state here if the state is complex. // We only ever need to update the start state here if the state is complex.
// //
// If the state is simple, the first insert to the tracker would cover it. // If the state is simple, the first insert to the tracker would cover it.
let mut start_complex = None; let mut start_complex = None;
if start_simple == TextureUses::COMPLEX { if let Some(start_state_set) = start_state_set {
start_complex = Some(unsafe { start_state_set.complex.get_mut(&index).unwrap_unchecked() }); let start_simple = unsafe { *start_state_set.simple.get_unchecked(index) };
if start_simple == TextureUses::COMPLEX {
start_complex =
Some(unsafe { start_state_set.complex.get_mut(&index).unwrap_unchecked() });
}
} }
let current_simple = unsafe { current_state_set.simple.get_unchecked_mut(index) }; let current_simple = unsafe { current_state_set.simple.get_unchecked_mut(index) };