Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use objc2-metal #5641

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Open

Use objc2-metal #5641

wants to merge 3 commits into from

Conversation

madsmtm
Copy link

@madsmtm madsmtm commented Apr 30, 2024

Description

Use the objc2-metal crate instead of the metal crate. This will:

  • Improve memory management and soundness.
  • Make it easier to quickly support new Metal APIs (they're either already generated for you, or is basically just an update of Xcode away).
  • Likely allow reducing the usage of Arc and Mutex, as Metal objects are already reference-counted (depending on thread-safety details, not entirely sure).
  • Likely improve performance, we use objc_retainAutoreleasedReturnValue underneath the hood to avoid putting objects into the autorelease pool when possible, reducing memory pressure.

Background

The metal crate contains bindings to the Metal framework. This uses objc to manually perform the message sends, which is quite error-prone, see gfx-rs/metal-rs#284, gfx-rs/metal-rs#319 and gfx-rs/metal-rs#209 for a few examples of unsoundness (out of many).

To solve such problems in the Rust ecosystem in general, I created a successor to objc called objc2, which contains most notably the smart-pointer Retained and the macro msg_send_id!, which together ensure that Objective-C's memory-management rules are upheld correctly.

This is only part of the solution though - we'd still have to write the bindings manually. To solve this, I created a tool (planning to integrate it with bindgen, but that's likely a multi-year project) to generate such framework crates automatically. In acknowledgement that this tool is by far not perfect, and never will be, I've ensured that there's a bunch of options to modify each generated crate.

The modifications for objc2-metal in particular are currently just a few hundred lines of code, weak evidence that the generator is fairly good at this point. I'll also bring attention to the file where unsafe methods are marked safe - I have plans to investigate ways to semi-automatically figure out if something is safe or not, or at least reduce the burden of doing so, but it's a hard problem to ensure is completely sound, so for now it's a bit verbose.

Connections

gpu-allocator is also transitioning to objc2-metal in Traverse-Research/gpu-allocator#225.

gfx-rs/metal-rs#241 is an old open PR for using objc2 in metal internally instead. There currently isn't really a clear path forwards there, and it's a lot of work for less direct benefits to the ecosystem (wgpu-hal is by far the biggest user of metal). But more fundamentally IMO, it's a problem of separation of concerns; metal defines several Foundation types like NSArray, NSString, NSError and so on, and even CAMetalLayer from QuartzCore, and that's a bad idea for interoperability compared to having separate crates for each of these frameworks.

Implementation

The first commit removes the link feature which is not available in these crates. This was added by @crowlKats in #3853, but Deno doesn't actually use it from what I can tell, they instead specify this using -weak_framework, which is the correct solution to this problem IMO. (If I'm wrong about this, please say so, I could be persuaded to add a similar feature to the objc2-... crates).

The second commit implements the actual migration, by using a branch of objc2, with a method naming scheme that (more closely) matches metal, to make it easier to review and test what's changed.

The third commit moves to the real naming scheme that objc2 uses.

I'd strongly recommend you review these two commits separately.

Testing

Tested by using the checklist below, as well as running each example individually, and checking that they seem to work.

During the development of this I made two quite critical typos, which were luckily found by the test suite, but there's bound to be at least one more lurking in here somewhere, please test this thoroughly!

Checklist

  • Run cargo fmt.
  • Run cargo clippy.
  • Run cargo xtask test to run tests.
  • Add change to CHANGELOG.md. See simple instructions inside file.

madsmtm added a commit to madsmtm/objc2 that referenced this pull request May 20, 2024
This may technically be a breaking change if the user implemented these
protocols themselves on a `Mutable` class, but that'd be unsound anyhow,
so I'll consider this a correctness fix.

This is useful for wgpu, see gfx-rs/wgpu#5641,
and the hack will become unnecessary after
#563.
@madsmtm madsmtm force-pushed the objc2-metal branch 2 times, most recently from 92d3ca3 to bd69f0d Compare May 22, 2024 21:42
To keep the diff smaller and easier to review, this uses a temporary
fork of `objc2-metal` and `objc2-quartz-core` whose methods use the
naming scheme of the `metal` crate.

One particular difficult part with this is that the `metal` crate has
several methods where the order of the arguments are swapped relative
to the corresponding Objective-C methods.

This includes most perilously (since these have both an offset and an
index argument, both of which are integers):
- `set_bytes`
- `set_vertex_bytes`
- `set_fragment_bytes`
- `set_buffer`
- `set_vertex_buffer`
- `set_fragment_buffer`
- `set_threadgroup_memory_length`

But also:
- `set_vertex_texture`
- `set_fragment_texture`
- `set_sampler_state`
- `set_vertex_sampler_state`
- `set_fragment_sampler_state`
@madsmtm madsmtm marked this pull request as ready for review May 23, 2024 01:35
@madsmtm madsmtm requested a review from a team as a code owner May 23, 2024 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant