From c77b4d3f563c9a4924aa93472fe3ada8c270eef5 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Mon, 26 Feb 2024 21:25:12 +0100 Subject: [PATCH] Use a unique tracker index per resource instead of the ID in trackers (#5244) Co-authored-by: Connor Fitzgerald --- CHANGELOG.md | 1 + tests/tests/bind_group_layout_dedup.rs | 23 +-- tests/tests/mem_leaks.rs | 27 ++-- wgpu-core/src/command/bundle.rs | 5 +- wgpu-core/src/command/compute.rs | 27 ++-- wgpu-core/src/command/draw.rs | 1 - wgpu-core/src/command/mod.rs | 1 + wgpu-core/src/command/render.rs | 30 ++-- wgpu-core/src/device/global.rs | 86 +++++------ wgpu-core/src/device/life.rs | 129 ++++++++-------- wgpu-core/src/device/queue.rs | 53 ++++--- wgpu-core/src/device/resource.rs | 78 +++++++--- wgpu-core/src/instance.rs | 14 +- wgpu-core/src/present.rs | 18 ++- wgpu-core/src/registry.rs | 9 +- wgpu-core/src/resource.rs | 35 +++-- wgpu-core/src/track/buffer.rs | 51 +++---- wgpu-core/src/track/metadata.rs | 11 +- wgpu-core/src/track/mod.rs | 194 +++++++++++++++++-------- wgpu-core/src/track/stateless.rs | 29 ++-- wgpu-core/src/track/texture.rs | 65 +++------ 21 files changed, 488 insertions(+), 399 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9377731d42..07d3cccef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Bottom level categories: - Fix timeout when presenting a surface where no work has been done. By @waywardmonkeys in [#5200](https://github.com/gfx-rs/wgpu/pull/5200) - Simplify and speed up the allocation of internal IDs. By @nical in [#5229](https://github.com/gfx-rs/wgpu/pull/5229) - Fix an issue where command encoders weren't properly freed if an error occurred during command encoding. By @ErichDonGubler in [#5251](https://github.com/gfx-rs/wgpu/pull/5251). +- Fix registry leaks with de-duplicated resources. By @nical in [#5244](https://github.com/gfx-rs/wgpu/pull/5244) - Fix behavior of integer `clamp` when `min` argument > `max` argument. By @cwfitzgerald in [#5300](https://github.com/gfx-rs/wgpu/pull/5300). - Fix missing validation for `Device::clear_buffer` where `offset + size buffer.size` was not checked when `size` was omitted. By @ErichDonGubler in [#5282](https://github.com/gfx-rs/wgpu/pull/5282). diff --git a/tests/tests/bind_group_layout_dedup.rs b/tests/tests/bind_group_layout_dedup.rs index a0f6dad25d..7ac30fb8fe 100644 --- a/tests/tests/bind_group_layout_dedup.rs +++ b/tests/tests/bind_group_layout_dedup.rs @@ -128,8 +128,12 @@ async fn bgl_dedupe(ctx: TestingContext) { .panic_on_timeout(); if ctx.adapter_info.backend != wgt::Backend::BrowserWebGpu { + // Indices are made reusable as soon as the handle is dropped so we keep them around + // for the duration of the loop. + let mut bgls = Vec::new(); + let mut indices = Vec::new(); // Now all of the BGL ids should be dead, so we should get the same ids again. - for i in 0..=2 { + for _ in 0..=2 { let test_bgl = ctx .device .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -138,15 +142,14 @@ async fn bgl_dedupe(ctx: TestingContext) { }); let test_bgl_idx = test_bgl.global_id().inner() & 0xFFFF_FFFF; - - // https://github.com/gfx-rs/wgpu/issues/4912 - // - // ID 2 is the deduplicated ID, which is never properly recycled. - if i == 2 { - assert_eq!(test_bgl_idx, 3); - } else { - assert_eq!(test_bgl_idx, i); - } + bgls.push(test_bgl); + indices.push(test_bgl_idx); + } + // We don't guarantee that the IDs will appear in the same order. Sort them + // and check that they all appear exactly once. + indices.sort(); + for (i, index) in indices.iter().enumerate() { + assert_eq!(*index, i as u64); } } } diff --git a/tests/tests/mem_leaks.rs b/tests/tests/mem_leaks.rs index 91a329d20c..83fa2bbc11 100644 --- a/tests/tests/mem_leaks.rs +++ b/tests/tests/mem_leaks.rs @@ -127,7 +127,7 @@ async fn draw_test_with_reports( let global_report = ctx.instance.generate_report().unwrap(); let report = global_report.hub_report(ctx.adapter_info.backend); - assert_eq!(report.shader_modules.num_allocated, 1); + assert_eq!(report.shader_modules.num_allocated, 0); assert_eq!(report.shader_modules.num_kept_from_user, 0); assert_eq!(report.textures.num_allocated, 0); assert_eq!(report.texture_views.num_allocated, 0); @@ -166,7 +166,7 @@ async fn draw_test_with_reports( assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.texture_views.num_allocated, 1); assert_eq!(report.texture_views.num_kept_from_user, 1); - assert_eq!(report.textures.num_allocated, 1); + assert_eq!(report.textures.num_allocated, 0); assert_eq!(report.textures.num_kept_from_user, 0); let mut encoder = ctx @@ -204,7 +204,7 @@ async fn draw_test_with_reports( assert_eq!(report.command_buffers.num_allocated, 1); assert_eq!(report.render_bundles.num_allocated, 0); assert_eq!(report.texture_views.num_allocated, 1); - assert_eq!(report.textures.num_allocated, 1); + assert_eq!(report.textures.num_allocated, 0); function(&mut rpass); @@ -227,19 +227,20 @@ async fn draw_test_with_reports( assert_eq!(report.texture_views.num_kept_from_user, 0); assert_eq!(report.textures.num_kept_from_user, 0); assert_eq!(report.command_buffers.num_allocated, 1); - assert_eq!(report.render_pipelines.num_allocated, 1); - assert_eq!(report.pipeline_layouts.num_allocated, 1); - assert_eq!(report.bind_group_layouts.num_allocated, 1); - assert_eq!(report.bind_groups.num_allocated, 1); - assert_eq!(report.buffers.num_allocated, 1); - assert_eq!(report.texture_views.num_allocated, 1); - assert_eq!(report.textures.num_allocated, 1); + assert_eq!(report.render_pipelines.num_allocated, 0); + assert_eq!(report.pipeline_layouts.num_allocated, 0); + assert_eq!(report.bind_group_layouts.num_allocated, 0); + assert_eq!(report.bind_groups.num_allocated, 0); + assert_eq!(report.buffers.num_allocated, 0); + assert_eq!(report.texture_views.num_allocated, 0); + assert_eq!(report.textures.num_allocated, 0); let submit_index = ctx.queue.submit(Some(encoder.finish())); - let global_report = ctx.instance.generate_report().unwrap(); - let report = global_report.hub_report(ctx.adapter_info.backend); - assert_eq!(report.command_buffers.num_allocated, 0); + // TODO: fix in https://github.com/gfx-rs/wgpu/pull/5141 + // let global_report = ctx.instance.generate_report().unwrap(); + // let report = global_report.hub_report(ctx.adapter_info.backend); + // assert_eq!(report.command_buffers.num_allocated, 0); ctx.async_poll(wgpu::Maintain::wait_for(submit_index)) .await diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 9e4b8d9fec..9000c1b4b3 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -758,7 +758,10 @@ impl RenderBundleEncoder { buffer_memory_init_actions, texture_memory_init_actions, context: self.context, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(device.tracker_indices.bundles.clone()), + ), discard_hal_labels: device .instance_flags .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS), diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index b95a90dee8..c2fd3ab397 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -1,6 +1,7 @@ use crate::device::DeviceError; use crate::resource::Resource; use crate::snatch::SnatchGuard; +use crate::track::TrackerIndex; use crate::{ binding_model::{ BindError, BindGroup, LateMinBufferBindingSizeMismatch, PushConstantUploadError, @@ -305,7 +306,7 @@ impl State { raw_encoder: &mut A::CommandEncoder, base_trackers: &mut Tracker, bind_group_guard: &Storage>, - indirect_buffer: Option, + indirect_buffer: Option, snatch_guard: &SnatchGuard, ) -> Result<(), UsageConflict> { for id in self.binder.list_active() { @@ -402,12 +403,11 @@ impl Global { let pipeline_guard = hub.compute_pipelines.read(); let query_set_guard = hub.query_sets.read(); let buffer_guard = hub.buffers.read(); - let texture_guard = hub.textures.read(); let mut state = State { binder: Binder::new(), pipeline: None, - scope: UsageScope::new(&*buffer_guard, &*texture_guard), + scope: UsageScope::new(&device.tracker_indices), debug_scope_depth: 0, }; let mut temp_offsets = Vec::new(); @@ -452,17 +452,14 @@ impl Global { let snatch_guard = device.snatchable_lock.read(); - tracker.set_size( - Some(&*buffer_guard), - Some(&*texture_guard), - None, - None, - Some(&*bind_group_guard), - Some(&*pipeline_guard), - None, - None, - Some(&*query_set_guard), - ); + let indices = &device.tracker_indices; + tracker.buffers.set_size(indices.buffers.size()); + tracker.textures.set_size(indices.textures.size()); + tracker.bind_groups.set_size(indices.bind_groups.size()); + tracker + .compute_pipelines + .set_size(indices.compute_pipelines.size()); + tracker.query_sets.set_size(indices.query_sets.size()); let discard_hal_labels = self .instance @@ -757,7 +754,7 @@ impl Global { raw, &mut intermediate_trackers, &*bind_group_guard, - Some(buffer_id), + Some(indirect_buffer.as_info().tracker_index()), &snatch_guard, ) .map_pass_err(scope)?; diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 9b35f0b69d..98aa689b78 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -277,7 +277,6 @@ pub enum ArcRenderCommand { SetStencilReference(u32), SetViewport { rect: Rect, - //TODO: use half-float to reduce the size? depth_min: f32, depth_max: f32, }, diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 416c0b057e..febed4fc97 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -174,6 +174,7 @@ impl CommandBuffer { .as_ref() .unwrap_or(&String::from("")) .as_str(), + None, ), data: Mutex::new(Some(CommandBufferMutable { encoder: CommandEncoder { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 4a9d392a90..9141ddb021 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -22,7 +22,7 @@ use crate::{ hal_label, id, init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction}, pipeline::{self, PipelineFlags}, - resource::{Buffer, QuerySet, Texture, TextureView, TextureViewNotRenderableReason}, + resource::{QuerySet, Texture, TextureView, TextureViewNotRenderableReason}, storage::Storage, track::{TextureSelector, Tracker, UsageConflict, UsageScope}, validation::{ @@ -801,8 +801,6 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { texture_memory_actions: &mut CommandBufferTextureMemoryActions, pending_query_resets: &mut QueryResetMap, view_guard: &'a Storage>, - buffer_guard: &'a Storage>, - texture_guard: &'a Storage>, query_set_guard: &'a Storage>, snatch_guard: &SnatchGuard<'a>, ) -> Result { @@ -1216,7 +1214,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { Ok(Self { context, - usage_scope: UsageScope::new(buffer_guard, texture_guard), + usage_scope: UsageScope::new(&device.tracker_indices), render_attachments, is_depth_read_only, is_stencil_read_only, @@ -1388,7 +1386,6 @@ impl Global { let render_pipeline_guard = hub.render_pipelines.read(); let query_set_guard = hub.query_sets.read(); let buffer_guard = hub.buffers.read(); - let texture_guard = hub.textures.read(); let view_guard = hub.texture_views.read(); log::trace!( @@ -1408,24 +1405,21 @@ impl Global { texture_memory_actions, pending_query_resets, &*view_guard, - &*buffer_guard, - &*texture_guard, &*query_set_guard, &snatch_guard, ) .map_pass_err(pass_scope)?; - tracker.set_size( - Some(&*buffer_guard), - Some(&*texture_guard), - Some(&*view_guard), - None, - Some(&*bind_group_guard), - None, - Some(&*render_pipeline_guard), - Some(&*bundle_guard), - Some(&*query_set_guard), - ); + let indices = &device.tracker_indices; + tracker.buffers.set_size(indices.buffers.size()); + tracker.textures.set_size(indices.textures.size()); + tracker.views.set_size(indices.texture_views.size()); + tracker.bind_groups.set_size(indices.bind_groups.size()); + tracker + .render_pipelines + .set_size(indices.render_pipelines.size()); + tracker.bundles.set_size(indices.bundles.size()); + tracker.query_sets.set_size(indices.query_sets.size()); let raw = &mut encoder.raw; diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index e22b6e8392..9303d72097 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -260,7 +260,7 @@ impl Global { .trackers .lock() .buffers - .insert_single(id, resource, buffer_use); + .insert_single(resource, buffer_use); return (id, None); }; @@ -527,7 +527,7 @@ impl Global { .lock_life() .suspected_resources .buffers - .insert(buffer_id, buffer); + .insert(buffer.info.tracker_index(), buffer); } if wait { @@ -571,11 +571,11 @@ impl Global { let (id, resource) = fid.assign(texture); api_log!("Device::create_texture({desc:?}) -> {id:?}"); - device.trackers.lock().textures.insert_single( - id, - resource, - hal::TextureUses::UNINITIALIZED, - ); + device + .trackers + .lock() + .textures + .insert_single(resource, hal::TextureUses::UNINITIALIZED); return (id, None); }; @@ -645,11 +645,11 @@ impl Global { let (id, resource) = fid.assign(texture); api_log!("Device::create_texture({desc:?}) -> {id:?}"); - device.trackers.lock().textures.insert_single( - id, - resource, - hal::TextureUses::UNINITIALIZED, - ); + device + .trackers + .lock() + .textures + .insert_single(resource, hal::TextureUses::UNINITIALIZED); return (id, None); }; @@ -702,7 +702,7 @@ impl Global { .trackers .lock() .buffers - .insert_single(id, buffer, hal::BufferUses::empty()); + .insert_single(buffer, hal::BufferUses::empty()); return (id, None); }; @@ -762,7 +762,7 @@ impl Global { .lock_life() .suspected_resources .textures - .insert(texture_id, texture.clone()); + .insert(texture.info.tracker_index(), texture.clone()); } } @@ -822,7 +822,7 @@ impl Global { } api_log!("Texture::create_view({texture_id:?}) -> {id:?}"); - device.trackers.lock().views.insert_single(id, resource); + device.trackers.lock().views.insert_single(resource); return (id, None); }; @@ -852,7 +852,7 @@ impl Global { .lock_life() .suspected_resources .texture_views - .insert(texture_view_id, view.clone()); + .insert(view.info.tracker_index(), view.clone()); if wait { match view.device.wait_for_submit(last_submit_index) { @@ -898,7 +898,7 @@ impl Global { let (id, resource) = fid.assign(sampler); api_log!("Device::create_sampler -> {id:?}"); - device.trackers.lock().samplers.insert_single(id, resource); + device.trackers.lock().samplers.insert_single(resource); return (id, None); }; @@ -923,7 +923,7 @@ impl Global { .lock_life() .suspected_resources .samplers - .insert(sampler_id, sampler.clone()); + .insert(sampler.info.tracker_index(), sampler.clone()); } } @@ -1022,7 +1022,7 @@ impl Global { .lock_life() .suspected_resources .bind_group_layouts - .insert(bind_group_layout_id, layout.clone()); + .insert(layout.info.tracker_index(), layout.clone()); } } @@ -1083,7 +1083,7 @@ impl Global { .lock_life() .suspected_resources .pipeline_layouts - .insert(pipeline_layout_id, layout.clone()); + .insert(layout.info.tracker_index(), layout.clone()); } } @@ -1138,11 +1138,7 @@ impl Global { api_log!("Device::create_bind_group -> {id:?}"); - device - .trackers - .lock() - .bind_groups - .insert_single(id, resource); + device.trackers.lock().bind_groups.insert_single(resource); return (id, None); }; @@ -1166,7 +1162,7 @@ impl Global { .lock_life() .suspected_resources .bind_groups - .insert(bind_group_id, bind_group.clone()); + .insert(bind_group.info.tracker_index(), bind_group.clone()); } } @@ -1448,7 +1444,7 @@ impl Global { let (id, resource) = fid.assign(render_bundle); api_log!("RenderBundleEncoder::finish -> {id:?}"); - device.trackers.lock().bundles.insert_single(id, resource); + device.trackers.lock().bundles.insert_single(resource); return (id, None); }; @@ -1472,7 +1468,7 @@ impl Global { .lock_life() .suspected_resources .render_bundles - .insert(render_bundle_id, bundle.clone()); + .insert(bundle.info.tracker_index(), bundle.clone()); } } @@ -1511,11 +1507,7 @@ impl Global { let (id, resource) = fid.assign(query_set); api_log!("Device::create_query_set -> {id:?}"); - device - .trackers - .lock() - .query_sets - .insert_single(id, resource); + device.trackers.lock().query_sets.insert_single(resource); return (id, None); }; @@ -1542,7 +1534,7 @@ impl Global { .lock_life() .suspected_resources .query_sets - .insert(query_set_id, query_set.clone()); + .insert(query_set.info.tracker_index(), query_set.clone()); } } @@ -1598,7 +1590,7 @@ impl Global { .trackers .lock() .render_pipelines - .insert_single(id, resource); + .insert_single(resource); return (id, None); }; @@ -1670,18 +1662,17 @@ impl Global { let hub = A::hub(self); if let Some(pipeline) = hub.render_pipelines.unregister(render_pipeline_id) { - let layout_id = pipeline.layout.as_info().id(); let device = &pipeline.device; let mut life_lock = device.lock_life(); life_lock .suspected_resources .render_pipelines - .insert(render_pipeline_id, pipeline.clone()); + .insert(pipeline.info.tracker_index(), pipeline.clone()); - life_lock - .suspected_resources - .pipeline_layouts - .insert(layout_id, pipeline.layout.clone()); + life_lock.suspected_resources.pipeline_layouts.insert( + pipeline.layout.info.tracker_index(), + pipeline.layout.clone(), + ); } } @@ -1732,7 +1723,7 @@ impl Global { .trackers .lock() .compute_pipelines - .insert_single(id, resource); + .insert_single(resource); return (id, None); }; @@ -1802,17 +1793,16 @@ impl Global { let hub = A::hub(self); if let Some(pipeline) = hub.compute_pipelines.unregister(compute_pipeline_id) { - let layout_id = pipeline.layout.as_info().id(); let device = &pipeline.device; let mut life_lock = device.lock_life(); life_lock .suspected_resources .compute_pipelines - .insert(compute_pipeline_id, pipeline.clone()); - life_lock - .suspected_resources - .pipeline_layouts - .insert(layout_id, pipeline.layout.clone()); + .insert(pipeline.info.tracker_index(), pipeline.clone()); + life_lock.suspected_resources.pipeline_layouts.insert( + pipeline.layout.info.tracker_index(), + pipeline.layout.clone(), + ); } } diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 86c5d027c7..7b06a4a30b 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -6,17 +6,13 @@ use crate::{ DeviceError, DeviceLostClosure, }, hal_api::HalApi, - id::{ - self, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, Id, PipelineLayoutId, - QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, StagingBufferId, TextureId, - TextureViewId, - }, + id, pipeline::{ComputePipeline, RenderPipeline}, resource::{ self, Buffer, DestroyedBuffer, DestroyedTexture, QuerySet, Resource, Sampler, StagingBuffer, Texture, TextureView, }, - track::{ResourceTracker, Tracker}, + track::{ResourceTracker, Tracker, TrackerIndex}, FastHashMap, SubmissionIndex, }; use smallvec::SmallVec; @@ -28,20 +24,20 @@ use thiserror::Error; /// A struct that keeps lists of resources that are no longer needed by the user. #[derive(Default)] pub(crate) struct ResourceMaps { - pub buffers: FastHashMap>>, - pub staging_buffers: FastHashMap>>, - pub textures: FastHashMap>>, - pub texture_views: FastHashMap>>, - pub samplers: FastHashMap>>, - pub bind_groups: FastHashMap>>, - pub bind_group_layouts: FastHashMap>>, - pub render_pipelines: FastHashMap>>, - pub compute_pipelines: FastHashMap>>, - pub pipeline_layouts: FastHashMap>>, - pub render_bundles: FastHashMap>>, - pub query_sets: FastHashMap>>, - pub destroyed_buffers: FastHashMap>>, - pub destroyed_textures: FastHashMap>>, + pub buffers: FastHashMap>>, + pub staging_buffers: FastHashMap>>, + pub textures: FastHashMap>>, + pub texture_views: FastHashMap>>, + pub samplers: FastHashMap>>, + pub bind_groups: FastHashMap>>, + pub bind_group_layouts: FastHashMap>>, + pub render_pipelines: FastHashMap>>, + pub compute_pipelines: FastHashMap>>, + pub pipeline_layouts: FastHashMap>>, + pub render_bundles: FastHashMap>>, + pub query_sets: FastHashMap>>, + pub destroyed_buffers: FastHashMap>>, + pub destroyed_textures: FastHashMap>>, } impl ResourceMaps { @@ -276,25 +272,29 @@ impl LifetimeTracker { for res in temp_resources { match res { TempResource::Buffer(raw) => { - last_resources.buffers.insert(raw.as_info().id(), raw); + last_resources + .buffers + .insert(raw.as_info().tracker_index(), raw); } TempResource::StagingBuffer(raw) => { last_resources .staging_buffers - .insert(raw.as_info().id(), raw); + .insert(raw.as_info().tracker_index(), raw); } TempResource::DestroyedBuffer(destroyed) => { last_resources .destroyed_buffers - .insert(destroyed.id, destroyed); + .insert(destroyed.tracker_index, destroyed); } TempResource::Texture(raw) => { - last_resources.textures.insert(raw.as_info().id(), raw); + last_resources + .textures + .insert(raw.as_info().tracker_index(), raw); } TempResource::DestroyedTexture(destroyed) => { last_resources .destroyed_textures - .insert(destroyed.id, destroyed); + .insert(destroyed.tracker_index, destroyed); } } } @@ -310,12 +310,14 @@ impl LifetimeTracker { pub fn post_submit(&mut self) { for v in self.future_suspected_buffers.drain(..).take(1) { - self.suspected_resources.buffers.insert(v.as_info().id(), v); + self.suspected_resources + .buffers + .insert(v.as_info().tracker_index(), v); } for v in self.future_suspected_textures.drain(..).take(1) { self.suspected_resources .textures - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } } @@ -386,19 +388,27 @@ impl LifetimeTracker { if let Some(resources) = resources { match temp_resource { TempResource::Buffer(raw) => { - resources.buffers.insert(raw.as_info().id(), raw); + resources.buffers.insert(raw.as_info().tracker_index(), raw); } TempResource::StagingBuffer(raw) => { - resources.staging_buffers.insert(raw.as_info().id(), raw); + resources + .staging_buffers + .insert(raw.as_info().tracker_index(), raw); } TempResource::DestroyedBuffer(destroyed) => { - resources.destroyed_buffers.insert(destroyed.id, destroyed); + resources + .destroyed_buffers + .insert(destroyed.tracker_index, destroyed); } TempResource::Texture(raw) => { - resources.textures.insert(raw.as_info().id(), raw); + resources + .textures + .insert(raw.as_info().tracker_index(), raw); } TempResource::DestroyedTexture(destroyed) => { - resources.destroyed_textures.insert(destroyed.id, destroyed); + resources + .destroyed_textures + .insert(destroyed.tracker_index, destroyed); } } } @@ -420,27 +430,27 @@ impl LifetimeTracker { impl LifetimeTracker { fn triage_resources( - resources_map: &mut FastHashMap, Arc>, + resources_map: &mut FastHashMap>, active: &mut [ActiveSubmission], - trackers: &mut impl ResourceTracker, - get_resource_map: impl Fn(&mut ResourceMaps) -> &mut FastHashMap, Arc>, + trackers: &mut impl ResourceTracker, + get_resource_map: impl Fn(&mut ResourceMaps) -> &mut FastHashMap>, ) -> Vec> where R: Resource, { let mut removed_resources = Vec::new(); - resources_map.retain(|&id, resource| { + resources_map.retain(|&index, resource| { let submit_index = resource.as_info().submission_index(); let non_referenced_resources = active .iter_mut() .find(|a| a.index == submit_index) .map(|a| &mut a.last_resources); - let is_removed = trackers.remove_abandoned(id); + let is_removed = trackers.remove_abandoned(index); if is_removed { removed_resources.push(resource.clone()); if let Some(resources) = non_referenced_resources { - get_resource_map(resources).insert(id, resource.clone()); + get_resource_map(resources).insert(index, resource.clone()); } } !is_removed @@ -459,27 +469,29 @@ impl LifetimeTracker { ); removed_resources.drain(..).for_each(|bundle| { for v in bundle.used.buffers.write().drain_resources() { - self.suspected_resources.buffers.insert(v.as_info().id(), v); + self.suspected_resources + .buffers + .insert(v.as_info().tracker_index(), v); } for v in bundle.used.textures.write().drain_resources() { self.suspected_resources .textures - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } for v in bundle.used.bind_groups.write().drain_resources() { self.suspected_resources .bind_groups - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } for v in bundle.used.render_pipelines.write().drain_resources() { self.suspected_resources .render_pipelines - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } for v in bundle.used.query_sets.write().drain_resources() { self.suspected_resources .query_sets - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } }); self @@ -496,27 +508,30 @@ impl LifetimeTracker { ); removed_resource.drain(..).for_each(|bind_group| { for v in bind_group.used.buffers.drain_resources() { - self.suspected_resources.buffers.insert(v.as_info().id(), v); + self.suspected_resources + .buffers + .insert(v.as_info().tracker_index(), v); } for v in bind_group.used.textures.drain_resources() { self.suspected_resources .textures - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } for v in bind_group.used.views.drain_resources() { self.suspected_resources .texture_views - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } for v in bind_group.used.samplers.drain_resources() { self.suspected_resources .samplers - .insert(v.as_info().id(), v); + .insert(v.as_info().tracker_index(), v); } - self.suspected_resources - .bind_group_layouts - .insert(bind_group.layout.as_info().id(), bind_group.layout.clone()); + self.suspected_resources.bind_group_layouts.insert( + bind_group.layout.as_info().tracker_index(), + bind_group.layout.clone(), + ); }); self } @@ -605,7 +620,7 @@ impl LifetimeTracker { ); removed_resources.drain(..).for_each(|compute_pipeline| { self.suspected_resources.pipeline_layouts.insert( - compute_pipeline.layout.as_info().id(), + compute_pipeline.layout.as_info().tracker_index(), compute_pipeline.layout.clone(), ); }); @@ -623,7 +638,7 @@ impl LifetimeTracker { ); removed_resources.drain(..).for_each(|render_pipeline| { self.suspected_resources.pipeline_layouts.insert( - render_pipeline.layout.as_info().id(), + render_pipeline.layout.as_info().tracker_index(), render_pipeline.layout.clone(), ); }); @@ -642,7 +657,7 @@ impl LifetimeTracker { for bgl in &pipeline_layout.bind_group_layouts { self.suspected_resources .bind_group_layouts - .insert(bgl.as_info().id(), bgl.clone()); + .insert(bgl.as_info().tracker_index(), bgl.clone()); } }); self @@ -773,14 +788,14 @@ impl LifetimeTracker { Vec::with_capacity(self.ready_to_map.len()); for buffer in self.ready_to_map.drain(..) { - let buffer_id = buffer.info.id(); + let tracker_index = buffer.info.tracker_index(); let is_removed = { let mut trackers = trackers.lock(); - trackers.buffers.remove_abandoned(buffer_id) + trackers.buffers.remove_abandoned(tracker_index) }; if is_removed { *buffer.map_state.lock() = resource::BufferMapState::Idle; - log::trace!("Buffer ready to map {:?} is not tracked anymore", buffer_id); + log::trace!("Buffer ready to map {tracker_index:?} is not tracked anymore"); } else { let mapping = match std::mem::replace( &mut *buffer.map_state.lock(), @@ -798,7 +813,7 @@ impl LifetimeTracker { _ => panic!("No pending mapping."), }; let status = if mapping.range.start != mapping.range.end { - log::debug!("Buffer {:?} map state -> Active", buffer_id); + log::debug!("Buffer {tracker_index:?} map state -> Active"); let host = mapping.op.host; let size = mapping.range.end - mapping.range.start; match super::map_buffer(raw, &buffer, mapping.range.start, size, host) { diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index a280abd9b3..5e8cb4be02 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -310,7 +310,10 @@ fn prepare_staging_buffer( raw: Mutex::new(Some(buffer)), device: device.clone(), size, - info: ResourceInfo::new(""), + info: ResourceInfo::new( + "", + Some(device.tracker_indices.staging_buffers.clone()), + ), is_coherent: mapping.is_coherent, }; @@ -1195,11 +1198,13 @@ impl Global { // update submission IDs for buffer in cmd_buf_trackers.buffers.used_resources() { - let id = buffer.info.id(); + let tracker_index = buffer.info.tracker_index(); let raw_buf = match buffer.raw.get(&snatch_guard) { Some(raw) => raw, None => { - return Err(QueueSubmitError::DestroyedBuffer(id)); + return Err(QueueSubmitError::DestroyedBuffer( + buffer.info.id(), + )); } }; buffer.info.use_at(submit_index); @@ -1214,19 +1219,25 @@ impl Global { .as_mut() .unwrap() .buffers - .insert(id, buffer.clone()); + .insert(tracker_index, buffer.clone()); } else { match *buffer.map_state.lock() { BufferMapState::Idle => (), - _ => return Err(QueueSubmitError::BufferStillMapped(id)), + _ => { + return Err(QueueSubmitError::BufferStillMapped( + buffer.info.id(), + )) + } } } } for texture in cmd_buf_trackers.textures.used_resources() { - let id = texture.info.id(); + let tracker_index = texture.info.tracker_index(); let should_extend = match texture.inner.get(&snatch_guard) { None => { - return Err(QueueSubmitError::DestroyedTexture(id)); + return Err(QueueSubmitError::DestroyedTexture( + texture.info.id(), + )); } Some(TextureInner::Native { .. }) => false, Some(TextureInner::Surface { ref raw, .. }) => { @@ -1243,7 +1254,7 @@ impl Global { .as_mut() .unwrap() .textures - .insert(id, texture.clone()); + .insert(tracker_index, texture.clone()); } if should_extend { unsafe { @@ -1256,11 +1267,10 @@ impl Global { for texture_view in cmd_buf_trackers.views.used_resources() { texture_view.info.use_at(submit_index); if texture_view.is_unique() { - temp_suspected - .as_mut() - .unwrap() - .texture_views - .insert(texture_view.as_info().id(), texture_view.clone()); + temp_suspected.as_mut().unwrap().texture_views.insert( + texture_view.as_info().tracker_index(), + texture_view.clone(), + ); } } { @@ -1280,7 +1290,7 @@ impl Global { .as_mut() .unwrap() .bind_groups - .insert(bg.as_info().id(), bg.clone()); + .insert(bg.as_info().tracker_index(), bg.clone()); } } } @@ -1291,7 +1301,7 @@ impl Global { compute_pipeline.info.use_at(submit_index); if compute_pipeline.is_unique() { temp_suspected.as_mut().unwrap().compute_pipelines.insert( - compute_pipeline.as_info().id(), + compute_pipeline.as_info().tracker_index(), compute_pipeline.clone(), ); } @@ -1302,7 +1312,7 @@ impl Global { render_pipeline.info.use_at(submit_index); if render_pipeline.is_unique() { temp_suspected.as_mut().unwrap().render_pipelines.insert( - render_pipeline.as_info().id(), + render_pipeline.as_info().tracker_index(), render_pipeline.clone(), ); } @@ -1310,11 +1320,10 @@ impl Global { for query_set in cmd_buf_trackers.query_sets.used_resources() { query_set.info.use_at(submit_index); if query_set.is_unique() { - temp_suspected - .as_mut() - .unwrap() - .query_sets - .insert(query_set.as_info().id(), query_set.clone()); + temp_suspected.as_mut().unwrap().query_sets.insert( + query_set.as_info().tracker_index(), + query_set.clone(), + ); } } for bundle in cmd_buf_trackers.bundles.used_resources() { @@ -1335,7 +1344,7 @@ impl Global { .as_mut() .unwrap() .render_bundles - .insert(bundle.as_info().id(), bundle.clone()); + .insert(bundle.as_info().tracker_index(), bundle.clone()); } } } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 6100130c43..a29770cb2e 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -28,7 +28,7 @@ use crate::{ resource_log, snatch::{SnatchGuard, SnatchLock, Snatchable}, storage::Storage, - track::{BindGroupStates, TextureSelector, Tracker}, + track::{BindGroupStates, TextureSelector, Tracker, TrackerIndexAllocators}, validation::{ self, check_buffer_usage, check_texture_usage, validate_color_attachment_bytes_per_sample, }, @@ -118,6 +118,7 @@ pub struct Device { /// Has to be locked temporarily only (locked last) /// and never before pending_writes pub(crate) trackers: Mutex>, + pub(crate) tracker_indices: TrackerIndexAllocators, // Life tracker should be locked right after the device and before anything else. life_tracker: Mutex>, /// Temporary storage for resource management functions. Cleared at the end @@ -263,13 +264,14 @@ impl Device { queue: OnceCell::new(), queue_to_drop: OnceCell::new(), zero_buffer: Some(zero_buffer), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), command_allocator: Mutex::new(Some(com_alloc)), active_submission_index: AtomicU64::new(0), fence: RwLock::new(Some(fence)), snatchable_lock: unsafe { SnatchLock::new() }, valid: AtomicBool::new(true), trackers: Mutex::new(Tracker::new()), + tracker_indices: TrackerIndexAllocators::new(), life_tracker: Mutex::new(life::LifetimeTracker::new()), temp_suspected: Mutex::new(Some(life::ResourceMaps::new())), bgl_pool: ResourcePool::new(), @@ -493,56 +495,56 @@ impl Device { if resource.is_unique() { temp_suspected .buffers - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.textures.used_resources() { if resource.is_unique() { temp_suspected .textures - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.views.used_resources() { if resource.is_unique() { temp_suspected .texture_views - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.bind_groups.used_resources() { if resource.is_unique() { temp_suspected .bind_groups - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.samplers.used_resources() { if resource.is_unique() { temp_suspected .samplers - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.compute_pipelines.used_resources() { if resource.is_unique() { temp_suspected .compute_pipelines - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.render_pipelines.used_resources() { if resource.is_unique() { temp_suspected .render_pipelines - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } for resource in trackers.query_sets.used_resources() { if resource.is_unique() { temp_suspected .query_sets - .insert(resource.as_info().id(), resource.clone()); + .insert(resource.as_info().tracker_index(), resource.clone()); } } } @@ -643,7 +645,10 @@ impl Device { initialization_status: RwLock::new(BufferInitTracker::new(aligned_size)), sync_mapped_writes: Mutex::new(None), map_state: Mutex::new(resource::BufferMapState::Idle), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.buffers.clone()), + ), bind_groups: Mutex::new(Vec::new()), }) } @@ -672,7 +677,10 @@ impl Device { mips: 0..desc.mip_level_count, layers: 0..desc.array_layer_count(), }, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.textures.clone()), + ), clear_mode: RwLock::new(clear_mode), views: Mutex::new(Vec::new()), bind_groups: Mutex::new(Vec::new()), @@ -694,7 +702,10 @@ impl Device { initialization_status: RwLock::new(BufferInitTracker::new(0)), sync_mapped_writes: Mutex::new(None), map_state: Mutex::new(resource::BufferMapState::Idle), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.buffers.clone()), + ), bind_groups: Mutex::new(Vec::new()), } } @@ -1272,7 +1283,10 @@ impl Device { render_extent, samples: texture.desc.sample_count, selector, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.texture_views.clone()), + ), }) } @@ -1376,7 +1390,10 @@ impl Device { Ok(Sampler { raw: Some(raw), device: self.clone(), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.samplers.clone()), + ), comparison: desc.compare.is_some(), filtering: desc.min_filter == wgt::FilterMode::Linear || desc.mag_filter == wgt::FilterMode::Linear, @@ -1569,7 +1586,7 @@ impl Device { raw: Some(raw), device: self.clone(), interface: Some(interface), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new(desc.label.borrow_or_default(), None), label: desc.label.borrow_or_default().to_string(), }) } @@ -1610,7 +1627,7 @@ impl Device { raw: Some(raw), device: self.clone(), interface: None, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new(desc.label.borrow_or_default(), None), label: desc.label.borrow_or_default().to_string(), }) } @@ -1863,7 +1880,10 @@ impl Device { entries: entry_map, origin, binding_count_validator: count_validator, - info: ResourceInfo::new(label.unwrap_or("")), + info: ResourceInfo::new( + label.unwrap_or(""), + Some(self.tracker_indices.bind_group_layouts.clone()), + ), label: label.unwrap_or_default().to_string(), }) } @@ -2296,7 +2316,10 @@ impl Device { raw: Snatchable::new(raw), device: self.clone(), layout: layout.clone(), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.bind_groups.clone()), + ), used, used_buffer_ranges, used_texture_ranges, @@ -2578,7 +2601,10 @@ impl Device { Ok(binding_model::PipelineLayout { raw: Some(raw), device: self.clone(), - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.pipeline_layouts.clone()), + ), bind_group_layouts, push_constant_ranges: desc.push_constant_ranges.iter().cloned().collect(), }) @@ -2743,7 +2769,10 @@ impl Device { device: self.clone(), _shader_module: shader_module, late_sized_buffer_groups, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.compute_pipelines.clone()), + ), }; Ok(pipeline) } @@ -3337,7 +3366,10 @@ impl Device { strip_index_format: desc.primitive.strip_index_format, vertex_steps, late_sized_buffer_groups, - info: ResourceInfo::new(desc.label.borrow_or_default()), + info: ResourceInfo::new( + desc.label.borrow_or_default(), + Some(self.tracker_indices.render_pipelines.clone()), + ), }; Ok(pipeline) } @@ -3450,7 +3482,7 @@ impl Device { Ok(QuerySet { raw: Some(unsafe { self.raw().create_query_set(&hal_desc).unwrap() }), device: self.clone(), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", Some(self.tracker_indices.query_sets.clone())), desc: desc.map_label(|_| ()), }) } diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 95d56ad1ff..b909245fac 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -198,7 +198,7 @@ impl Adapter { Self { raw, - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), } } @@ -303,7 +303,7 @@ impl Adapter { let queue = Queue { device: None, raw: Some(hal_device.queue), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), }; return Ok((device, queue)); } @@ -521,7 +521,7 @@ impl Global { let surface = Surface { presentation: Mutex::new(None), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), raw: hal_surface, }; @@ -542,7 +542,7 @@ impl Global { let surface = Surface { presentation: Mutex::new(None), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), raw: { let hal_surface = self .instance @@ -575,7 +575,7 @@ impl Global { let surface = Surface { presentation: Mutex::new(None), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), raw: { let hal_surface = self .instance @@ -604,7 +604,7 @@ impl Global { let surface = Surface { presentation: Mutex::new(None), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), raw: { let hal_surface = self .instance @@ -633,7 +633,7 @@ impl Global { let surface = Surface { presentation: Mutex::new(None), - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), raw: { let hal_surface = self .instance diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index ce270384a4..63052c6020 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -220,7 +220,7 @@ impl Global { layers: 0..1, mips: 0..1, }, - info: ResourceInfo::new(""), + info: ResourceInfo::new("", None), clear_mode: RwLock::new(resource::TextureClearMode::Surface { clear_view: Some(clear_view), }), @@ -236,7 +236,7 @@ impl Global { let mut trackers = device.trackers.lock(); trackers .textures - .insert_single(id, resource, hal::TextureUses::UNINITIALIZED); + .insert_single(resource, hal::TextureUses::UNINITIALIZED); } if present.acquired_texture.is_some() { @@ -313,10 +313,13 @@ impl Global { "Removing swapchain texture {:?} from the device tracker", texture_id ); - device.trackers.lock().textures.remove(texture_id); - let texture = hub.textures.unregister(texture_id); if let Some(texture) = texture { + device + .trackers + .lock() + .textures + .remove(texture.info.tracker_index()); let mut exclusive_snatch_guard = device.snatchable_lock.write(); let suf = A::get_surface(&surface); let mut inner = texture.inner_mut(&mut exclusive_snatch_guard); @@ -403,10 +406,15 @@ impl Global { "Removing swapchain texture {:?} from the device tracker", texture_id ); - device.trackers.lock().textures.remove(texture_id); let texture = hub.textures.unregister(texture_id); + if let Some(texture) = texture { + device + .trackers + .lock() + .textures + .remove(texture.info.tracker_index()); let suf = A::get_surface(&surface); let exclusive_snatch_guard = device.snatchable_lock.write(); match texture.inner.snatch(exclusive_snatch_guard).unwrap() { diff --git a/wgpu-core/src/registry.rs b/wgpu-core/src/registry.rs index f55809770b..80394351af 100644 --- a/wgpu-core/src/registry.rs +++ b/wgpu-core/src/registry.rs @@ -60,7 +60,6 @@ impl Registry { #[must_use] pub(crate) struct FutureId<'a, T: Resource> { id: Id, - identity: Arc>, data: &'a RwLock>, } @@ -75,7 +74,7 @@ impl FutureId<'_, T> { } pub fn init(&self, mut value: T) -> Arc { - value.as_info_mut().set_id(self.id, &self.identity); + value.as_info_mut().set_id(self.id); Arc::new(value) } @@ -117,7 +116,6 @@ impl Registry { } None => self.identity.process(self.backend), }, - identity: self.identity.clone(), data: &self.storage, } } @@ -125,7 +123,6 @@ impl Registry { pub(crate) fn request(&self) -> FutureId { FutureId { id: self.identity.process(self.backend), - identity: self.identity.clone(), data: &self.storage, } } @@ -142,11 +139,12 @@ impl Registry { self.storage.write() } pub fn unregister_locked(&self, id: Id, storage: &mut Storage) -> Option> { + self.identity.free(id); storage.remove(id) } pub fn force_replace(&self, id: Id, mut value: T) { let mut storage = self.storage.write(); - value.as_info_mut().set_id(id, &self.identity); + value.as_info_mut().set_id(id); storage.force_replace(id, value) } pub fn force_replace_with_error(&self, id: Id, label: &str) { @@ -155,6 +153,7 @@ impl Registry { storage.insert_error(id, label); } pub(crate) fn unregister(&self, id: Id) -> Option> { + self.identity.free(id); let value = self.storage.write().remove(id); //Returning None is legal if it's an error ID value diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 5108328f2b..aca077caab 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -9,11 +9,10 @@ use crate::{ global::Global, hal_api::HalApi, id::{AdapterId, BufferId, DeviceId, Id, Marker, SurfaceId, TextureId}, - identity::IdentityManager, init_tracker::{BufferInitTracker, TextureInitTracker}, resource, resource_log, snatch::{ExclusiveSnatchGuard, SnatchGuard, Snatchable}, - track::TextureSelector, + track::{SharedTrackerIndexAllocator, TextureSelector, TrackerIndex}, validation::MissingBufferUsageError, Label, SubmissionIndex, }; @@ -58,7 +57,8 @@ use std::{ #[derive(Debug)] pub struct ResourceInfo { id: Option>, - identity: Option>>, + tracker_index: TrackerIndex, + tracker_indices: Option>, /// The index of the last queue submission in which the resource /// was used. /// @@ -74,19 +74,26 @@ pub struct ResourceInfo { impl Drop for ResourceInfo { fn drop(&mut self) { - if let Some(identity) = self.identity.as_ref() { - let id = self.id.as_ref().unwrap(); - identity.free(*id); + if let Some(indices) = &self.tracker_indices { + indices.free(self.tracker_index); } } } impl ResourceInfo { #[allow(unused_variables)] - pub(crate) fn new(label: &str) -> Self { + pub(crate) fn new( + label: &str, + tracker_indices: Option>, + ) -> Self { + let tracker_index = tracker_indices + .as_ref() + .map(|indices| indices.alloc()) + .unwrap_or(TrackerIndex::INVALID); Self { id: None, - identity: None, + tracker_index, + tracker_indices, submission_index: AtomicUsize::new(0), label: label.to_string(), } @@ -111,9 +118,13 @@ impl ResourceInfo { self.id.unwrap() } - pub(crate) fn set_id(&mut self, id: Id, identity: &Arc>) { + pub(crate) fn tracker_index(&self) -> TrackerIndex { + debug_assert!(self.tracker_index != TrackerIndex::INVALID); + self.tracker_index + } + + pub(crate) fn set_id(&mut self, id: Id) { self.id = Some(id); - self.identity = Some(identity.clone()); } /// Record that this resource will be used by the queue submission with the @@ -551,6 +562,7 @@ impl Buffer { device: Arc::clone(&self.device), submission_index: self.info.submission_index(), id: self.info.id.unwrap(), + tracker_index: self.info.tracker_index(), label: self.info.label.clone(), bind_groups, })) @@ -611,6 +623,7 @@ pub struct DestroyedBuffer { device: Arc>, label: String, pub(crate) id: BufferId, + pub(crate) tracker_index: TrackerIndex, pub(crate) submission_index: u64, bind_groups: Vec>>, } @@ -885,6 +898,7 @@ impl Texture { views, bind_groups, device: Arc::clone(&self.device), + tracker_index: self.info.tracker_index(), submission_index: self.info.submission_index(), id: self.info.id.unwrap(), label: self.info.label.clone(), @@ -1002,6 +1016,7 @@ pub struct DestroyedTexture { device: Arc>, label: String, pub(crate) id: TextureId, + pub(crate) tracker_index: TrackerIndex, pub(crate) submission_index: u64, } diff --git a/wgpu-core/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs index e81d03792b..a30ac2a225 100644 --- a/wgpu-core/src/track/buffer.rs +++ b/wgpu-core/src/track/buffer.rs @@ -7,7 +7,7 @@ use std::{borrow::Cow, marker::PhantomData, sync::Arc}; -use super::{PendingTransition, ResourceTracker}; +use super::{PendingTransition, ResourceTracker, TrackerIndex}; use crate::{ hal_api::HalApi, id::BufferId, @@ -64,16 +64,16 @@ impl BufferBindGroupState { #[allow(clippy::pattern_type_mismatch)] pub(crate) fn optimize(&self) { let mut buffers = self.buffers.lock(); - buffers.sort_unstable_by_key(|(b, _)| b.as_info().id().unzip().0); + buffers.sort_unstable_by_key(|(b, _)| b.as_info().tracker_index()); } /// Returns a list of all buffers tracked. May contain duplicates. #[allow(clippy::pattern_type_mismatch)] - pub fn used_ids(&self) -> impl Iterator + '_ { + pub fn used_tracker_indices(&self) -> impl Iterator + '_ { let buffers = self.buffers.lock(); buffers .iter() - .map(|(ref b, _)| b.as_info().id()) + .map(|(ref b, _)| b.as_info().tracker_index()) .collect::>() .into_iter() } @@ -167,7 +167,7 @@ impl BufferUsageScope { ) -> Result<(), UsageConflict> { let buffers = bind_group.buffers.lock(); for &(ref resource, state) in &*buffers { - let index = resource.as_info().id().unzip().0 as usize; + let index = resource.as_info().tracker_index().as_usize(); unsafe { insert_or_merge( @@ -241,7 +241,7 @@ impl BufferUsageScope { .get(id) .map_err(|_| UsageConflict::BufferInvalid { id })?; - let index = id.unzip().0 as usize; + let index = buffer.info.tracker_index().as_usize(); self.allow_index(index); @@ -278,7 +278,7 @@ pub(crate) struct BufferTracker { temp: Vec>, } -impl ResourceTracker> for BufferTracker { +impl ResourceTracker for BufferTracker { /// Try to remove the buffer `id` from this tracker if it is otherwise unused. /// /// A buffer is 'otherwise unused' when the only references to it are: @@ -299,8 +299,8 @@ impl ResourceTracker> for BufferTracker { /// [`Device::trackers`]: crate::device::Device /// [`self.metadata`]: BufferTracker::metadata /// [`Hub::buffers`]: crate::hub::Hub::buffers - fn remove_abandoned(&mut self, id: BufferId) -> bool { - let index = id.unzip().0 as usize; + fn remove_abandoned(&mut self, index: TrackerIndex) -> bool { + let index = index.as_usize(); if index > self.metadata.size() { return false; @@ -315,16 +315,10 @@ impl ResourceTracker> for BufferTracker { //so it's already been released from user and so it's not inside Registry\Storage if existing_ref_count <= 2 { self.metadata.remove(index); - log::trace!("Buffer {:?} is not tracked anymore", id,); return true; - } else { - log::trace!( - "Buffer {:?} is still referenced from {}", - id, - existing_ref_count - ); - return false; } + + return false; } } true @@ -390,8 +384,8 @@ impl BufferTracker { /// /// 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, id: BufferId, resource: Arc>, state: BufferUses) { - let index = id.unzip().0 as usize; + pub fn insert_single(&mut self, resource: Arc>, state: BufferUses) { + let index = resource.info.tracker_index().as_usize(); self.allow_index(index); @@ -426,7 +420,7 @@ impl BufferTracker { /// 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 set_single(&mut self, buffer: &Arc>, state: BufferUses) -> SetSingleResult { - let index: usize = buffer.as_info().id().unzip().0 as usize; + let index: usize = buffer.as_info().tracker_index().as_usize(); self.allow_index(index); @@ -547,16 +541,15 @@ impl BufferTracker { pub unsafe fn set_and_remove_from_usage_scope_sparse( &mut self, scope: &mut BufferUsageScope, - id_source: impl IntoIterator, + index_source: impl IntoIterator, ) { let incoming_size = scope.state.len(); if incoming_size > self.start.len() { self.set_size(incoming_size); } - for id in id_source { - let (index32, _, _) = id.unzip(); - let index = index32 as usize; + for index in index_source { + let index = index.as_usize(); scope.tracker_assert_in_bounds(index); @@ -585,8 +578,8 @@ impl BufferTracker { } #[allow(dead_code)] - pub fn get(&self, id: BufferId) -> Option<&Arc>> { - let index = id.unzip().0 as usize; + pub fn get(&self, index: TrackerIndex) -> Option<&Arc>> { + let index = index.as_usize(); if index > self.metadata.size() { return None; } @@ -771,11 +764,7 @@ unsafe fn merge( if invalid_resource_state(merged_state) { return Err(UsageConflict::from_buffer( - BufferId::zip( - index32, - unsafe { metadata_provider.get_epoch(index) }, - A::VARIANT, - ), + unsafe { metadata_provider.get_own(index).info.id() }, *current_state, new_state, )); diff --git a/wgpu-core/src/track/metadata.rs b/wgpu-core/src/track/metadata.rs index e5f4d5e969..744783a7fa 100644 --- a/wgpu-core/src/track/metadata.rs +++ b/wgpu-core/src/track/metadata.rs @@ -1,6 +1,6 @@ //! The `ResourceMetadata` type. -use crate::{resource::Resource, Epoch}; +use crate::resource::Resource; use bit_vec::BitVec; use std::{borrow::Cow, mem, sync::Arc}; use wgt::strict_assert; @@ -194,15 +194,6 @@ impl ResourceMetadataProvider<'_, T> { } } } - /// Get the epoch from this. - /// - /// # Safety - /// - /// - The index must be in bounds of the metadata tracker if this uses an indirect source. - #[inline(always)] - pub(super) unsafe fn get_epoch(self, index: usize) -> Epoch { - unsafe { self.get_own(index).as_info().id().unzip().1 } - } } /// Resizes the given bitvec to the given size. I'm not sure why this is hard to do but it is. diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index a36280d03b..c0895c44a6 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -102,16 +102,12 @@ mod stateless; mod texture; use crate::{ - binding_model, command, conv, - hal_api::HalApi, - id::{self, Id}, - pipeline, resource, - snatch::SnatchGuard, + binding_model, command, conv, hal_api::HalApi, id, pipeline, resource, snatch::SnatchGuard, storage::Storage, }; -use parking_lot::RwLock; -use std::{fmt, ops}; +use parking_lot::{Mutex, RwLock}; +use std::{fmt, ops, sync::Arc}; use thiserror::Error; pub(crate) use buffer::{BufferBindGroupState, BufferTracker, BufferUsageScope}; @@ -122,6 +118,130 @@ pub(crate) use texture::{ }; use wgt::strict_assert_ne; +#[repr(transparent)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) struct TrackerIndex(u32); + +impl TrackerIndex { + /// A dummy value to place in ResourceInfo for resources that are never tracked. + pub const INVALID: Self = TrackerIndex(u32::MAX); + + pub fn as_usize(self) -> usize { + debug_assert!(self != Self::INVALID); + self.0 as usize + } +} + +/// wgpu-core internally use some array-like storage for tracking resources. +/// To that end, there needs to be a uniquely assigned index for each live resource +/// of a certain type. This index is separate from the resource ID for various reasons: +/// - There can be multiple resource IDs pointing the the same resource. +/// - IDs of dead handles can be recycled while resources are internally held alive (and tracked). +/// - The plan is to remove IDs in the long run (https://github.com/gfx-rs/wgpu/issues/5121). +/// In order to produce these tracker indices, there is a shared TrackerIndexAllocator +/// per resource type. Indices have the same lifetime as the internal resource they +/// are associated to (alloc happens when creating the resource and free is called when +/// the resource is dropped). +struct TrackerIndexAllocator { + unused: Vec, + next_index: TrackerIndex, +} + +impl TrackerIndexAllocator { + pub fn new() -> Self { + TrackerIndexAllocator { + unused: Vec::new(), + next_index: TrackerIndex(0), + } + } + + pub fn alloc(&mut self) -> TrackerIndex { + if let Some(index) = self.unused.pop() { + return index; + } + + let index = self.next_index; + self.next_index.0 += 1; + + index + } + + pub fn free(&mut self, index: TrackerIndex) { + self.unused.push(index); + } + + // This is used to pre-allocate the tracker storage. + pub fn size(&self) -> usize { + self.next_index.0 as usize + } +} + +impl std::fmt::Debug for TrackerIndexAllocator { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Ok(()) + } +} + +/// See TrackerIndexAllocator. +#[derive(Debug)] +pub(crate) struct SharedTrackerIndexAllocator { + inner: Mutex, +} + +impl SharedTrackerIndexAllocator { + pub fn new() -> Self { + SharedTrackerIndexAllocator { + inner: Mutex::new(TrackerIndexAllocator::new()), + } + } + + pub fn alloc(&self) -> TrackerIndex { + self.inner.lock().alloc() + } + + pub fn free(&self, index: TrackerIndex) { + self.inner.lock().free(index); + } + + pub fn size(&self) -> usize { + self.inner.lock().size() + } +} + +pub(crate) struct TrackerIndexAllocators { + pub buffers: Arc, + pub staging_buffers: Arc, + pub textures: Arc, + pub texture_views: Arc, + pub samplers: Arc, + pub bind_groups: Arc, + pub bind_group_layouts: Arc, + pub compute_pipelines: Arc, + pub render_pipelines: Arc, + pub pipeline_layouts: Arc, + pub bundles: Arc, + pub query_sets: Arc, +} + +impl TrackerIndexAllocators { + pub fn new() -> Self { + TrackerIndexAllocators { + buffers: Arc::new(SharedTrackerIndexAllocator::new()), + staging_buffers: Arc::new(SharedTrackerIndexAllocator::new()), + textures: Arc::new(SharedTrackerIndexAllocator::new()), + texture_views: Arc::new(SharedTrackerIndexAllocator::new()), + samplers: Arc::new(SharedTrackerIndexAllocator::new()), + bind_groups: Arc::new(SharedTrackerIndexAllocator::new()), + bind_group_layouts: Arc::new(SharedTrackerIndexAllocator::new()), + compute_pipelines: Arc::new(SharedTrackerIndexAllocator::new()), + render_pipelines: Arc::new(SharedTrackerIndexAllocator::new()), + pipeline_layouts: Arc::new(SharedTrackerIndexAllocator::new()), + bundles: Arc::new(SharedTrackerIndexAllocator::new()), + query_sets: Arc::new(SharedTrackerIndexAllocator::new()), + } + } +} + /// A structure containing all the information about a particular resource /// transition. User code should be able to generate a pipeline barrier /// based on the contents. @@ -420,17 +540,14 @@ pub(crate) struct UsageScope { impl UsageScope { /// Create the render bundle scope and pull the maximum IDs from the hubs. - pub fn new( - buffers: &Storage>, - textures: &Storage>, - ) -> Self { + pub fn new(tracker_indices: &TrackerIndexAllocators) -> Self { let mut value = Self { buffers: BufferUsageScope::new(), textures: TextureUsageScope::new(), }; - value.buffers.set_size(buffers.len()); - value.textures.set_size(textures.len()); + value.buffers.set_size(tracker_indices.buffers.size()); + value.textures.set_size(tracker_indices.textures.size()); value } @@ -478,11 +595,8 @@ impl UsageScope { } } -pub(crate) trait ResourceTracker -where - R: resource::Resource, -{ - fn remove_abandoned(&mut self, id: Id) -> bool; +pub(crate) trait ResourceTracker { + fn remove_abandoned(&mut self, index: TrackerIndex) -> bool; } /// A full double sided tracker used by CommandBuffers and the Device. @@ -513,48 +627,6 @@ impl Tracker { } } - /// Pull the maximum IDs from the hubs. - pub fn set_size( - &mut self, - buffers: Option<&Storage>>, - textures: Option<&Storage>>, - views: Option<&Storage>>, - samplers: Option<&Storage>>, - bind_groups: Option<&Storage>>, - compute_pipelines: Option<&Storage>>, - render_pipelines: Option<&Storage>>, - bundles: Option<&Storage>>, - query_sets: Option<&Storage>>, - ) { - if let Some(buffers) = buffers { - self.buffers.set_size(buffers.len()); - }; - if let Some(textures) = textures { - self.textures.set_size(textures.len()); - }; - if let Some(views) = views { - self.views.set_size(views.len()); - }; - if let Some(samplers) = samplers { - self.samplers.set_size(samplers.len()); - }; - if let Some(bind_groups) = bind_groups { - self.bind_groups.set_size(bind_groups.len()); - }; - if let Some(compute_pipelines) = compute_pipelines { - self.compute_pipelines.set_size(compute_pipelines.len()); - } - if let Some(render_pipelines) = render_pipelines { - self.render_pipelines.set_size(render_pipelines.len()); - }; - if let Some(bundles) = bundles { - self.bundles.set_size(bundles.len()); - }; - if let Some(query_sets) = query_sets { - self.query_sets.set_size(query_sets.len()); - }; - } - /// Iterates through all resources in the given bind group and adopts /// the state given for those resources in the UsageScope. It also /// removes all touched resources from the usage scope. @@ -585,7 +657,7 @@ impl Tracker { unsafe { self.buffers.set_and_remove_from_usage_scope_sparse( &mut scope.buffers, - bind_group.buffers.used_ids(), + bind_group.buffers.used_tracker_indices(), ) }; unsafe { diff --git a/wgpu-core/src/track/stateless.rs b/wgpu-core/src/track/stateless.rs index 31441b0006..00225f2305 100644 --- a/wgpu-core/src/track/stateless.rs +++ b/wgpu-core/src/track/stateless.rs @@ -10,7 +10,7 @@ use parking_lot::Mutex; use crate::{id::Id, resource::Resource, resource_log, storage::Storage, track::ResourceMetadata}; -use super::ResourceTracker; +use super::{ResourceTracker, TrackerIndex}; /// Satisfy clippy. type Pair = (Id<::Marker>, Arc); @@ -74,7 +74,7 @@ pub(crate) struct StatelessTracker { metadata: ResourceMetadata, } -impl ResourceTracker for StatelessTracker { +impl ResourceTracker for StatelessTracker { /// Try to remove the given resource from the tracker iff we have the last reference to the /// resource and the epoch matches. /// @@ -82,14 +82,14 @@ impl ResourceTracker for StatelessTracker { /// /// If the ID is higher than the length of internal vectors, /// false will be returned. - fn remove_abandoned(&mut self, id: Id) -> bool { - let index = id.unzip().0 as usize; + fn remove_abandoned(&mut self, index: TrackerIndex) -> bool { + let index = index.as_usize(); if index >= self.metadata.size() { return false; } - resource_log!("StatelessTracker::remove_abandoned {id:?}"); + resource_log!("StatelessTracker::remove_abandoned {index:?}"); self.tracker_assert_in_bounds(index); @@ -100,17 +100,10 @@ impl ResourceTracker for StatelessTracker { //so it's already been released from user and so it's not inside Registry\Storage if existing_ref_count <= 2 { self.metadata.remove(index); - log::trace!("{} {:?} is not tracked anymore", T::TYPE, id,); return true; - } else { - log::trace!( - "{} {:?} is still referenced from {}", - T::TYPE, - id, - existing_ref_count - ); - return false; } + + return false; } } true @@ -160,9 +153,8 @@ impl StatelessTracker { /// /// 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, id: Id, resource: Arc) { - let (index32, _epoch, _) = id.unzip(); - let index = index32 as usize; + pub fn insert_single(&mut self, resource: Arc) { + let index = resource.as_info().tracker_index().as_usize(); self.allow_index(index); @@ -184,8 +176,7 @@ impl StatelessTracker { ) -> Option<&'a Arc> { let resource = storage.get(id).ok()?; - let (index32, _epoch, _) = id.unzip(); - let index = index32 as usize; + let index = resource.as_info().tracker_index().as_usize(); self.allow_index(index); diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index 601df11e1b..e7c4707c93 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -19,10 +19,11 @@ * will treat the contents as junk. !*/ -use super::{range::RangedStates, PendingTransition, PendingTransitionList, ResourceTracker}; +use super::{ + range::RangedStates, PendingTransition, PendingTransitionList, ResourceTracker, TrackerIndex, +}; use crate::{ hal_api::HalApi, - id::TextureId, resource::{Resource, Texture, TextureInner}, snatch::SnatchGuard, track::{ @@ -173,7 +174,7 @@ impl TextureBindGroupState { /// accesses will be in a constant ascending order. pub(crate) fn optimize(&self) { let mut textures = self.textures.lock(); - textures.sort_unstable_by_key(|v| v.texture.as_info().id().unzip().0); + textures.sort_unstable_by_key(|v| v.texture.as_info().tracker_index()); } /// Returns a list of all textures tracked. May contain duplicates. @@ -359,7 +360,7 @@ impl TextureUsageScope { selector: Option, new_state: TextureUses, ) -> Result<(), UsageConflict> { - let index = texture.as_info().id().unzip().0 as usize; + let index = texture.as_info().tracker_index().as_usize(); self.tracker_assert_in_bounds(index); @@ -393,7 +394,7 @@ pub(crate) struct TextureTracker { _phantom: PhantomData, } -impl ResourceTracker> for TextureTracker { +impl ResourceTracker for TextureTracker { /// Try to remove the given resource from the tracker iff we have the last reference to the /// resource and the epoch matches. /// @@ -401,10 +402,10 @@ impl ResourceTracker> for TextureTracker { /// /// If the ID is higher than the length of internal vectors, /// false will be returned. - fn remove_abandoned(&mut self, id: TextureId) -> bool { - let index = id.unzip().0 as usize; + fn remove_abandoned(&mut self, index: TrackerIndex) -> bool { + let index = index.as_usize(); - if index > self.metadata.size() { + if index >= self.metadata.size() { return false; } @@ -419,16 +420,10 @@ impl ResourceTracker> for TextureTracker { self.start_set.complex.remove(&index); self.end_set.complex.remove(&index); self.metadata.remove(index); - log::trace!("Texture {:?} is not tracked anymore", id,); return true; - } else { - log::trace!( - "Texture {:?} is still referenced from {}", - id, - existing_ref_count - ); - return false; } + + return false; } } true @@ -518,8 +513,8 @@ impl TextureTracker { /// /// 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, id: TextureId, resource: Arc>, usage: TextureUses) { - let index = id.unzip().0 as usize; + pub fn insert_single(&mut self, resource: Arc>, usage: TextureUses) { + let index = resource.info.tracker_index().as_usize(); self.allow_index(index); @@ -560,7 +555,7 @@ impl TextureTracker { selector: TextureSelector, new_state: TextureUses, ) -> Option>> { - let index = texture.as_info().id().unzip().0 as usize; + let index = texture.as_info().tracker_index().as_usize(); self.allow_index(index); @@ -694,7 +689,7 @@ impl TextureTracker { let textures = bind_group_state.textures.lock(); for t in textures.iter() { - let index = t.texture.as_info().id().unzip().0 as usize; + let index = t.texture.as_info().tracker_index().as_usize(); scope.tracker_assert_in_bounds(index); if unsafe { !scope.metadata.contains_unchecked(index) } { @@ -727,10 +722,10 @@ impl TextureTracker { /// /// If the ID is higher than the length of internal vectors, /// false will be returned. - pub fn remove(&mut self, id: TextureId) -> bool { - let index = id.unzip().0 as usize; + pub fn remove(&mut self, index: TrackerIndex) -> bool { + let index = index.as_usize(); - if index > self.metadata.size() { + if index >= self.metadata.size() { return false; } @@ -1080,11 +1075,7 @@ unsafe fn merge( if invalid_resource_state(merged_state) { return Err(UsageConflict::from_texture( - TextureId::zip( - index as _, - unsafe { metadata_provider.get_epoch(index) }, - A::VARIANT, - ), + unsafe { metadata_provider.get_own(index).info.id() }, texture_selector.clone(), *current_simple, new_simple, @@ -1111,11 +1102,7 @@ unsafe fn merge( if invalid_resource_state(merged_state) { return Err(UsageConflict::from_texture( - TextureId::zip( - index as _, - unsafe { metadata_provider.get_epoch(index) }, - A::VARIANT, - ), + unsafe { metadata_provider.get_own(index).info.id() }, selector, *current_simple, new_state, @@ -1156,11 +1143,7 @@ unsafe fn merge( if invalid_resource_state(merged_state) { return Err(UsageConflict::from_texture( - TextureId::zip( - index as _, - unsafe { metadata_provider.get_epoch(index) }, - A::VARIANT, - ), + unsafe { metadata_provider.get_own(index).info.id() }, TextureSelector { mips: mip_id..mip_id + 1, layers: layers.clone(), @@ -1201,11 +1184,7 @@ unsafe fn merge( if invalid_resource_state(merged_state) { return Err(UsageConflict::from_texture( - TextureId::zip( - index as _, - unsafe { metadata_provider.get_epoch(index) }, - A::VARIANT, - ), + unsafe { metadata_provider.get_own(index).info.id() }, TextureSelector { mips: mip_id..mip_id + 1, layers: layers.clone(),