Skip to content

Commit

Permalink
🤘 metal: Migrate to objc2 architecture with objc2-metal bindings (#…
Browse files Browse the repository at this point in the history
…225)

* metal: Migrate to `objc2` architecture with `objc2-metal` bindings

The current `objc` crate stack is completely unmaintained and has
severely fallen out of date with no updates for over 4 years.  The
`metal-rs` crate, built on top of this architecture, is completely
written by hand which is tedious to keep up-to-date, not to mention
has inconsistencies in its implementation.

All of this is superseded by the new `objc2` crate stack built by
@madsmtm.  Beyond providing what seems like a better, safer abstraction
over Objective-C, _all_ bindings are completely autogenerated meaning
we'll no longer lag behind upstream bindings (requiring painstaking
manual patching) or have inconsistencies in the implementations, as long
as the generator is properly able to represent the bindings.

* Use `target_vendor = "apple"` instead of many custom `target_os`es

* Work around unused_qualifications lint for Rust 1.80 prelude extension

`size_of(_val)()` was added to the prelude in Rust 1.80, causing
`unused_qualifications` warnings whenever we qualify a call to it with
via `std::mem::size_of_val()`.

The easiest workaround is to remove the prefix and explicitly import the
function in scope.  We annotate the import with a `TODO` to remove it
once bumping our MSRV on or past 1.80.
  • Loading branch information
MarijnS95 authored Oct 3, 2024
1 parent a1aee6d commit 031b39d
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 107 deletions.
9 changes: 0 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 --features ${{ matrix.features }} --no-default-features -- -D warnings

Expand Down
23 changes: 19 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.29.0", default-features = false, features = ["link", "dispatch"], optional = true }
[target.'cfg(target_vendor = "apple")'.dependencies]
objc2 = { version = "0.5.2", default-features = false, optional = true }
objc2-foundation = { version = "0.2", default-features = false, optional = true }
objc2-metal = { version = "0.2.1", default-features = false, features = [
"MTLAccelerationStructure",
"MTLBuffer",
"MTLDevice",
"MTLHeap",
"MTLResource",
"MTLTexture",
"std",
], optional = true }

[target.'cfg(windows)'.dependencies]
# Only needed for public-winapi interop helpers
Expand Down Expand Up @@ -65,6 +75,11 @@ features = [
"Win32_Graphics_Dxgi_Common",
]

[target.'cfg(target_vendor = "apple")'.dev-dependencies]
objc2-metal = { version = "0.2.1", default-features = false, features = [
"MTLPixelFormat",
] }

[[example]]
name = "vulkan-buffer"
required-features = ["vulkan", "ash/loaded"]
Expand All @@ -85,8 +100,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"]
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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();
Expand Down
34 changes: 21 additions & 13 deletions examples/metal-buffer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
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());
// Allow the innards of objc2-metal to link the static function below:
// https://docs.rs/objc2-metal/0.2.2/objc2_metal/index.html
#[link(name = "CoreGraphics", kind = "framework")]
extern "C" {}

let device =
unsafe { Id::from_raw(metal::MTLCreateSystemDefaultDevice()) }.expect("No MTLDevice found");

// Setting up the allocator
let mut allocator = Allocator::new(&AllocatorCreateDesc {
Expand Down Expand Up @@ -60,11 +68,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();
Expand All @@ -75,14 +83,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();
Expand Down
10 changes: 8 additions & 2 deletions src/d3d12/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use std::{backtrace::Backtrace, fmt, sync::Arc};
use std::{
backtrace::Backtrace,
fmt,
// TODO: Remove when bumping MSRV to 1.80
mem::size_of_val,
sync::Arc,
};

use log::{debug, warn, Level};
use windows::Win32::{
Expand Down Expand Up @@ -628,7 +634,7 @@ impl Allocator {
device.CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS,
<*mut D3D12_FEATURE_DATA_D3D12_OPTIONS>::cast(&mut options),
std::mem::size_of_val(&options) as u32,
size_of_val(&options) as u32,
)
}
.map_err(|e| {
Expand Down
19 changes: 11 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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();
Expand All @@ -206,6 +208,7 @@
//! # #[cfg(not(feature = "metal"))]
//! # fn main() {}
//! ```
#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)]

mod result;
pub use result::*;
Expand All @@ -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)]
Expand Down
Loading

0 comments on commit 031b39d

Please sign in to comment.