diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adb8c43..a1b3395 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,15 +69,6 @@ jobs: uses: dtolnay/rust-toolchain@nightly - name: Generate lockfile with minimal dependency versions run: cargo +nightly generate-lockfile -Zminimal-versions - - name: Bump `libc 0.1` version to `0.2` via `malloc_buf 0.0.6` - if: ${{ runner.os == 'macOS' }} - run: | - # The 7-year-unmaintained malloc_buf (depended on via metal-rs->objc) - # only allows using libc 0.2 since the 0.0.6 release, which is necessary - # since the libc 0.1 range no longer compiles. Fortunately objc which - # is also unmaintained for 4 years depends on malloc_buf >=0.0,<0.1.0, - # allowing the 0.0.6 release to be used (but not the 1.0.0 release). - cargo update -p malloc_buf --precise 0.0.6 - name: Cargo clippy with minimal-versions run: cargo +stable clippy --workspace --all-targets --features ${{ matrix.features }} --no-default-features -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index cf2ab9f..15c6042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,18 @@ ash = { version = "0.38", optional = true, default-features = false, features = 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 } +[target.'cfg(target_vendor = "apple")'.dependencies] +objc2 = { version = "0.5", default-features = false, optional = true } +objc2-foundation = { version = "0.2", default-features = false, optional = true } +objc2-metal = { version = "0.2.2", default-features = false, features = [ + "MTLAccelerationStructure", + "MTLBuffer", + "MTLDevice", + "MTLHeap", + "MTLResource", + "MTLTexture", + "std", +], optional = true } [target.'cfg(windows)'.dependencies] # Only needed for public-winapi interop helpers @@ -64,6 +74,11 @@ features = [ "Win32_Graphics_Dxgi_Common", ] +[target.'cfg(target_vendor = "apple")'.dev-dependencies] +objc2-metal = { version = "0.2.2", default-features = false, features = [ + "MTLPixelFormat", +] } + [[example]] name = "vulkan-buffer" required-features = ["vulkan", "ash/loaded"] @@ -84,8 +99,8 @@ required-features = ["metal"] visualizer = ["dep:egui", "dep:egui_extras"] vulkan = ["dep:ash"] d3d12 = ["dep:windows"] -metal = ["dep:metal"] +metal = ["dep:objc2", "dep:objc2-metal", "dep:objc2-foundation"] # Expose helper functionality for winapi types to interface with gpu-allocator, which is primarily windows-rs driven public-winapi = ["dep:winapi"] -default = ["d3d12", "vulkan"] +default = ["d3d12", "vulkan", "metal"] diff --git a/README.md b/README.md index 9fe60bc..f3bc3eb 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ allocator.free(allocation).unwrap(); ```rust use gpu_allocator::metal::*; - +use objc2_metal as metal; let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.clone(), debug_settings: Default::default(), @@ -146,12 +146,12 @@ let mut allocator = Allocator::new(&AllocatorCreateDesc { ```rust use gpu_allocator::metal::*; use gpu_allocator::MemoryLocation; - +use objc2_metal as metal; let allocation_desc = AllocationCreateDesc::buffer( &device, "Example allocation", 512, // size in bytes - gpu_allocator::MemoryLocation::GpuOnly, + MemoryLocation::GpuOnly, ); let allocation = allocator.allocate(&allocation_desc).unwrap(); let resource = allocation.make_buffer().unwrap(); diff --git a/examples/metal-buffer.rs b/examples/metal-buffer.rs index 5674bef..9829e4c 100644 --- a/examples/metal-buffer.rs +++ b/examples/metal-buffer.rs @@ -1,12 +1,16 @@ -use std::sync::Arc; - use gpu_allocator::metal::{AllocationCreateDesc, Allocator, AllocatorCreateDesc}; use log::info; +use metal::MTLDevice as _; +use objc2::rc::Id; +use objc2_foundation::NSArray; +use objc2_metal as metal; fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); - let device = Arc::new(metal::Device::system_default().unwrap()); + let device = unsafe { metal::MTLCreateSystemDefaultDevice() }; + // TODO: Not SendSync + let device = unsafe { Id::from_raw(device) }.expect("No MTLDevice found"); // Setting up the allocator let mut allocator = Allocator::new(&AllocatorCreateDesc { @@ -60,11 +64,11 @@ fn main() { // Test allocating texture { - let texture_desc = metal::TextureDescriptor::new(); - texture_desc.set_pixel_format(metal::MTLPixelFormat::RGBA8Unorm); - texture_desc.set_width(64); - texture_desc.set_height(64); - texture_desc.set_storage_mode(metal::MTLStorageMode::Private); + let texture_desc = unsafe { metal::MTLTextureDescriptor::new() }; + texture_desc.setPixelFormat(metal::MTLPixelFormat::RGBA8Unorm); + unsafe { texture_desc.setWidth(64) }; + unsafe { texture_desc.setHeight(64) }; + texture_desc.setStorageMode(metal::MTLStorageMode::Private); let allocation_desc = AllocationCreateDesc::texture(&device, "Test allocation (Texture)", &texture_desc); let allocation = allocator.allocate(&allocation_desc).unwrap(); @@ -75,14 +79,14 @@ fn main() { // Test allocating acceleration structure { - let empty_array = metal::Array::from_slice(&[]); - let acc_desc = metal::PrimitiveAccelerationStructureDescriptor::descriptor(); - acc_desc.set_geometry_descriptors(empty_array); - let sizes = device.acceleration_structure_sizes_with_descriptor(&acc_desc); + let empty_array = NSArray::from_slice(&[]); + let acc_desc = metal::MTLPrimitiveAccelerationStructureDescriptor::descriptor(); + acc_desc.setGeometryDescriptors(Some(&empty_array)); + let sizes = device.accelerationStructureSizesWithDescriptor(&acc_desc); let allocation_desc = AllocationCreateDesc::acceleration_structure_with_size( &device, "Test allocation (Acceleration structure)", - sizes.acceleration_structure_size, + sizes.accelerationStructureSize as u64, gpu_allocator::MemoryLocation::GpuOnly, ); let allocation = allocator.allocate(&allocation_desc).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 7d2112d..878082c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,10 +161,11 @@ //! ```no_run //! # #[cfg(feature = "metal")] //! # fn main() { -//! # use std::sync::Arc; //! use gpu_allocator::metal::*; -//! -//! # let device = Arc::new(metal::Device::system_default().unwrap()); +//! # use objc2::rc::Id; +//! use objc2_metal as metal; +//! # let device = unsafe { metal::MTLCreateSystemDefaultDevice() }; +//! # let device = unsafe { Id::from_raw(device) }.expect("No MTLDevice found"); //! let mut allocator = Allocator::new(&AllocatorCreateDesc { //! device: device.clone(), //! debug_settings: Default::default(), @@ -179,22 +180,23 @@ //! ```no_run //! # #[cfg(feature = "metal")] //! # fn main() { -//! # use std::sync::Arc; //! use gpu_allocator::metal::*; //! use gpu_allocator::MemoryLocation; -//! # let device = Arc::new(metal::Device::system_default().unwrap()); +//! # use objc2::rc::Id; +//! use objc2_metal as metal; +//! # let device = unsafe { metal::MTLCreateSystemDefaultDevice() }; +//! # let device = unsafe { Id::from_raw(device) }.expect("No MTLDevice found"); //! # let mut allocator = Allocator::new(&AllocatorCreateDesc { //! # device: device.clone(), //! # debug_settings: Default::default(), //! # allocation_sizes: Default::default(), //! # }) //! # .unwrap(); -//! //! let allocation_desc = AllocationCreateDesc::buffer( //! &device, //! "Example allocation", //! 512, // size in bytes -//! gpu_allocator::MemoryLocation::GpuOnly, +//! MemoryLocation::GpuOnly, //! ); //! let allocation = allocator.allocate(&allocation_desc).unwrap(); //! let resource = allocation.make_buffer().unwrap(); @@ -206,6 +208,7 @@ //! # #[cfg(not(feature = "metal"))] //! # fn main() {} //! ``` +#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] mod result; pub use result::*; @@ -223,7 +226,7 @@ pub mod vulkan; #[cfg(all(windows, feature = "d3d12"))] pub mod d3d12; -#[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))] +#[cfg(all(target_vendor = "apple", feature = "metal"))] pub mod metal; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/src/metal/mod.rs b/src/metal/mod.rs index b14096c..f1dab75 100644 --- a/src/metal/mod.rs +++ b/src/metal/mod.rs @@ -1,7 +1,10 @@ -#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] use std::{backtrace::Backtrace, sync::Arc}; use log::debug; +use metal::{MTLDevice as _, MTLHeap as _, MTLResource as _}; +use objc2::{rc::Retained, runtime::ProtocolObject}; +use objc2_foundation::NSString; +use objc2_metal as metal; use crate::{ allocator::{self, AllocatorReport, MemoryBlockReport}, @@ -10,9 +13,9 @@ use crate::{ fn memory_location_to_metal(location: MemoryLocation) -> metal::MTLResourceOptions { match location { - MemoryLocation::GpuOnly => metal::MTLResourceOptions::StorageModePrivate, + MemoryLocation::GpuOnly => metal::MTLResourceOptions::MTLResourceStorageModePrivate, MemoryLocation::CpuToGpu | MemoryLocation::GpuToCpu | MemoryLocation::Unknown => { - metal::MTLResourceOptions::StorageModeShared + metal::MTLResourceOptions::MTLResourceStorageModeShared } } } @@ -24,44 +27,57 @@ pub struct Allocation { size: u64, memory_block_index: usize, memory_type_index: usize, - heap: Arc, + heap: Retained>, name: Option>, } impl Allocation { - pub fn heap(&self) -> &metal::Heap { - self.heap.as_ref() + pub fn heap(&self) -> &ProtocolObject { + &self.heap } - pub fn make_buffer(&self) -> Option { - let resource = - self.heap - .new_buffer_with_offset(self.size, self.heap.resource_options(), self.offset); + pub fn make_buffer(&self) -> Option>> { + let resource = unsafe { + self.heap.newBufferWithLength_options_offset( + self.size as usize, + self.heap.resourceOptions(), + self.offset as usize, + ) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource } - pub fn make_texture(&self, desc: &metal::TextureDescriptor) -> Option { - let resource = self.heap.new_texture_with_offset(desc, self.offset); + pub fn make_texture( + &self, + desc: &metal::MTLTextureDescriptor, + ) -> Option>> { + let resource = unsafe { + self.heap + .newTextureWithDescriptor_offset(desc, self.offset as usize) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource } - pub fn make_acceleration_structure(&self) -> Option { - let resource = self - .heap - .new_acceleration_structure_with_size_offset(self.size, self.offset); + pub fn make_acceleration_structure( + &self, + ) -> Option>> { + let resource = unsafe { + self.heap + .newAccelerationStructureWithSize_offset(self.size as usize, self.offset as usize) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource @@ -84,54 +100,64 @@ pub struct AllocationCreateDesc<'a> { impl<'a> AllocationCreateDesc<'a> { pub fn buffer( - device: &metal::Device, + device: &ProtocolObject, name: &'a str, length: u64, location: MemoryLocation, ) -> Self { - let size_and_align = - device.heap_buffer_size_and_align(length, memory_location_to_metal(location)); + let size_and_align = device.heapBufferSizeAndAlignWithLength_options( + length as usize, + memory_location_to_metal(location), + ); Self { name, location, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } - pub fn texture(device: &metal::Device, name: &'a str, desc: &metal::TextureDescriptor) -> Self { - let size_and_align = device.heap_texture_size_and_align(desc); + pub fn texture( + device: &ProtocolObject, + name: &'a str, + desc: &metal::MTLTextureDescriptor, + ) -> Self { + let size_and_align = device.heapTextureSizeAndAlignWithDescriptor(desc); Self { name, - location: match desc.storage_mode() { + location: match desc.storageMode() { metal::MTLStorageMode::Shared | metal::MTLStorageMode::Managed | metal::MTLStorageMode::Memoryless => MemoryLocation::Unknown, metal::MTLStorageMode::Private => MemoryLocation::GpuOnly, + metal::MTLStorageMode(mode /* @ 4.. */) => todo!("Unknown storage mode {mode}"), }, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } pub fn acceleration_structure_with_size( - device: &metal::Device, + device: &ProtocolObject, name: &'a str, - size: u64, + size: u64, // TODO: usize location: MemoryLocation, ) -> Self { - let size_and_align = device.heap_acceleration_structure_size_and_align_with_size(size); + // TODO: See if we can mark this function as safe, after checking what happens if size is too large? + // What other preconditions need to be upheld? + let size_and_align = + unsafe { device.heapAccelerationStructureSizeAndAlignWithSize(size as usize) }; Self { name, location, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } } pub struct Allocator { - device: Arc, + device: Retained>, debug_settings: AllocatorDebugSettings, memory_types: Vec, allocation_sizes: AllocationSizes, @@ -139,7 +165,7 @@ pub struct Allocator { #[derive(Debug)] pub struct AllocatorCreateDesc { - pub device: Arc, + pub device: Retained>, pub debug_settings: AllocatorDebugSettings, pub allocation_sizes: AllocationSizes, } @@ -152,23 +178,28 @@ pub struct CommittedAllocationStatistics { #[derive(Debug)] struct MemoryBlock { - heap: Arc, + heap: Retained>, size: u64, sub_allocator: Box, } impl MemoryBlock { fn new( - device: &Arc, + device: &ProtocolObject, size: u64, - heap_descriptor: &metal::HeapDescriptor, + heap_descriptor: &metal::MTLHeapDescriptor, dedicated: bool, memory_location: MemoryLocation, ) -> Result { - heap_descriptor.set_size(size); + heap_descriptor.setSize(size as usize); - let heap = Arc::new(device.new_heap(heap_descriptor)); - heap.set_label(&format!("MemoryBlock {memory_location:?}")); + let heap = device + .newHeapWithDescriptor(heap_descriptor) + .ok_or_else(|| AllocationError::Internal("No MTLHeap was returned".to_string()))?; + + heap.setLabel(Some(&NSString::from_str(&format!( + "MemoryBlock {memory_location:?}" + )))); let sub_allocator: Box = if dedicated { Box::new(allocator::DedicatedBlockAllocator::new(size)) @@ -189,7 +220,7 @@ struct MemoryType { memory_blocks: Vec>, _committed_allocations: CommittedAllocationStatistics, memory_location: MemoryLocation, - heap_properties: metal::HeapDescriptor, + heap_properties: Retained, memory_type_index: usize, active_general_blocks: usize, } @@ -197,14 +228,14 @@ struct MemoryType { impl MemoryType { fn allocate( &mut self, - device: &Arc, + device: &ProtocolObject, desc: &AllocationCreateDesc<'_>, backtrace: Arc, allocation_sizes: &AllocationSizes, ) -> Result { let allocation_type = allocator::AllocationType::Linear; - let memblock_size = if self.heap_properties.storage_mode() == metal::MTLStorageMode::Private + let memblock_size = if self.heap_properties.storageMode() == metal::MTLStorageMode::Private { allocation_sizes.device_memblock_size } else { @@ -380,24 +411,24 @@ impl Allocator { pub fn new(desc: &AllocatorCreateDesc) -> Result { let heap_types = [ (MemoryLocation::GpuOnly, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::DefaultCache); - heap_desc.set_storage_mode(metal::MTLStorageMode::Private); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::DefaultCache); + heap_desc.setStorageMode(metal::MTLStorageMode::Private); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), (MemoryLocation::CpuToGpu, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::WriteCombined); - heap_desc.set_storage_mode(metal::MTLStorageMode::Shared); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::WriteCombined); + heap_desc.setStorageMode(metal::MTLStorageMode::Shared); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), (MemoryLocation::GpuToCpu, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::DefaultCache); - heap_desc.set_storage_mode(metal::MTLStorageMode::Shared); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::DefaultCache); + heap_desc.setStorageMode(metal::MTLStorageMode::Shared); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), ]; @@ -482,15 +513,15 @@ impl Allocator { Ok(()) } - pub fn get_heaps(&self) -> Vec<&metal::HeapRef> { - // Get all memory blocks - let mut heaps: Vec<&metal::HeapRef> = Vec::new(); - for memory_type in &self.memory_types { - for block in memory_type.memory_blocks.iter().flatten() { - heaps.push(block.heap.as_ref()); - } - } - heaps + /// Returns heaps for all memory blocks + pub fn heaps(&self) -> impl Iterator> { + self.memory_types.iter().flat_map(|memory_type| { + memory_type + .memory_blocks + .iter() + .flatten() + .map(|block| block.heap.as_ref()) + }) } pub fn generate_report(&self) -> AllocatorReport { diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 560d7cb..bb189d4 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,5 +1,3 @@ -#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] - #[cfg(feature = "visualizer")] mod visualizer; use std::{backtrace::Backtrace, fmt, marker::PhantomData, sync::Arc};