diff --git a/tests/tests/mem_leaks.rs b/tests/tests/mem_leaks.rs index 56109faf5c..af7eb1766c 100644 --- a/tests/tests/mem_leaks.rs +++ b/tests/tests/mem_leaks.rs @@ -238,14 +238,13 @@ fn draw_test_with_reports( assert_eq!(report.textures.num_allocated, 1); let submit_index = ctx.queue.submit(Some(encoder.finish())); - ctx.device - .poll(wgpu::Maintain::WaitForSubmissionIndex(submit_index)); let global_report = ctx.instance.generate_report(); let report = global_report.hub_report(); assert_eq!(report.command_buffers.num_allocated, 0); - ctx.device.poll(wgpu::Maintain::Wait); + ctx.device + .poll(wgpu::Maintain::WaitForSubmissionIndex(submit_index)); let global_report = ctx.instance.generate_report(); let report = global_report.hub_report(); diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index e4d389703e..2a4e3084a7 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -7,7 +7,7 @@ use crate::{ TextureViewId, }, init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction}, - resource::{Resource, ResourceInfo}, + resource::{Resource, ResourceInfo, ResourceType}, track::{BindGroupStates, UsageConflict}, validation::{MissingBufferUsageError, MissingTextureUsageError}, FastHashMap, Label, @@ -481,7 +481,7 @@ impl Drop for BindGroupLayout { } impl Resource for BindGroupLayout { - const TYPE: &'static str = "BindGroupLayout"; + const TYPE: ResourceType = "BindGroupLayout"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -730,7 +730,7 @@ impl PipelineLayout { } impl Resource for PipelineLayout { - const TYPE: &'static str = "PipelineLayout"; + const TYPE: ResourceType = "PipelineLayout"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -921,7 +921,7 @@ impl BindGroup { } impl Resource for BindGroup { - const TYPE: &'static str = "BindGroup"; + const TYPE: ResourceType = "BindGroup"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 4bc47c7414..e476a5ecf8 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -95,7 +95,7 @@ use crate::{ id::{self, RenderBundleId}, init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction}, pipeline::{self, PipelineFlags, RenderPipeline}, - resource::{Resource, ResourceInfo}, + resource::{Resource, ResourceInfo, ResourceType}, track::RenderBundleScope, validation::check_buffer_usage, Label, LabelHelpers, @@ -952,7 +952,7 @@ impl RenderBundle { } impl Resource for RenderBundle { - const TYPE: &'static str = "RenderBundle"; + const TYPE: ResourceType = "RenderBundle"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 672e81bb90..0f40478d6e 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -24,7 +24,7 @@ use crate::hub::Hub; use crate::id::CommandBufferId; use crate::init_tracker::BufferInitTrackerAction; -use crate::resource::{Resource, ResourceInfo}; +use crate::resource::{Resource, ResourceInfo, ResourceType}; use crate::track::{Tracker, UsageScope}; use crate::{global::Global, hal_api::HalApi, id, identity::GlobalIdentityHandlerFactory, Label}; @@ -278,7 +278,7 @@ impl CommandBuffer { } impl Resource for CommandBuffer { - const TYPE: &'static str = "CommandBuffer"; + const TYPE: ResourceType = "CommandBuffer"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 8a469a40be..8c90b141a7 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -486,7 +486,6 @@ impl Global { device .lock_life() .suspected_resources - .buffers .insert(buffer_id, buffer); } @@ -731,7 +730,6 @@ impl Global { } else { life_lock .suspected_resources - .textures .insert(texture_id, texture.clone()); } } @@ -807,7 +805,6 @@ impl Global { view.device .lock_life() .suspected_resources - .texture_views .insert(texture_view_id, view.clone()); if wait { @@ -876,7 +873,6 @@ impl Global { .device .lock_life() .suspected_resources - .samplers .insert(sampler_id, sampler.clone()); } } @@ -965,7 +961,6 @@ impl Global { .device .lock_life() .suspected_resources - .bind_group_layouts .insert(bind_group_layout_id, layout.clone()); } } @@ -1028,7 +1023,6 @@ impl Global { .device .lock_life() .suspected_resources - .pipeline_layouts .insert(pipeline_layout_id, layout.clone()); } } @@ -1099,7 +1093,6 @@ impl Global { .device .lock_life() .suspected_resources - .bind_groups .insert(bind_group_id, bind_group.clone()); } } @@ -1376,7 +1369,6 @@ impl Global { .device .lock_life() .suspected_resources - .render_bundles .insert(render_bundle_id, bundle.clone()); } } @@ -1442,7 +1434,6 @@ impl Global { device .lock_life() .suspected_resources - .query_sets .insert(query_set_id, query_set.clone()); } } @@ -1558,12 +1549,10 @@ impl Global { let mut life_lock = device.lock_life(); life_lock .suspected_resources - .render_pipelines .insert(render_pipeline_id, pipeline.clone()); life_lock .suspected_resources - .pipeline_layouts .insert(layout_id, pipeline.layout.clone()); } } @@ -1676,11 +1665,9 @@ impl Global { 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()); } } diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 12f56d1ceb..bbcb0eb643 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -9,79 +9,135 @@ use crate::{ }, hal_api::HalApi, hub::Hub, - id::{self}, + id::{ + self, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, PipelineLayoutId, + QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, StagingBufferId, TextureId, + TextureViewId, + }, pipeline::{ComputePipeline, RenderPipeline}, - resource::{self, Buffer, QuerySet, Resource, Sampler, StagingBuffer, Texture, TextureView}, - track::Tracker, - SubmissionIndex, + registry::Registry, + resource::{ + self, Buffer, QuerySet, Resource, ResourceType, Sampler, StagingBuffer, Texture, + TextureView, + }, + track::{ResourceTracker, Tracker}, + FastHashMap, SubmissionIndex, }; use smallvec::SmallVec; use parking_lot::Mutex; use thiserror::Error; +use wgt::{WasmNotSend, WasmNotSync}; -use std::{collections::HashMap, sync::Arc}; +use std::{any::Any, collections::HashMap, sync::Arc}; -/// A struct that keeps lists of resources that are no longer needed by the user. -pub(crate) struct ResourceMaps { - pub(crate) buffers: HashMap>>, - pub(crate) staging_buffers: HashMap>>, - pub(crate) textures: HashMap>>, - pub(crate) texture_views: HashMap>>, - pub(crate) samplers: HashMap>>, - pub(crate) bind_groups: HashMap>>, - pub(crate) compute_pipelines: HashMap>>, - pub(crate) render_pipelines: HashMap>>, - pub(crate) bind_group_layouts: HashMap>>, - pub(crate) pipeline_layouts: HashMap>>, - pub(crate) render_bundles: HashMap>>, - pub(crate) query_sets: HashMap>>, +pub(crate) trait ResourceMap: Any + WasmNotSend + WasmNotSync { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; + fn clear_map(&mut self); + fn extend_map(&mut self, maps: &mut ResourceMaps); } -impl ResourceMaps { - pub(crate) fn new() -> Self { - Self { - buffers: HashMap::new(), - staging_buffers: HashMap::new(), - textures: HashMap::new(), - texture_views: HashMap::new(), - samplers: HashMap::new(), - bind_groups: HashMap::new(), - compute_pipelines: HashMap::new(), - render_pipelines: HashMap::new(), - bind_group_layouts: HashMap::new(), - pipeline_layouts: HashMap::new(), - render_bundles: HashMap::new(), - query_sets: HashMap::new(), +impl ResourceMap for HashMap> +where + Id: id::TypedId, + R: Resource, +{ + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn clear_map(&mut self) { + self.clear() + } + fn extend_map(&mut self, r: &mut ResourceMaps) { + if let Some(other) = r.maps.get_mut(R::TYPE) { + if let Some(other) = other.as_any_mut().downcast_mut::() { + self.extend(other.drain()); + } } } +} + +/// A struct that keeps lists of resources that are no longer needed by the user. +#[derive(Default)] +pub(crate) struct ResourceMaps { + pub(crate) maps: FastHashMap>, +} + +impl ResourceMaps { + fn add_type(&mut self) -> &mut Self + where + Id: id::TypedId, + R: Resource, + { + let map = HashMap::>::default(); + self.maps.insert(R::TYPE, Box::new(map)); + self + } + fn map(&self) -> &HashMap> + where + Id: id::TypedId, + R: Resource, + { + let map = self.maps.get(R::TYPE).unwrap(); + let any_map = map.as_ref().as_any(); + let map = any_map.downcast_ref::>>().unwrap(); + map + } + fn map_mut(&mut self) -> &mut HashMap> + where + Id: id::TypedId, + R: Resource, + { + let map = self + .maps + .entry(R::TYPE) + .or_insert_with(|| Box::>>::new(HashMap::default())); + let any_map = map.as_mut().as_any_mut(); + let map = any_map.downcast_mut::>>().unwrap(); + map + } + pub(crate) fn new() -> Self { + let mut maps = Self::default(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps.add_type::>(); + maps + } pub(crate) fn clear(&mut self) { - self.render_bundles.clear(); - self.bind_groups.clear(); - self.compute_pipelines.clear(); - self.render_pipelines.clear(); - self.bind_group_layouts.clear(); - self.pipeline_layouts.clear(); - self.texture_views.clear(); - self.samplers.clear(); - self.staging_buffers.clear(); - self.query_sets.clear(); - self.textures.clear(); - self.buffers.clear(); - } - - pub(crate) fn extend(&mut self, other: Self) { - self.buffers.extend(other.buffers); - self.staging_buffers.extend(other.staging_buffers); - self.textures.extend(other.textures); - self.texture_views.extend(other.texture_views); - self.samplers.extend(other.samplers); - self.bind_groups.extend(other.bind_groups); - self.compute_pipelines.extend(other.compute_pipelines); - self.render_pipelines.extend(other.render_pipelines); - self.bind_group_layouts.extend(other.bind_group_layouts); - self.pipeline_layouts.extend(other.pipeline_layouts); - self.query_sets.extend(other.query_sets); + self.maps.iter_mut().for_each(|(_t, map)| map.clear_map()); + } + pub(crate) fn extend(&mut self, mut other: Self) { + self.maps.iter_mut().for_each(|(_t, map)| { + map.extend_map(&mut other); + }); + } + pub(crate) fn insert(&mut self, id: Id, r: Arc) -> &mut Self + where + Id: id::TypedId, + R: Resource, + { + self.map_mut().insert(id, r); + self + } + pub(crate) fn contains(&mut self, id: &Id) -> bool + where + Id: id::TypedId, + R: Resource, + { + self.map::().contains_key(id) } } @@ -103,7 +159,7 @@ struct ActiveSubmission { /// This includes things like temporary resources and resources that are /// used by submitted commands but have been dropped by the user (meaning that /// this submission is their last reference.) - last_resources: ResourceMaps, + last_resources: ResourceMaps, /// Buffers to be mapped once this submission has completed. mapped: Vec>>, @@ -177,7 +233,7 @@ pub(crate) struct LifetimeTracker { /// Resources whose user handle has died (i.e. drop/destroy has been called) /// and will likely be ready for destruction soon. - pub suspected_resources: ResourceMaps, + pub suspected_resources: ResourceMaps, /// Resources used by queue submissions still in flight. One entry per /// submission, with older submissions appearing before younger. @@ -192,7 +248,7 @@ pub(crate) struct LifetimeTracker { /// These are freed by `LifeTracker::cleanup`, which is called from periodic /// maintenance functions like `Global::device_poll`, and when a device is /// destroyed. - free_resources: ResourceMaps, + free_resources: ResourceMaps, /// Buffers the user has asked us to map, and which are not used by any /// queue submission still in flight. @@ -211,9 +267,9 @@ impl LifetimeTracker { mapped: Vec::new(), future_suspected_buffers: Vec::new(), future_suspected_textures: Vec::new(), - suspected_resources: ResourceMaps::new(), + suspected_resources: ResourceMaps::new::(), active: Vec::new(), - free_resources: ResourceMaps::new(), + free_resources: ResourceMaps::new::(), ready_to_map: Vec::new(), work_done_closures: SmallVec::new(), } @@ -231,19 +287,17 @@ impl LifetimeTracker { temp_resources: impl Iterator>, encoders: Vec>, ) { - let mut last_resources = ResourceMaps::new(); + let mut last_resources = ResourceMaps::new::(); for res in temp_resources { match res { TempResource::Buffer(raw) => { - last_resources.buffers.insert(raw.as_info().id(), raw); + last_resources.insert(raw.as_info().id(), raw); } TempResource::StagingBuffer(raw) => { - last_resources - .staging_buffers - .insert(raw.as_info().id(), raw); + last_resources.insert(raw.as_info().id(), raw); } TempResource::Texture(raw) => { - last_resources.textures.insert(raw.as_info().id(), raw); + last_resources.insert(raw.as_info().id(), raw); } } } @@ -259,12 +313,10 @@ 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.insert(v.as_info().id(), v); } for v in self.future_suspected_textures.drain(..).take(1) { - self.suspected_resources - .textures - .insert(v.as_info().id(), v); + self.suspected_resources.insert(v.as_info().id(), v); } } @@ -340,13 +392,13 @@ impl LifetimeTracker { .map_or(&mut self.free_resources, |a| &mut a.last_resources); match temp_resource { TempResource::Buffer(raw) => { - resources.buffers.insert(raw.as_info().id(), raw); + resources.insert(raw.as_info().id(), raw); } TempResource::StagingBuffer(raw) => { - resources.staging_buffers.insert(raw.as_info().id(), raw); + resources.insert(raw.as_info().id(), raw); } TempResource::Texture(raw) => { - resources.textures.insert(raw.as_info().id(), raw); + resources.insert(raw.as_info().id(), raw); } } } @@ -366,392 +418,339 @@ impl LifetimeTracker { } impl LifetimeTracker { - fn triage_suspected_render_bundles( - &mut self, - hub: &Hub, - trackers: &Mutex>, + fn triage_resources( + resources_map: &mut HashMap>, + active: &mut [ActiveSubmission], + free_resources: &mut ResourceMaps, + trackers: &mut impl ResourceTracker, + registry: &Registry, mut f: F, - ) -> &mut Self + ) -> Vec> where - F: FnMut(&id::RenderBundleId), + Id: id::TypedId, + R: Resource, + F: FnMut(&Id, &Arc), { - self.suspected_resources - .render_bundles - .retain(|&bundle_id, bundle| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .bundles - .remove_abandoned(bundle_id, hub.render_bundles.contains(bundle_id)); + let mut removed_resources = Vec::new(); + resources_map.retain(|&id, resource| { + let submit_index = resource.as_info().submission_index(); + let non_referenced_resources = active + .iter_mut() + .find(|a| a.index == submit_index) + .map_or(&mut *free_resources, |a| &mut a.last_resources); - f(&bundle_id); + let mut count = 1; + count += registry.contains(id) as usize; + count += non_referenced_resources.contains::(&id) as usize; + let is_removed = trackers.remove_abandoned(id, count); + if is_removed { + f(&id, resource); + removed_resources.push(resource.clone()); + non_referenced_resources.insert(id, resource.clone()); + } + !is_removed + }); + removed_resources + } - for v in bundle.used.buffers.write().drain_resources() { - self.suspected_resources - .buffers - .insert(v.as_info().id(), v.clone()); - } - for v in bundle.used.textures.write().drain_resources() { - self.suspected_resources - .textures - .insert(v.as_info().id(), v.clone()); - } - for v in bundle.used.bind_groups.write().drain_resources() { - self.suspected_resources - .bind_groups - .insert(v.as_info().id(), v.clone()); - } - for v in bundle.used.render_pipelines.write().drain_resources() { - self.suspected_resources - .render_pipelines - .insert(v.as_info().id(), v.clone()); - } - for v in bundle.used.query_sets.write().drain_resources() { - self.suspected_resources - .query_sets - .insert(v.as_info().id(), v.clone()); + fn triage_suspected_render_bundles( + &mut self, + hub: &Hub, + trackers: &Mutex>, + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resources = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.bundles, + &hub.render_bundles, + |_bundle_id, _bundle| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyRenderBundle(*_bundle_id)); } - !is_removed - }); + }, + ); + removed_resources.drain(..).for_each(|bundle| { + for v in bundle.used.buffers.write().drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v.clone()); + } + for v in bundle.used.textures.write().drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v.clone()); + } + for v in bundle.used.bind_groups.write().drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v.clone()); + } + for v in bundle.used.render_pipelines.write().drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v.clone()); + } + for v in bundle.used.query_sets.write().drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v.clone()); + } + }); self } - fn triage_suspected_bind_groups( + fn triage_suspected_bind_groups( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::BindGroupId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources - .bind_groups - .retain(|&bind_group_id, bind_group| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .bind_groups - .remove_abandoned(bind_group_id, hub.bind_groups.contains(bind_group_id)); - - f(&bind_group_id); - - for v in bind_group.used.buffers.drain_resources() { - self.suspected_resources - .buffers - .insert(v.as_info().id(), v.clone()); - } - for v in bind_group.used.textures.drain_resources() { - self.suspected_resources - .textures - .insert(v.as_info().id(), v.clone()); - } - for v in bind_group.used.views.drain_resources() { - self.suspected_resources - .texture_views - .insert(v.as_info().id(), v.clone()); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resource = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.bind_groups, + &hub.bind_groups, + |_bind_group_id, _bind_group| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyBindGroup(*_bind_group_id)); } - for v in bind_group.used.samplers.drain_resources() { - self.suspected_resources - .samplers - .insert(v.as_info().id(), v.clone()); - } - - self.suspected_resources - .bind_group_layouts - .insert(bind_group.layout.as_info().id(), bind_group.layout.clone()); + }, + ); + removed_resource.drain(..).for_each(|bind_group| { + for v in bind_group.used.buffers.drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v); + } + for v in bind_group.used.textures.drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v); + } + for v in bind_group.used.views.drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v); + } + for v in bind_group.used.samplers.drain_resources() { + self.suspected_resources.insert(v.as_info().id(), v); + } - let submit_index = bind_group.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); - } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .bind_groups - .insert(bind_group_id, bind_group.clone()); - !is_removed - }); - submit_indices + self.suspected_resources + .insert(bind_group.layout.as_info().id(), bind_group.layout.clone()); + }); + self } - fn triage_suspected_texture_views( + fn triage_suspected_texture_views( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::TextureViewId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources - .texture_views - .retain(|&view_id, view| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .views - .remove_abandoned(view_id, hub.texture_views.contains(view_id)); - - f(&view_id); - - { - let mut lock = view.parent.write(); - if let Some(parent_texture) = lock.take() { - self.suspected_resources - .textures - .insert(parent_texture.as_info().id(), parent_texture); - } + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resources = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.views, + &hub.texture_views, + |_texture_view_id, _texture_view| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyTextureView(*_texture_view_id)); } - let submit_index = view.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); - } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .texture_views - .insert(view_id, view.clone()); - !is_removed - }); - submit_indices + }, + ); + removed_resources.drain(..).for_each(|texture_view| { + let mut lock = texture_view.parent.write(); + if let Some(parent_texture) = lock.take() { + self.suspected_resources + .insert(parent_texture.as_info().id(), parent_texture); + } + }); + self } - fn triage_suspected_textures( + fn triage_suspected_textures( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> &mut Self - where - F: FnMut(&id::TextureId), - { - self.suspected_resources - .textures - .retain(|&texture_id, texture| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .textures - .remove_abandoned(texture_id, hub.textures.contains(texture_id)); - - f(&texture_id); - - let submit_index = texture.info.submission_index(); - let non_referenced_resources = self - .active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources); - non_referenced_resources - .textures - .insert(texture_id, texture.clone()); - !is_removed - }); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.textures, + &hub.textures, + |_texture_id, _texture| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyTexture(*_texture_id)); + } + }, + ); self } - fn triage_suspected_samplers( + fn triage_suspected_samplers( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::SamplerId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources - .samplers - .retain(|&sampler_id, sampler| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .samplers - .remove_abandoned(sampler_id, hub.samplers.contains(sampler_id)); - - f(&sampler_id); - - let submit_index = sampler.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.samplers, + &hub.samplers, + |_sampler_id, _sampler| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroySampler(*_sampler_id)); } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .samplers - .insert(sampler_id, sampler.clone()); - !is_removed - }); - submit_indices + }, + ); + self } - fn triage_suspected_buffers( + fn triage_suspected_buffers( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::BufferId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources - .buffers - .retain(|&buffer_id, buffer| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .buffers - .remove_abandoned(buffer_id, hub.buffers.contains(buffer_id)); - - f(&buffer_id); - - let submit_index = buffer.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); - } - if let resource::BufferMapState::Init { - ref stage_buffer, .. - } = *buffer.map_state.lock() - { - self.free_resources - .buffers - .insert(stage_buffer.as_info().id(), stage_buffer.clone()); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resources = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.buffers, + &hub.buffers, + |_buffer_id, _buffer| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyBuffer(*_buffer_id)); } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .buffers - .insert(buffer_id, buffer.clone()); - !is_removed - }); - submit_indices + }, + ); + removed_resources.drain(..).for_each(|buffer| { + if let resource::BufferMapState::Init { + ref stage_buffer, .. + } = *buffer.map_state.lock() + { + self.free_resources + .insert(stage_buffer.as_info().id(), stage_buffer.clone()); + } + }); + self } - fn triage_suspected_compute_pipelines( + fn triage_suspected_compute_pipelines( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::ComputePipelineId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources.compute_pipelines.retain( - |&compute_pipeline_id, compute_pipeline| { - let mut trackers = trackers.lock(); - let is_removed = trackers.compute_pipelines.remove_abandoned( - compute_pipeline_id, - hub.compute_pipelines.contains(compute_pipeline_id), - ); - - f(&compute_pipeline_id); - - self.suspected_resources.pipeline_layouts.insert( - compute_pipeline.layout.as_info().id(), - compute_pipeline.layout.clone(), - ); - - let submit_index = compute_pipeline.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resources = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.compute_pipelines, + &hub.compute_pipelines, + |_compute_pipeline_id, _compute_pipeline| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyComputePipeline(*_compute_pipeline_id)); } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .compute_pipelines - .insert(compute_pipeline_id, compute_pipeline.clone()); - !is_removed }, ); - submit_indices + removed_resources.drain(..).for_each(|compute_pipeline| { + self.suspected_resources.insert( + compute_pipeline.layout.as_info().id(), + compute_pipeline.layout.clone(), + ); + }); + self } - fn triage_suspected_render_pipelines( + fn triage_suspected_render_pipelines( &mut self, hub: &Hub, trackers: &Mutex>, - mut f: F, - ) -> Vec - where - F: FnMut(&id::RenderPipelineId), - { - let mut submit_indices = Vec::new(); - self.suspected_resources - .render_pipelines - .retain(|&render_pipeline_id, render_pipeline| { - let mut trackers = trackers.lock(); - let is_removed = trackers.render_pipelines.remove_abandoned( - render_pipeline_id, - hub.render_pipelines.contains(render_pipeline_id), - ); - - f(&render_pipeline_id); - - self.suspected_resources.pipeline_layouts.insert( - render_pipeline.layout.as_info().id(), - render_pipeline.layout.clone(), - ); - - let submit_index = render_pipeline.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + let mut removed_resources = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.render_pipelines, + &hub.render_pipelines, + |_render_pipeline_id, _render_pipeline| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyRenderPipeline(*_render_pipeline_id)); } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .render_pipelines - .insert(render_pipeline_id, render_pipeline.clone()); - !is_removed - }); - submit_indices + }, + ); + removed_resources.drain(..).for_each(|render_pipeline| { + self.suspected_resources.insert( + render_pipeline.layout.as_info().id(), + render_pipeline.layout.clone(), + ); + }); + self } - fn triage_suspected_pipeline_layouts(&mut self, mut f: F) -> &mut Self - where - F: FnMut(&id::PipelineLayoutId), - { + fn triage_suspected_pipeline_layouts( + &mut self, + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + let mut removed_resources = Vec::new(); self.suspected_resources - .pipeline_layouts - .retain(|pipeline_layout_id, pipeline_layout| { - //Note: this has to happen after all the suspected pipelines are destroyed - f(pipeline_layout_id); - - for bgl in &pipeline_layout.bind_group_layouts { - self.suspected_resources - .bind_group_layouts - .insert(bgl.as_info().id(), bgl.clone()); + .map_mut::>() + .retain(|_pipeline_layout_id, pipeline_layout| { + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyPipelineLayout(*_pipeline_layout_id)); } - self.free_resources - .pipeline_layouts - .insert(*pipeline_layout_id, pipeline_layout.clone()); + removed_resources.push(pipeline_layout.clone()); false }); + removed_resources.drain(..).for_each(|pipeline_layout| { + for bgl in &pipeline_layout.bind_group_layouts { + self.suspected_resources + .insert(bgl.as_info().id(), bgl.clone()); + } + }); self } - fn triage_suspected_bind_group_layouts(&mut self, mut f: F) -> &mut Self - where - F: FnMut(&id::BindGroupLayoutId), - { - self.suspected_resources.bind_group_layouts.retain( - |bind_group_layout_id, bind_group_layout| { + fn triage_suspected_bind_group_layouts( + &mut self, + #[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>, + ) -> &mut Self { + self.suspected_resources + .map_mut::>() + .retain(|bind_group_layout_id, bind_group_layout| { //Note: this has to happen after all the suspected pipelines are destroyed //Note: nothing else can bump the refcount since the guard is locked exclusively //Note: same BGL can appear multiple times in the list, but only the last // encounter could drop the refcount to 0. - f(bind_group_layout_id); + #[cfg(feature = "trace")] + if let Some(ref mut t) = *trace { + t.add(trace::Action::DestroyBindGroupLayout(*bind_group_layout_id)); + } self.free_resources - .bind_group_layouts .insert(*bind_group_layout_id, bind_group_layout.clone()); false - }, - ); + }); self } @@ -759,31 +758,29 @@ impl LifetimeTracker { &mut self, hub: &Hub, trackers: &Mutex>, - ) -> Vec { - let mut submit_indices = Vec::new(); + ) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = self.suspected_resources.map_mut(); + Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut self.free_resources, + &mut trackers.query_sets, + &hub.query_sets, + |_query_set_id, _query_set| {}, + ); + self + } + + fn triage_suspected_staging_buffers(&mut self) -> &mut Self { self.suspected_resources - .query_sets - .retain(|&query_set_id, query_set| { - let mut trackers = trackers.lock(); - let is_removed = trackers - .query_sets - .remove_abandoned(query_set_id, hub.query_sets.contains(query_set_id)); - // #[cfg(feature = "trace")] - // trace.map(|t| t.add(trace::Action::DestroyComputePipeline(id))); - - let submit_index = query_set.info.submission_index(); - if !submit_indices.contains(&submit_index) { - submit_indices.push(submit_index); - } - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .query_sets - .insert(query_set_id, query_set.clone()); - !is_removed + .map_mut::>() + .retain(|staging_buffer_id, staging_buffer| { + self.free_resources + .insert(*staging_buffer_id, staging_buffer.clone()); + false }); - submit_indices + self } /// Identify resources to free, according to `trackers` and `self.suspected_resources`. @@ -833,67 +830,65 @@ impl LifetimeTracker { ) { profiling::scope!("triage_suspected"); - self.triage_suspected_render_bundles(hub, trackers, |_id| { + //NOTE: the order is important to release resources that depends between each other! + self.triage_suspected_render_bundles( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyRenderBundle(*_id)); - } - }); - self.triage_suspected_compute_pipelines(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_compute_pipelines( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyComputePipeline(*_id)); - } - }); - self.triage_suspected_render_pipelines(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_render_pipelines( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyRenderPipeline(*_id)); - } - }); - self.triage_suspected_bind_groups(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_bind_groups( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyBindGroup(*_id)); - } - }); - self.triage_suspected_pipeline_layouts(|_id| { + &mut trace, + ); + self.triage_suspected_pipeline_layouts( #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyPipelineLayout(*_id)); - } - }); - self.triage_suspected_bind_group_layouts(|_id| { + &mut trace, + ); + self.triage_suspected_bind_group_layouts( #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyBindGroupLayout(*_id)); - } - }); + &mut trace, + ); self.triage_suspected_query_sets(hub, trackers); - self.triage_suspected_samplers(hub, trackers, |_id| { + self.triage_suspected_samplers( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroySampler(*_id)); - } - }); - self.triage_suspected_texture_views(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_staging_buffers(); + self.triage_suspected_texture_views( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyTextureView(*_id)); - } - }); - self.triage_suspected_textures(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_textures( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyTexture(*_id)); - } - }); - self.triage_suspected_buffers(hub, trackers, |_id| { + &mut trace, + ); + self.triage_suspected_buffers( + hub, + trackers, #[cfg(feature = "trace")] - if let Some(ref mut t) = trace { - t.add(trace::Action::DestroyBuffer(*_id)); - } - }); + &mut trace, + ); } /// Determine which buffers are ready to map, and which must wait for the @@ -944,16 +939,14 @@ impl LifetimeTracker { let buffer_id = buffer.info.id(); let is_removed = { let mut trackers = trackers.lock(); - trackers - .buffers - .remove_abandoned(buffer_id, hub.buffers.contains(buffer_id)) + let mut count = 1; + count += hub.buffers.contains(buffer_id) as usize; + trackers.buffers.remove_abandoned(buffer_id, count) }; if is_removed { *buffer.map_state.lock() = resource::BufferMapState::Idle; - log::info!("Buffer {:?} is not tracked anymore", buffer_id); - self.free_resources - .buffers - .insert(buffer_id, buffer.clone()); + log::info!("Buffer ready to map {:?} is not tracked anymore", buffer_id); + self.free_resources.insert(buffer_id, buffer.clone()); } else { let mapping = match std::mem::replace( &mut *buffer.map_state.lock(), diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 2247933acc..0d60974dac 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -14,8 +14,8 @@ use crate::{ identity::{GlobalIdentityHandlerFactory, Input}, init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange}, resource::{ - Buffer, BufferAccessError, BufferMapState, Resource, ResourceInfo, StagingBuffer, Texture, - TextureInner, + Buffer, BufferAccessError, BufferMapState, Resource, ResourceInfo, ResourceType, + StagingBuffer, Texture, TextureInner, }, track, FastHashMap, SubmissionIndex, }; @@ -38,7 +38,7 @@ pub struct Queue { } impl Resource for Queue { - const TYPE: &'static str = "Queue"; + const TYPE: ResourceType = "Queue"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -1136,7 +1136,7 @@ impl Global { { let mut suspected = temp_suspected.take().unwrap(); suspected.clear(); - temp_suspected.replace(ResourceMaps::new()); + temp_suspected.replace(ResourceMaps::new::()); } // finish all the command buffers first @@ -1201,11 +1201,7 @@ impl Global { unsafe { device.raw().unmap_buffer(raw_buf) } .map_err(DeviceError::from)?; } - temp_suspected - .as_mut() - .unwrap() - .buffers - .insert(id, buffer.clone()); + temp_suspected.as_mut().unwrap().insert(id, buffer.clone()); } else { match *buffer.map_state.lock() { BufferMapState::Idle => (), @@ -1227,11 +1223,7 @@ impl Global { }; texture.info.use_at(submit_index); if texture.is_unique() { - temp_suspected - .as_mut() - .unwrap() - .textures - .insert(id, texture.clone()); + temp_suspected.as_mut().unwrap().insert(id, texture.clone()); } if should_extend { unsafe { @@ -1247,7 +1239,6 @@ impl Global { temp_suspected .as_mut() .unwrap() - .texture_views .insert(texture_view.as_info().id(), texture_view.clone()); } } @@ -1267,7 +1258,6 @@ impl Global { temp_suspected .as_mut() .unwrap() - .bind_groups .insert(bg.as_info().id(), bg.clone()); } } @@ -1278,7 +1268,7 @@ impl Global { { compute_pipeline.info.use_at(submit_index); if compute_pipeline.is_unique() { - temp_suspected.as_mut().unwrap().compute_pipelines.insert( + temp_suspected.as_mut().unwrap().insert( compute_pipeline.as_info().id(), compute_pipeline.clone(), ); @@ -1289,7 +1279,7 @@ impl Global { { render_pipeline.info.use_at(submit_index); if render_pipeline.is_unique() { - temp_suspected.as_mut().unwrap().render_pipelines.insert( + temp_suspected.as_mut().unwrap().insert( render_pipeline.as_info().id(), render_pipeline.clone(), ); @@ -1301,7 +1291,6 @@ impl Global { temp_suspected .as_mut() .unwrap() - .query_sets .insert(query_set.as_info().id(), query_set.clone()); } } @@ -1322,7 +1311,6 @@ impl Global { temp_suspected .as_mut() .unwrap() - .render_bundles .insert(bundle.as_info().id(), bundle.clone()); } } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 67a258b0b8..50a25a203c 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -24,7 +24,7 @@ use crate::{ registry::Registry, resource::ResourceInfo, resource::{ - self, Buffer, QuerySet, Resource, Sampler, Texture, TextureView, + self, Buffer, QuerySet, Resource, ResourceType, Sampler, Texture, TextureView, TextureViewNotRenderableReason, }, storage::Storage, @@ -105,7 +105,7 @@ pub struct Device { life_tracker: Mutex>, /// Temporary storage for resource management functions. Cleared at the end /// of every call (unless an error occurs). - pub(crate) temp_suspected: Mutex>>, + pub(crate) temp_suspected: Mutex>, pub(crate) alignments: hal::Alignments, pub(crate) limits: wgt::Limits, pub(crate) features: wgt::Features, @@ -241,7 +241,7 @@ impl Device { fence: RwLock::new(Some(fence)), trackers: Mutex::new(Tracker::new()), life_tracker: Mutex::new(life::LifetimeTracker::new()), - temp_suspected: Mutex::new(Some(life::ResourceMaps::new())), + temp_suspected: Mutex::new(Some(life::ResourceMaps::new::())), #[cfg(feature = "trace")] trace: Mutex::new(trace_path.and_then(|path| match trace::Trace::new(path) { Ok(mut trace) => { @@ -304,7 +304,7 @@ impl Device { let temp_suspected = self.temp_suspected.lock().take().unwrap(); life_tracker.suspected_resources.extend(temp_suspected); } - self.temp_suspected.lock().replace(ResourceMaps::new()); + self.temp_suspected.lock().replace(ResourceMaps::new::()); life_tracker.triage_suspected( hub, @@ -348,6 +348,8 @@ impl Device { self.command_allocator.lock().as_mut().unwrap(), ); let mapping_closures = life_tracker.handle_mapping(hub, self.raw(), &self.trackers); + + //Cleaning up resources and released all unused suspected ones life_tracker.cleanup(); let closures = UserClosures { @@ -365,63 +367,47 @@ impl Device { { for resource in trackers.buffers.used_resources() { if resource.is_unique() { - temp_suspected - .buffers - .insert(resource.as_info().id(), resource.clone()); + temp_suspected.insert(resource.as_info().id(), resource.clone()); } } for resource in trackers.textures.used_resources() { if resource.is_unique() { - temp_suspected - .textures - .insert(resource.as_info().id(), resource.clone()); + temp_suspected.insert(resource.as_info().id(), resource.clone()); } } for resource in trackers.views.used_resources() { if resource.is_unique() { - temp_suspected - .texture_views - .insert(resource.as_info().id(), resource.clone()); + temp_suspected.insert(resource.as_info().id(), 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()); + temp_suspected.insert(resource.as_info().id(), resource.clone()); } } for resource in trackers.samplers.used_resources() { if resource.is_unique() { - temp_suspected - .samplers - .insert(resource.as_info().id(), resource.clone()); + temp_suspected.insert(resource.as_info().id(), 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()); + temp_suspected.insert(resource.as_info().id(), 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()); + temp_suspected.insert(resource.as_info().id(), 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()); + temp_suspected.insert(resource.as_info().id(), resource.clone()); } } } self.lock_life().suspected_resources.extend(temp_suspected); - self.temp_suspected.lock().replace(ResourceMaps::new()); + self.temp_suspected.lock().replace(ResourceMaps::new::()); } pub(crate) fn create_buffer( @@ -3169,7 +3155,7 @@ impl Device { } impl Resource for Device { - const TYPE: &'static str = "Device"; + const TYPE: ResourceType = "Device"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/hal_api.rs b/wgpu-core/src/hal_api.rs index 870557b442..b56ff9a458 100644 --- a/wgpu-core/src/hal_api.rs +++ b/wgpu-core/src/hal_api.rs @@ -1,4 +1,4 @@ -use wgt::Backend; +use wgt::{Backend, WasmNotSend, WasmNotSync}; use crate::{ global::Global, @@ -7,7 +7,7 @@ use crate::{ instance::{HalSurface, Instance, Surface}, }; -pub trait HalApi: hal::Api + 'static { +pub trait HalApi: hal::Api + 'static + WasmNotSend + WasmNotSync { const VARIANT: Backend; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance; fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance>; diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 116fb00fa2..b4642d8315 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -3,9 +3,10 @@ use std::{ any::Any, cmp::Ordering, fmt::{self, Debug}, + hash::Hash, marker::PhantomData, }; -use wgt::Backend; +use wgt::{Backend, WasmNotSend, WasmNotSync}; #[cfg(feature = "id32")] type IdType = u32; @@ -71,7 +72,7 @@ type Dummy = hal::api::Empty; all(feature = "serde", not(feature = "replay")), derive(serde::Deserialize) )] -pub struct Id(NonZeroId, PhantomData); +pub struct Id(NonZeroId, PhantomData); // This type represents Id in a more readable (and editable) way. #[allow(dead_code)] @@ -82,14 +83,20 @@ enum SerialId { Id(Index, Epoch, Backend), } #[cfg(feature = "trace")] -impl From> for SerialId { +impl From> for SerialId +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn from(id: Id) -> Self { let (index, epoch, backend) = id.unzip(); Self::Id(index, epoch, backend) } } #[cfg(feature = "replay")] -impl From for Id { +impl From for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn from(id: SerialId) -> Self { match id { SerialId::Id(index, epoch, backend) => TypedId::zip(index, epoch, backend), @@ -97,7 +104,10 @@ impl From for Id { } } -impl Id { +impl Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ /// # Safety /// /// The raw id must be valid for the type. @@ -128,41 +138,59 @@ impl Id { } } -impl Copy for Id {} +impl Copy for Id where T: 'static + WasmNotSend + WasmNotSync {} -impl Clone for Id { +impl Clone for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn clone(&self) -> Self { Self(self.0, PhantomData) } } -impl Debug for Id { +impl Debug for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.unzip().fmt(formatter) } } -impl std::hash::Hash for Id { +impl Hash for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn hash(&self, state: &mut H) { self.0.hash(state); } } -impl PartialEq for Id { +impl PartialEq for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } -impl Eq for Id {} +impl Eq for Id where T: 'static + WasmNotSend + WasmNotSync {} -impl PartialOrd for Id { +impl PartialOrd for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } -impl Ord for Id { +impl Ord for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } @@ -173,14 +201,17 @@ impl Ord for Id { /// Most `wgpu-core` clients should not use this trait. Unusual clients that /// need to construct `Id` values directly, or access their components, like the /// WGPU recording player, may use this trait to do so. -pub trait TypedId: Copy + Debug + Any { +pub trait TypedId: Copy + Debug + Any + 'static + WasmNotSend + WasmNotSync + Eq + Hash { fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self; fn unzip(self) -> (Index, Epoch, Backend); fn into_raw(self) -> NonZeroId; } #[allow(trivial_numeric_casts)] -impl TypedId for Id { +impl TypedId for Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self { assert_eq!(0, epoch >> EPOCH_BITS); assert_eq!(0, (index as IdType) >> INDEX_BITS); diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 7846e7031d..48890528df 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -8,7 +8,7 @@ use crate::{ id::{AdapterId, DeviceId, QueueId, SurfaceId}, identity::{GlobalIdentityHandlerFactory, Input}, present::Presentation, - resource::{Resource, ResourceInfo}, + resource::{Resource, ResourceInfo, ResourceType}, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE, }; @@ -162,7 +162,7 @@ pub struct Surface { } impl Resource for Surface { - const TYPE: &'static str = "Surface"; + const TYPE: ResourceType = "Surface"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -386,7 +386,7 @@ impl Adapter { } impl Resource for Adapter { - const TYPE: &'static str = "Adapter"; + const TYPE: ResourceType = "Adapter"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index d62e32f8b7..c09a2265bf 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -6,7 +6,7 @@ use crate::{ device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext}, hal_api::HalApi, id::{ComputePipelineId, PipelineLayoutId, RenderPipelineId, ShaderModuleId}, - resource::{Resource, ResourceInfo}, + resource::{Resource, ResourceInfo, ResourceType}, validation, Label, }; use arrayvec::ArrayVec; @@ -69,7 +69,7 @@ impl Drop for ShaderModule { } impl Resource for ShaderModule { - const TYPE: &'static str = "ShaderModule"; + const TYPE: ResourceType = "ShaderModule"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -264,7 +264,7 @@ impl Drop for ComputePipeline { } impl Resource for ComputePipeline { - const TYPE: &'static str = "ComputePipeline"; + const TYPE: ResourceType = "ComputePipeline"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -496,7 +496,7 @@ impl Drop for RenderPipeline { } impl Resource for RenderPipeline { - const TYPE: &'static str = "RenderPipeline"; + const TYPE: ResourceType = "RenderPipeline"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index e005bf7da3..1da553165a 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -23,6 +23,7 @@ use hal::CommandEncoder; use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; use smallvec::SmallVec; use thiserror::Error; +use wgt::{WasmNotSend, WasmNotSync}; use std::{ borrow::Borrow, @@ -132,8 +133,10 @@ impl ResourceInfo { } } -pub trait Resource { - const TYPE: &'static str; +pub(crate) type ResourceType = &'static str; + +pub trait Resource: 'static + WasmNotSend + WasmNotSync { + const TYPE: ResourceType; fn as_info(&self) -> &ResourceInfo; fn as_info_mut(&mut self) -> &mut ResourceInfo; fn label(&self) -> String { @@ -604,7 +607,7 @@ pub enum CreateBufferError { } impl Resource for Buffer { - const TYPE: &'static str = "Buffer"; + const TYPE: ResourceType = "Buffer"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -656,7 +659,7 @@ impl Drop for StagingBuffer { } impl Resource for StagingBuffer { - const TYPE: &'static str = "StagingBuffer"; + const TYPE: ResourceType = "StagingBuffer"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -956,7 +959,7 @@ pub enum CreateTextureError { } impl Resource for Texture { - const TYPE: &'static str = "Texture"; + const TYPE: ResourceType = "Texture"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -1112,7 +1115,7 @@ pub enum CreateTextureViewError { pub enum TextureViewDestroyError {} impl Resource for TextureView { - const TYPE: &'static str = "TextureView"; + const TYPE: ResourceType = "TextureView"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -1227,7 +1230,7 @@ pub enum CreateSamplerError { } impl Resource for Sampler { - const TYPE: &'static str = "Sampler"; + const TYPE: ResourceType = "Sampler"; fn as_info(&self) -> &ResourceInfo { &self.info @@ -1274,7 +1277,7 @@ impl Drop for QuerySet { } impl Resource for QuerySet { - const TYPE: &'static str = "QuerySet"; + const TYPE: ResourceType = "QuerySet"; fn as_info(&self) -> &ResourceInfo { &self.info diff --git a/wgpu-core/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs index 992b083ba1..932993681a 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; +use super::{PendingTransition, ResourceTracker}; use crate::{ hal_api::HalApi, id::{BufferId, TypedId}, @@ -291,6 +291,62 @@ pub(crate) struct BufferTracker { temp: Vec>, } + +impl ResourceTracker> for BufferTracker { + /// Removes the buffer `id` from this tracker if it is otherwise unused. + /// + /// A buffer is 'otherwise unused' when the only references to it are: + /// + /// 1) the `Arc` that our caller, `LifetimeTracker::triage_suspected`, has just + /// drained from `LifetimeTracker::suspected_resources`, + /// + /// 2) its `Arc` in [`self.metadata`] (owned by [`Device::trackers`]), and + /// + /// 3) its `Arc` in the [`Hub::buffers`] registry. + /// + /// If the buffer is indeed unused, this function removes 2), and + /// `triage_suspected` will remove 3), leaving 1) as the sole + /// remaining reference. + /// + /// Return `true` if this tracker contained the buffer `id`. This + /// implies that we removed it. + /// + /// [`Device::trackers`]: crate::device::Device + /// [`self.metadata`]: BufferTracker::metadata + /// [`Hub::buffers`]: crate::hub::Hub::buffers + fn remove_abandoned(&mut self, id: BufferId, external_count: usize) -> bool { + let index = id.unzip().0 as usize; + + if index > self.metadata.size() { + return false; + } + + self.tracker_assert_in_bounds(index); + + unsafe { + if self.metadata.contains_unchecked(index) { + let existing_ref_count = self.metadata.get_ref_count_unchecked(index); + //2 ref count if only in Device Tracker and suspected resource itself and already released from user + //so not appearing in Registry + let min_ref_count = 1 + external_count; + if existing_ref_count <= min_ref_count { + self.metadata.remove(index); + log::info!("Buffer {:?} is not tracked anymore", id,); + return true; + } else { + log::info!( + "Buffer {:?} is still referenced from {}", + id, + existing_ref_count + ); + } + } + } + + false + } +} + impl BufferTracker { pub fn new() -> Self { Self { @@ -555,59 +611,6 @@ impl BufferTracker { } None } - - /// Removes the buffer `id` from this tracker if it is otherwise unused. - /// - /// A buffer is 'otherwise unused' when the only references to it are: - /// - /// 1) the `Arc` that our caller, `LifetimeTracker::triage_suspected`, has just - /// drained from `LifetimeTracker::suspected_resources`, - /// - /// 2) its `Arc` in [`self.metadata`] (owned by [`Device::trackers`]), and - /// - /// 3) its `Arc` in the [`Hub::buffers`] registry. - /// - /// If the buffer is indeed unused, this function removes 2), and - /// `triage_suspected` will remove 3), leaving 1) as the sole - /// remaining reference. - /// - /// Return `true` if this tracker contained the buffer `id`. This - /// implies that we removed it. - /// - /// [`Device::trackers`]: crate::device::Device - /// [`self.metadata`]: BufferTracker::metadata - /// [`Hub::buffers`]: crate::hub::Hub::buffers - pub fn remove_abandoned(&mut self, id: BufferId, is_in_registry: bool) -> bool { - let index = id.unzip().0 as usize; - - if index > self.metadata.size() { - return false; - } - - self.tracker_assert_in_bounds(index); - - unsafe { - if self.metadata.contains_unchecked(index) { - let existing_ref_count = self.metadata.get_ref_count_unchecked(index); - //2 ref count if only in Device Tracker and suspected resource itself and already released from user - //so not appearing in Registry - let min_ref_count = if is_in_registry { 3 } else { 2 }; - if existing_ref_count <= min_ref_count { - self.metadata.remove(index); - log::info!("Buffer {:?} is not tracked anymore", id,); - return true; - } else { - log::info!( - "Buffer {:?} is still referenced from {}", - id, - existing_ref_count - ); - } - } - } - - false - } } /// Source of Buffer State. diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index b5c3861ebb..bd8d3a5580 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -484,6 +484,14 @@ impl UsageScope { } } +pub(crate) trait ResourceTracker +where + Id: TypedId, + R: resource::Resource, +{ + fn remove_abandoned(&mut self, id: Id, external_count: usize) -> bool; +} + /// A full double sided tracker used by CommandBuffers and the Device. pub(crate) struct Tracker { pub buffers: BufferTracker, diff --git a/wgpu-core/src/track/stateless.rs b/wgpu-core/src/track/stateless.rs index 96353767b1..e88c0c0c61 100644 --- a/wgpu-core/src/track/stateless.rs +++ b/wgpu-core/src/track/stateless.rs @@ -12,6 +12,8 @@ use crate::{ hal_api::HalApi, id::TypedId, resource::Resource, storage::Storage, track::ResourceMetadata, }; +use super::ResourceTracker; + /// Stores all the resources that a bind group stores. #[derive(Debug)] pub(crate) struct StatelessBindGroupSate> { @@ -72,6 +74,50 @@ pub(crate) struct StatelessTracker> { _phantom: PhantomData, } +impl> ResourceTracker + for StatelessTracker +{ + /// Removes the given resource from the tracker iff we have the last reference to the + /// resource and the epoch matches. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + fn remove_abandoned(&mut self, id: Id, external_count: usize) -> bool { + let index = id.unzip().0 as usize; + + if index > self.metadata.size() { + return false; + } + + self.tracker_assert_in_bounds(index); + + unsafe { + if self.metadata.contains_unchecked(index) { + let existing_ref_count = self.metadata.get_ref_count_unchecked(index); + //2 ref count if only in Device Tracker and suspected resource itself and already released from user + //so not appearing in Registry + let min_ref_count = 1 + external_count; + if existing_ref_count <= min_ref_count { + self.metadata.remove(index); + log::info!("{} {:?} is not tracked anymore", T::TYPE, id,); + return true; + } else { + log::info!( + "{} {:?} is still referenced from {}", + T::TYPE, + id, + existing_ref_count + ); + } + } + } + + false + } +} + impl> StatelessTracker { pub fn new() -> Self { Self { @@ -187,44 +233,4 @@ impl> StatelessTracker { } None } - - /// Removes the given resource from the tracker iff we have the last reference to the - /// resource and the epoch matches. - /// - /// Returns true if the resource was removed. - /// - /// If the ID is higher than the length of internal vectors, - /// false will be returned. - pub fn remove_abandoned(&mut self, id: Id, is_in_registry: bool) -> bool { - let index = id.unzip().0 as usize; - - if index > self.metadata.size() { - return false; - } - - self.tracker_assert_in_bounds(index); - - unsafe { - if self.metadata.contains_unchecked(index) { - let existing_ref_count = self.metadata.get_ref_count_unchecked(index); - //2 ref count if only in Device Tracker and suspected resource itself and already released from user - //so not appearing in Registry - let min_ref_count = if is_in_registry { 3 } else { 2 }; - if existing_ref_count <= min_ref_count { - self.metadata.remove(index); - log::info!("{} {:?} is not tracked anymore", T::TYPE, id,); - return true; - } else { - log::info!( - "{} {:?} is still referenced from {}", - T::TYPE, - id, - existing_ref_count - ); - } - } - } - - false - } } diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index fe8598feee..bda2b9f850 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -19,7 +19,7 @@ * will treat the contents as junk. !*/ -use super::{range::RangedStates, PendingTransition, PendingTransitionList}; +use super::{range::RangedStates, PendingTransition, PendingTransitionList, ResourceTracker}; use crate::{ hal_api::HalApi, id::{TextureId, TypedId}, @@ -392,6 +392,50 @@ pub(crate) struct TextureTracker { _phantom: PhantomData, } + +impl ResourceTracker> for TextureTracker { + /// Removes the given resource from the tracker iff we have the last reference to the + /// resource and the epoch matches. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + fn remove_abandoned(&mut self, id: TextureId, external_count: usize) -> bool { + let index = id.unzip().0 as usize; + + if index > self.metadata.size() { + return false; + } + + self.tracker_assert_in_bounds(index); + + unsafe { + if self.metadata.contains_unchecked(index) { + let existing_ref_count = self.metadata.get_ref_count_unchecked(index); + //2 ref count if only in Device Tracker and suspected resource itself and already released from user + //so not appearing in Registry + let min_ref_count = 1 + external_count; + if existing_ref_count <= min_ref_count { + self.start_set.complex.remove(&index); + self.end_set.complex.remove(&index); + self.metadata.remove(index); + log::info!("Texture {:?} is not tracked anymore", id,); + return true; + } else { + log::info!( + "Texture {:?} is still referenced from {}", + id, + existing_ref_count + ); + } + } + } + + false + } +} + impl TextureTracker { pub fn new() -> Self { Self { @@ -706,47 +750,6 @@ impl TextureTracker { false } - - /// Removes the given resource from the tracker iff we have the last reference to the - /// resource and the epoch matches. - /// - /// Returns true if the resource was removed. - /// - /// If the ID is higher than the length of internal vectors, - /// false will be returned. - pub fn remove_abandoned(&mut self, id: TextureId, is_in_registry: bool) -> bool { - let index = id.unzip().0 as usize; - - if index > self.metadata.size() { - return false; - } - - self.tracker_assert_in_bounds(index); - - unsafe { - if self.metadata.contains_unchecked(index) { - let existing_ref_count = self.metadata.get_ref_count_unchecked(index); - //2 ref count if only in Device Tracker and suspected resource itself and already released from user - //so not appearing in Registry - let min_ref_count = if is_in_registry { 3 } else { 2 }; - if existing_ref_count <= min_ref_count { - self.start_set.complex.remove(&index); - self.end_set.complex.remove(&index); - self.metadata.remove(index); - log::info!("Texture {:?} is not tracked anymore", id,); - return true; - } else { - log::info!( - "Texture {:?} is still referenced from {}", - id, - existing_ref_count - ); - } - } - } - - false - } } /// An iterator adapter that can store two different iterator types. diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 3367654d94..7e0d95d6b4 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -3016,7 +3016,10 @@ impl crate::Context for Context { } } -impl From for wgc::id::Id { +impl From for wgc::id::Id +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn from(id: ObjectId) -> Self { // If the id32 feature is enabled in wgpu-core, this will make sure that the id fits in a NonZeroU32. #[allow(clippy::useless_conversion)] @@ -3026,7 +3029,10 @@ impl From for wgc::id::Id { } } -impl From> for ObjectId { +impl From> for ObjectId +where + T: 'static + WasmNotSend + WasmNotSync, +{ fn from(id: wgc::id::Id) -> Self { // If the id32 feature is enabled in wgpu-core, the conversion is not useless #[allow(clippy::useless_conversion)]