diff --git a/Cargo.toml b/Cargo.toml index cf2ab9fb..3a3f0306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,49 +12,65 @@ keywords = ["vulkan", "memory", "allocator"] documentation = "https://docs.rs/gpu-allocator/" rust-version = "1.69" -include = [ - "/README.md", - "/LICENSE-*", - "/src", - "/examples", -] +include = ["/README.md", "/LICENSE-*", "/src", "/examples"] [package.metadata.docs.rs] all-features = true [dependencies] +arc-swap = "1.7" log = "0.4" thiserror = "1.0" presser = { version = "0.3" } # Only needed for Vulkan. Disable all default features as good practice, # such as the ability to link/load a Vulkan library. -ash = { version = "0.38", optional = true, default-features = false, features = ["debug"] } +ash = { version = "0.38", optional = true, default-features = false, features = [ + "debug", +] } # Only needed for visualizer. egui = { version = ">=0.24, <=0.27", optional = true, default-features = false } egui_extras = { version = ">=0.24, <=0.27", optional = true, default-features = false } [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -metal = { version = "0.28.0", git = "https://github.com/gfx-rs/metal-rs", rev = "0d6214f", default-features = false, features = ["link", "dispatch"], optional = true } +metal = { version = "0.28.0", git = "https://github.com/gfx-rs/metal-rs", rev = "0d6214f", default-features = false, features = [ + "link", + "dispatch", +], optional = true } [target.'cfg(windows)'.dependencies] # Only needed for public-winapi interop helpers -winapi = { version = "0.3.9", features = ["d3d12", "winerror", "impl-default", "impl-debug"], optional = true } +winapi = { version = "0.3.9", features = [ + "d3d12", + "winerror", + "impl-default", + "impl-debug", +], optional = true } [target.'cfg(windows)'.dependencies.windows] version = ">=0.53,<=0.56" -features = [ - "Win32_Graphics_Direct3D12", - "Win32_Graphics_Dxgi_Common", -] +features = ["Win32_Graphics_Direct3D12", "Win32_Graphics_Dxgi_Common"] optional = true [dev-dependencies] # Enable the "loaded" feature to be able to access the Vulkan entrypoint. -ash = { version = "0.38", default-features = false, features = ["debug", "loaded"] } +ash = { version = "0.38", default-features = false, features = [ + "debug", + "loaded", +] } env_logger = "0.10" [target.'cfg(windows)'.dev-dependencies] -winapi = { version = "0.3.9", features = ["d3d12", "d3d12sdklayers", "dxgi1_6", "winerror", "impl-default", "impl-debug", "winuser", "windowsx", "libloaderapi"] } +winapi = { version = "0.3.9", features = [ + "d3d12", + "d3d12sdklayers", + "dxgi1_6", + "winerror", + "impl-default", + "impl-debug", + "winuser", + "windowsx", + "libloaderapi", +] } [target.'cfg(windows)'.dev-dependencies.windows] version = ">=0.53,<=0.56" diff --git a/src/allocator/dedicated_block_allocator/mod.rs b/src/allocator/dedicated_block_allocator/mod.rs index 746b4dcd..feeeb120 100644 --- a/src/allocator/dedicated_block_allocator/mod.rs +++ b/src/allocator/dedicated_block_allocator/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod visualizer; use std::{backtrace::Backtrace, sync::Arc}; +use arc_swap::ArcSwapOption; use log::{log, Level}; use super::{AllocationReport, AllocationType, SubAllocator, SubAllocatorBase}; @@ -15,7 +16,7 @@ pub(crate) struct DedicatedBlockAllocator { size: u64, allocated: u64, /// Only used if [`crate::AllocatorDebugSettings::store_stack_traces`] is [`true`] - name: Option, + name: ArcSwapOption, backtrace: Arc, } @@ -24,7 +25,7 @@ impl DedicatedBlockAllocator { Self { size, allocated: 0, - name: None, + name: ArcSwapOption::empty(), backtrace: Arc::new(Backtrace::disabled()), } } @@ -52,7 +53,7 @@ impl SubAllocator for DedicatedBlockAllocator { } self.allocated = size; - self.name = Some(name.to_string()); + self.name.swap(Some(Arc::new(name.to_string()))); self.backtrace = backtrace; #[allow(clippy::unwrap_used)] @@ -69,15 +70,11 @@ impl SubAllocator for DedicatedBlockAllocator { } } - fn rename_allocation( - &mut self, - chunk_id: Option, - name: &str, - ) -> Result<()> { + fn rename_allocation(&self, chunk_id: Option, name: &str) -> Result<()> { if chunk_id != std::num::NonZeroU64::new(1) { Err(AllocationError::Internal("Chunk ID must be 1.".into())) } else { - self.name = Some(name.into()); + self.name.swap(Some(Arc::new(name.into()))); Ok(()) } } @@ -88,8 +85,8 @@ impl SubAllocator for DedicatedBlockAllocator { memory_type_index: usize, memory_block_index: usize, ) { - let empty = "".to_string(); - let name = self.name.as_ref().unwrap_or(&empty); + let name = self.name.load(); + let name = (*name).as_ref().map_or("", |name| name); log!( log_level, @@ -112,10 +109,10 @@ impl SubAllocator for DedicatedBlockAllocator { fn report_allocations(&self) -> Vec { vec![AllocationReport { - name: self - .name - .clone() - .unwrap_or_else(|| "".to_owned()), + name: self.name.load().as_ref().map_or_else( + || "".to_owned(), + |s: &Arc| (**s).clone(), + ), offset: 0, size: self.size, #[cfg(feature = "visualizer")] diff --git a/src/allocator/free_list_allocator/mod.rs b/src/allocator/free_list_allocator/mod.rs index d7cde2e8..fa238118 100644 --- a/src/allocator/free_list_allocator/mod.rs +++ b/src/allocator/free_list_allocator/mod.rs @@ -9,6 +9,7 @@ use std::{ sync::Arc, }; +use arc_swap::ArcSwapOption; use log::{log, Level}; use super::{AllocationReport, AllocationType, SubAllocator, SubAllocatorBase}; @@ -30,7 +31,7 @@ pub(crate) struct MemoryChunk { pub(crate) size: u64, pub(crate) offset: u64, pub(crate) allocation_type: AllocationType, - pub(crate) name: Option, + pub(crate) name: ArcSwapOption, /// Only used if [`crate::AllocatorDebugSettings::store_stack_traces`] is [`true`] pub(crate) backtrace: Arc, next: Option, @@ -78,7 +79,7 @@ impl FreeListAllocator { size, offset: 0, allocation_type: AllocationType::Free, - name: None, + name: ArcSwapOption::empty(), backtrace: Arc::new(Backtrace::disabled()), prev: None, next: None, @@ -248,7 +249,7 @@ impl SubAllocator for FreeListAllocator { size: best_aligned_size, offset: free_chunk.offset, allocation_type, - name: Some(name.to_string()), + name: ArcSwapOption::from_pointee(name.to_string()), backtrace, prev: free_chunk.prev, next: Some(first_fit_id), @@ -277,7 +278,7 @@ impl SubAllocator for FreeListAllocator { .ok_or_else(|| AllocationError::Internal("Invalid chunk reference.".into()))?; chunk.allocation_type = allocation_type; - chunk.name = Some(name.to_string()); + chunk.name.swap(Some(Arc::new(name.to_string()))); chunk.backtrace = backtrace; self.remove_id_from_free_list(first_fit_id); @@ -301,7 +302,7 @@ impl SubAllocator for FreeListAllocator { ) })?; chunk.allocation_type = AllocationType::Free; - chunk.name = None; + chunk.name.swap(None); chunk.backtrace = Arc::new(Backtrace::disabled()); self.allocated -= chunk.size; @@ -325,15 +326,11 @@ impl SubAllocator for FreeListAllocator { Ok(()) } - fn rename_allocation( - &mut self, - chunk_id: Option, - name: &str, - ) -> Result<()> { + fn rename_allocation(&self, chunk_id: Option, name: &str) -> Result<()> { let chunk_id = chunk_id .ok_or_else(|| AllocationError::Internal("Chunk ID must be a valid value.".into()))?; - let chunk = self.chunks.get_mut(&chunk_id).ok_or_else(|| { + let chunk = self.chunks.get(&chunk_id).ok_or_else(|| { AllocationError::Internal( "Attempting to rename chunk that is not in chunk list.".into(), ) @@ -345,7 +342,7 @@ impl SubAllocator for FreeListAllocator { )); } - chunk.name = Some(name.into()); + chunk.name.swap(Some(Arc::new(name.into()))); Ok(()) } @@ -360,8 +357,9 @@ impl SubAllocator for FreeListAllocator { if chunk.allocation_type == AllocationType::Free { continue; } - let empty = "".to_string(); - let name = chunk.name.as_ref().unwrap_or(&empty); + + let name = chunk.name.load(); + let name = (*name).as_ref().map_or("", |name| name); log!( log_level, @@ -394,10 +392,10 @@ impl SubAllocator for FreeListAllocator { .iter() .filter(|(_key, chunk)| chunk.allocation_type != AllocationType::Free) .map(|(_key, chunk)| AllocationReport { - name: chunk - .name - .clone() - .unwrap_or_else(|| "".to_owned()), + name: chunk.name.load().as_ref().map_or_else( + || "".to_owned(), + |s: &Arc| (**s).clone(), + ), offset: chunk.offset, size: chunk.size, #[cfg(feature = "visualizer")] diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs index 5d40bbf6..2fc9404c 100644 --- a/src/allocator/mod.rs +++ b/src/allocator/mod.rs @@ -118,11 +118,7 @@ pub(crate) trait SubAllocator: SubAllocatorBase + fmt::Debug + Sync + Send { fn free(&mut self, chunk_id: Option) -> Result<()>; - fn rename_allocation( - &mut self, - chunk_id: Option, - name: &str, - ) -> Result<()>; + fn rename_allocation(&self, chunk_id: Option, name: &str) -> Result<()>; fn report_memory_leaks( &self, diff --git a/src/d3d12/mod.rs b/src/d3d12/mod.rs index 6236fd90..0e9ea363 100644 --- a/src/d3d12/mod.rs +++ b/src/d3d12/mod.rs @@ -1,5 +1,6 @@ use std::{backtrace::Backtrace, fmt, sync::Arc}; +use arc_swap::ArcSwapOption; use log::{debug, warn, Level}; use windows::Win32::{ Foundation::E_OUTOFMEMORY, @@ -320,7 +321,7 @@ pub struct Allocation { memory_type_index: usize, heap: ID3D12Heap, - name: Option>, + name: ArcSwapOption, } impl Allocation { @@ -485,7 +486,7 @@ impl MemoryType { memory_block_index: block_index, memory_type_index: self.memory_type_index, heap: mem_block.heap.clone(), - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), }); } @@ -510,7 +511,7 @@ impl MemoryType { memory_block_index: mem_block_i, memory_type_index: self.memory_type_index, heap: mem_block.heap.clone(), - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), }); } Err(AllocationError::OutOfMemory) => {} // Block is full, continue search. @@ -564,7 +565,7 @@ impl MemoryType { memory_block_index: new_block_index, memory_type_index: self.memory_type_index, heap: mem_block.heap.clone(), - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), }) } @@ -766,7 +767,8 @@ impl Allocator { pub fn free(&mut self, allocation: Allocation) -> Result<()> { if self.debug_settings.log_frees { - let name = allocation.name.as_deref().unwrap_or(""); + let name = allocation.name.load(); + let name = name.as_deref().map_or("", |name| name); debug!("Freeing `{}`.", name); if self.debug_settings.log_stack_traces { let backtrace = Backtrace::force_capture(); @@ -783,16 +785,16 @@ impl Allocator { Ok(()) } - pub fn rename_allocation(&mut self, allocation: &mut Allocation, name: &str) -> Result<()> { - allocation.name = Some(name.into()); + pub fn rename_allocation(&self, allocation: &Allocation, name: &str) -> Result<()> { + allocation.name.swap(Some(Arc::new(name.into()))); if allocation.is_null() { return Ok(()); } - let mem_type = &mut self.memory_types[allocation.memory_type_index]; + let mem_type = &self.memory_types[allocation.memory_type_index]; let mem_block = mem_type.memory_blocks[allocation.memory_block_index] - .as_mut() + .as_ref() .ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?; mem_block diff --git a/src/visualizer/memory_chunks.rs b/src/visualizer/memory_chunks.rs index ffe2afed..1f450a8a 100644 --- a/src/visualizer/memory_chunks.rs +++ b/src/visualizer/memory_chunks.rs @@ -111,7 +111,7 @@ pub(crate) fn render_memory_chunks_ui<'a>( "allocation_type: {}", chunk.allocation_type.as_str() )); - if let Some(name) = &chunk.name { + if let Some(name) = &*chunk.name.load() { ui.label(format!("name: {}", name)); } if settings.show_backtraces diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 560d7cbf..73a43959 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -4,6 +4,7 @@ mod visualizer; use std::{backtrace::Backtrace, fmt, marker::PhantomData, sync::Arc}; +use arc_swap::ArcSwapOption; use ash::vk; use log::{debug, Level}; #[cfg(feature = "visualizer")] @@ -158,7 +159,7 @@ pub struct Allocation { mapped_ptr: Option, dedicated_allocation: bool, memory_properties: vk::MemoryPropertyFlags, - name: Option>, + name: ArcSwapOption, } impl Allocation { @@ -273,7 +274,7 @@ impl Default for Allocation { device_memory: vk::DeviceMemory::null(), mapped_ptr: None, memory_properties: vk::MemoryPropertyFlags::empty(), - name: None, + name: ArcSwapOption::empty(), dedicated_allocation: false, } } @@ -531,7 +532,7 @@ impl MemoryType { device_memory: mem_block.device_memory, mapped_ptr: mem_block.mapped_ptr, memory_properties: self.memory_properties, - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), dedicated_allocation, }); } @@ -567,7 +568,7 @@ impl MemoryType { memory_properties: self.memory_properties, mapped_ptr, dedicated_allocation: false, - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), }); } Err(err) => match err { @@ -640,7 +641,7 @@ impl MemoryType { device_memory: mem_block.device_memory, mapped_ptr, memory_properties: self.memory_properties, - name: Some(desc.name.into()), + name: ArcSwapOption::from_pointee(desc.name.to_owned()), dedicated_allocation: false, }) } @@ -870,7 +871,8 @@ impl Allocator { pub fn free(&mut self, allocation: Allocation) -> Result<()> { if self.debug_settings.log_frees { - let name = allocation.name.as_deref().unwrap_or(""); + let name = allocation.name.load(); + let name = name.as_deref().map_or("", |name| name); debug!("Freeing `{}`.", name); if self.debug_settings.log_stack_traces { let backtrace = Backtrace::force_capture(); @@ -887,16 +889,16 @@ impl Allocator { Ok(()) } - pub fn rename_allocation(&mut self, allocation: &mut Allocation, name: &str) -> Result<()> { - allocation.name = Some(name.into()); + pub fn rename_allocation(&self, allocation: &Allocation, name: &str) -> Result<()> { + allocation.name.swap(Some(Arc::new(name.into()))); if allocation.is_null() { return Ok(()); } - let mem_type = &mut self.memory_types[allocation.memory_type_index]; + let mem_type = &self.memory_types[allocation.memory_type_index]; let mem_block = mem_type.memory_blocks[allocation.memory_block_index] - .as_mut() + .as_ref() .ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?; mem_block