@@ -6,6 +6,7 @@ use core::{
66 num:: NonZeroU64 ,
77 ops:: Range ,
88 ptr:: NonNull ,
9+ sync:: atomic:: { AtomicBool , AtomicU32 , Ordering } ,
910} ;
1011use smallvec:: SmallVec ;
1112use thiserror:: Error ;
@@ -438,6 +439,14 @@ pub struct Buffer {
438439 pub ( crate ) bind_groups : Mutex < WeakVec < BindGroup > > ,
439440 pub ( crate ) timestamp_normalization_bind_group : Snatchable < TimestampNormalizationBindGroup > ,
440441 pub ( crate ) indirect_validation_bind_groups : Snatchable < crate :: indirect_validation:: BindGroups > ,
442+ /// Set to true when destroy() is called. This makes check_destroyed() fail immediately,
443+ /// but the actual raw resource cleanup may be deferred if in_flight_count > 0.
444+ pub ( crate ) destroyed : AtomicBool ,
445+ /// Count of command buffer trackers that currently hold a reference to this buffer.
446+ /// Incremented when inserted into a command buffer's tracker, decremented when the
447+ /// tracker is cleared (on command buffer submission or drop).
448+ /// When destroy() is called and this is > 0, raw resource cleanup is deferred to drop().
449+ pub ( crate ) in_flight_count : AtomicU32 ,
441450}
442451
443452impl Drop for Buffer {
@@ -472,6 +481,11 @@ impl Buffer {
472481 & self ,
473482 guard : & SnatchGuard ,
474483 ) -> Result < ( ) , DestroyedResourceError > {
484+ // Check the destroyed flag first - this is set by destroy() even if
485+ // the raw resource hasn't been snatched yet (deferred cleanup case).
486+ if self . destroyed . load ( Ordering :: Acquire ) {
487+ return Err ( DestroyedResourceError ( self . error_ident ( ) ) ) ;
488+ }
475489 self . raw
476490 . get ( guard)
477491 . map ( |_| ( ) )
@@ -903,8 +917,23 @@ impl Buffer {
903917 }
904918
905919 pub fn destroy ( self : & Arc < Self > ) {
920+ // Mark as destroyed. This makes check_destroyed() fail,
921+ // so any future use of this buffer will return an error.
922+ self . destroyed . store ( true , Ordering :: Release ) ;
923+
906924 let device = & self . device ;
907925
926+ // Check if this buffer is currently in any command buffer's tracker.
927+ // If so, we should NOT snatch the raw resource yet - the tracker's Drop
928+ // will call destroy() again when the count reaches 0.
929+ if self . in_flight_count . load ( Ordering :: Acquire ) > 0 {
930+ // Buffer is in a recording command buffer.
931+ // The raw resource will be cleaned up when the command buffer is
932+ // submitted/dropped and the tracker calls destroy() again.
933+ return ;
934+ }
935+
936+ // Not in any command buffer tracker, safe to snatch and cleanup now.
908937 let temp = {
909938 let mut snatch_guard = device. snatchable_lock . write ( ) ;
910939
@@ -1255,6 +1284,14 @@ pub struct Texture {
12551284 pub ( crate ) clear_mode : RwLock < TextureClearMode > ,
12561285 pub ( crate ) views : Mutex < WeakVec < TextureView > > ,
12571286 pub ( crate ) bind_groups : Mutex < WeakVec < BindGroup > > ,
1287+ /// Set to true when destroy() is called. This makes check_destroyed() fail immediately,
1288+ /// but the actual raw resource cleanup may be deferred if in_flight_count > 0.
1289+ pub ( crate ) destroyed : AtomicBool ,
1290+ /// Count of command buffer trackers that currently hold a reference to this texture.
1291+ /// Incremented when inserted into a command buffer's tracker, decremented when the
1292+ /// tracker is cleared (on command buffer submission or drop).
1293+ /// When destroy() is called and this is > 0, raw resource cleanup is deferred to drop().
1294+ pub ( crate ) in_flight_count : AtomicU32 ,
12581295}
12591296
12601297impl Texture {
@@ -1290,6 +1327,8 @@ impl Texture {
12901327 clear_mode : RwLock :: new ( rank:: TEXTURE_CLEAR_MODE , clear_mode) ,
12911328 views : Mutex :: new ( rank:: TEXTURE_VIEWS , WeakVec :: new ( ) ) ,
12921329 bind_groups : Mutex :: new ( rank:: TEXTURE_BIND_GROUPS , WeakVec :: new ( ) ) ,
1330+ destroyed : AtomicBool :: new ( false ) ,
1331+ in_flight_count : AtomicU32 :: new ( 0 ) ,
12931332 }
12941333 }
12951334
@@ -1353,13 +1392,31 @@ impl RawResourceAccess for Texture {
13531392 fn raw < ' a > ( & ' a self , guard : & ' a SnatchGuard ) -> Option < & ' a Self :: DynResource > {
13541393 self . inner . get ( guard) . map ( |t| t. raw ( ) )
13551394 }
1395+
1396+ fn try_raw < ' a > (
1397+ & ' a self ,
1398+ guard : & ' a SnatchGuard ,
1399+ ) -> Result < & ' a Self :: DynResource , DestroyedResourceError > {
1400+ // Check the destroyed flag first - this is set by destroy() even if
1401+ // the raw resource hasn't been snatched yet (deferred cleanup case).
1402+ if self . destroyed . load ( Ordering :: Acquire ) {
1403+ return Err ( DestroyedResourceError ( self . error_ident ( ) ) ) ;
1404+ }
1405+ self . raw ( guard)
1406+ . ok_or_else ( || DestroyedResourceError ( self . error_ident ( ) ) )
1407+ }
13561408}
13571409
13581410impl Texture {
13591411 pub ( crate ) fn try_inner < ' a > (
13601412 & ' a self ,
13611413 guard : & ' a SnatchGuard ,
13621414 ) -> Result < & ' a TextureInner , DestroyedResourceError > {
1415+ // Check the destroyed flag first - this is set by destroy() even if
1416+ // the raw resource hasn't been snatched yet (deferred cleanup case).
1417+ if self . destroyed . load ( Ordering :: Acquire ) {
1418+ return Err ( DestroyedResourceError ( self . error_ident ( ) ) ) ;
1419+ }
13631420 self . inner
13641421 . get ( guard)
13651422 . ok_or_else ( || DestroyedResourceError ( self . error_ident ( ) ) )
@@ -1369,6 +1426,11 @@ impl Texture {
13691426 & self ,
13701427 guard : & SnatchGuard ,
13711428 ) -> Result < ( ) , DestroyedResourceError > {
1429+ // Check the destroyed flag first - this is set by destroy() even if
1430+ // the raw resource hasn't been snatched yet (deferred cleanup case).
1431+ if self . destroyed . load ( Ordering :: Acquire ) {
1432+ return Err ( DestroyedResourceError ( self . error_ident ( ) ) ) ;
1433+ }
13721434 self . inner
13731435 . get ( guard)
13741436 . map ( |_| ( ) )
@@ -1405,8 +1467,23 @@ impl Texture {
14051467 }
14061468
14071469 pub fn destroy ( self : & Arc < Self > ) {
1470+ // Mark as destroyed. This makes check_destroyed() fail,
1471+ // so any future use of this texture will return an error.
1472+ self . destroyed . store ( true , Ordering :: Release ) ;
1473+
14081474 let device = & self . device ;
14091475
1476+ // Check if this texture is currently in any command buffer's tracker.
1477+ // If so, we should NOT snatch the raw resource yet - the tracker's Drop
1478+ // will call destroy() again when the count reaches 0.
1479+ if self . in_flight_count . load ( Ordering :: Acquire ) > 0 {
1480+ // Texture is in a recording command buffer.
1481+ // The raw resource will be cleaned up when the command buffer is
1482+ // submitted/dropped and the tracker calls destroy() again.
1483+ return ;
1484+ }
1485+
1486+ // Not in any command buffer tracker, safe to snatch and cleanup now.
14101487 let temp = {
14111488 let raw = match self . inner . snatch ( & mut device. snatchable_lock . write ( ) ) {
14121489 Some ( TextureInner :: Native { raw } ) => raw,
0 commit comments