Skip to content

Implement bindless buffers on metal - #9081

Merged
cwfitzgerald merged 30 commits into
gfx-rs:trunkfrom
mate-h:m/bindless-buffer-metal
Jun 29, 2026
Merged

Implement bindless buffers on metal#9081
cwfitzgerald merged 30 commits into
gfx-rs:trunkfrom
mate-h:m/bindless-buffer-metal

Conversation

@mate-h

@mate-h mate-h commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Connections
Resolves #6741

Description
Problem: Metal on macOS did not support bindless storage buffers.

In wgpu-hal, encode buffer binding arrays using MTLBuffer argument encoder. Metal expects encoded pointers, not raw resource IDs. Creates an argument buffer from an encoder, encode each buffer, and register it in resources_to_use.

In naga MSL writer, emit device T* for buffer binding array elements (which are pointers to device memory) instead of the constant layout used for textures and samplers. For struct members behind binding arrays, emit -> instead of . because the elements are pointers.

In the adapter, expose the BUFFER_BINDING_ARRAY feature on supporting devices, with special handling for MTLCaptureDevice. This is a separate fix for Xcode frame capture, when some functions fail to invoke, in these cases fallback to feature table based limits. I referenced the metal feature table PDF.

Testing

Tested with Bevy Solari on metal / macOS which makes use of bindless storage buffers and raytracing acceleration structures.

Screenshot 2026-02-20 at 12 06 13 AM

Squash

Checklist

  • Run cargo fmt.
  • Run taplo format.
  • Run cargo clippy --tests. If applicable, add:
    • --target wasm32-unknown-unknown
  • Run cargo xtask test to run tests.
  • If this contains user-facing changes, add a CHANGELOG.md entry.

@inner-daemons inner-daemons self-assigned this Feb 20, 2026
@inner-daemons

Copy link
Copy Markdown
Collaborator

I'm excited to see this and plan on checking out the code. Thanks a ton for working on it!

@Vecvec Vecvec left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of things from a read through.

Comment thread wgpu-hal/src/metal/adapter.rs Outdated
Comment thread wgpu-hal/src/metal/adapter.rs Outdated

@inner-daemons inner-daemons left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so lots of thoughts here, mostly questions. Overall looks good. More comments would be great.

Big thing though is snapshot tests. I can't really review this change until I know what it generates and know that what it generates is correct. There might even be existing tests that you can just add METAL as a target to.

Thanks for putting in the work. I look forward to using this myself, and I'd like to get this landed soon!

Comment thread CHANGELOG.md Outdated
Comment thread naga/src/back/msl/writer.rs
Comment thread naga/src/proc/mod.rs
Comment thread wgpu-hal/src/metal/adapter.rs Outdated
Comment thread wgpu-hal/src/metal/adapter.rs Outdated
Comment thread wgpu-hal/src/metal/conv.rs Outdated
Comment thread wgpu-hal/src/metal/conv.rs Outdated
Comment thread naga/src/back/msl/writer.rs
Comment thread wgpu-hal/src/metal/device.rs Outdated
Comment thread wgpu-hal/src/metal/device.rs Outdated
@inner-daemons inner-daemons removed their assignment Feb 27, 2026
@inner-daemons

Copy link
Copy Markdown
Collaborator

@mate-h This will probably be assigned once you address at least some of the above comments.

@msvbg

msvbg commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Eagerly awaiting this one and will be testing the PR in my own project. I also volunteer to adopt this PR if @mate-h is busy.

@msvbg

msvbg commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

The PR appears to work as intended on my end, backported to wgpu v27. Will post back if I notice any issues.

msvbg added a commit to msvbg/wgpu that referenced this pull request Mar 21, 2026
Port of gfx-rs#9081 to the v27 metal crate API.

- naga: emit `device` pointer syntax for struct-based binding arrays in MSL,
  use `->` for member access, skip address space for BindingArray globals
- wgpu-hal: add BUFFER_BINDING_ARRAY feature flag on Metal (MSL 3.0 + Tier 2),
  use MTLArgumentEncoder to encode buffer pointer arrays in bind groups
- wgpu-hal: extend map_resource_usage for Buffer types, add
  pointer_array_argument_descriptor helper
@cwfitzgerald

Copy link
Copy Markdown
Member

@msvbg feel free to put up a new PR with things updated, just make sure to keep the authorship on the commits so it's properly attributed

@mate-h

mate-h commented Mar 21, 2026

Copy link
Copy Markdown
Contributor Author

Hi everyone quick update, I am working on addressing the remaining feedback by @inner-daemons and making sure the CI passes. Regarding the backport, for my understanding wondering why it is needed? Let me know @msvbg if you'd like to collaborate on this!

@msvbg

msvbg commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Oh my bad, I did not intend to push to this branch! I'm sorry, that was accidental. I didn't even realize I had push access to this. Can you hard reset/remove my commits?

@msvbg

msvbg commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Okay it seems that I misread the Github interface and I did not push to your branch. Phew! I needed a backport because I'm on an older version of wgpu. It's for my own use. Please disregard the confusion :)

As for collaboration, I have nothing further to add to the PR!

@inner-daemons

Copy link
Copy Markdown
Collaborator

@msvbg Just FYI, a back port of this is unlikely to be accepted. We only accept back ports for a few of the most critical bug fixes in every release cycle, and even then we mainly do it for the latest release (27 was 3 releases ago).

Thanks for your interest though!

@mate-h

mate-h commented Mar 23, 2026

Copy link
Copy Markdown
Contributor Author

All right I am finished addressing all the feedback, ready for review again.
for those that want to test out Solari with this PR I posted a branch to bevy: https://github.com/mate-h/bevy/tree/m/solari-metal

checkout this PR locally at wgpu repo. checkout bevy repo at the branch linked above. place this file to bevy/.cargo/config.toml

[patch.crates-io]
wgpu = { path = "../wgpu/wgpu" }
wgpu-core = { path = "../wgpu/wgpu-core" }
wgpu-hal = { path = "../wgpu/wgpu-hal" }
wgpu-types = { path = "../wgpu/wgpu-types" }
wgpu-macros = { path = "../wgpu/wgpu-macros" }
naga = { path = "../wgpu/naga" }
naga_oil = { path = "../naga_oil" }

last line is a local git clone of naga master branch or set to git dep.

@inner-daemons
inner-daemons self-requested a review April 8, 2026 20:24

@inner-daemons inner-daemons left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna tentatively approve this, since the biggest points have been addressed. I haven't tried incredibly hard yet to reason through all of the writer changes since my last review but what I've noticed seems reasonable.

Comment thread naga/src/proc/mod.rs
Comment thread naga/tests/in/wgsl/binding-buffer-arrays.toml Outdated
Comment thread wgpu-hal/src/metal/adapter.rs Outdated
Comment thread wgpu-hal/src/metal/adapter.rs
@inner-daemons

Copy link
Copy Markdown
Collaborator

@cwfitzgerald Feel free to take a look

@cwfitzgerald cwfitzgerald left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making solid progress! Some more minor and pedantic issues and a bit more testing

Comment thread naga/src/back/msl/writer.rs Outdated
Comment thread naga/tests/out/msl/wgsl-binding-buffer-arrays.metal Outdated
Comment thread wgpu-hal/src/metal/adapter.rs Outdated
Comment thread wgpu-hal/src/metal/adapter.rs
Comment thread Cargo.toml Outdated
Comment thread wgpu-hal/src/metal/device.rs
Comment thread wgpu-hal/src/metal/device.rs Outdated
Comment thread wgpu-hal/src/metal/device.rs Outdated
@inner-daemons

Copy link
Copy Markdown
Collaborator

This is still waiting on author

@msvbg

msvbg commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Is this still being worked on?

@mate-h

mate-h commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

I have not worked on this in a while, I'll come back to it now! I got some more feedback from @cwfitzgerald that I need to address. And also merge in the trunk branch again.

@cwfitzgerald cwfitzgerald left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the progress, some more comments, mainly around testing

Comment thread naga-test/tests/binding_array_length_map_toml.rs Outdated
Comment thread naga/tests/in/wgsl/binding-buffer-arrays.wgsl
Comment thread wgpu-hal/src/metal/adapter.rs
@mate-h

mate-h commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@cwfitzgerald thanks for your continued feedback! I have addressed every outstanding comment on this PR, let me know if there's anything else to resolve.

@cwfitzgerald cwfitzgerald left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving, looks good with one caveat that we should make an issue for.

int _e104 = uint(0) < 10 && _buffer_sizes.size0[0u] != 0u && uint(non_uniform_index) < 10 && _buffer_sizes.size0[unsigned(non_uniform_index)] != 0u ? storage_array[non_uniform_index].inner->far[0] : DefaultConstructible();
u1_ = _e99 + as_type<uint>(_e104);
uint _e107 = u1_;
int _e112 = uint(0) < 10 && _buffer_sizes.size0[0u] != 0u && uint(7) < 10 && _buffer_sizes.size0[7u] != 0u ? storage_array[7].inner->far[0] : DefaultConstructible();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this interact with partially bound binding arrays. We're introducing bounds checks on the binding array itself, but with partially bound arrays the size isn't known to the shader. This is definitely an improvement than before where we had no bounds checks at all, so feel free to push this to an issue/follow up, but something to consider.

@cwfitzgerald
cwfitzgerald merged commit bf5c6cf into gfx-rs:trunk Jun 29, 2026
59 checks passed
pull Bot pushed a commit to orzogc/bevy that referenced this pull request Jul 28, 2026
# Objective

[Ray tracing on Metal](gfx-rs/wgpu#8071) has
been available since wgpu 29.

I shipped a PR to wgpu for [implementing bindless storage
buffers](gfx-rs/wgpu#9081) for metal, which was
released with wgpu 30. With the [wgpu 30
PR](bevyengine#24841) in bevy merged, we
are fully ready to add support to Solari on metal backends!

However because there is a limit of 31 buffer slots per shader stage.
Running Solari on main on a raytracing compatible Mac results in a
crash.

## Solution

I had combine the world cache into a single packed buffer. In theory
this should not introduce performance regressions since it’s only
reorganizing bindings and doesn’t introduce and copy or other passes.
Tried keeping the diff fairly minimal and easy to review.

Getting the buffer layout for the world cache on the CPU with
`ShaderType` was tricky. To avoid blowing up memory I put it in a
private module so you can't construct it.

## Testing

- Ran the Solari example on mac

```
cargo run --example solari --features bevy_solari,https,free_camera
```

Currently this PR needs a benchmark against main to ensure there's no
perf regression. I could use some help with that!

---

## Showcase

<img width="2566" height="1514" alt="shot"
src="https://github.com/user-attachments/assets/691b9bd3-129f-47e6-8efe-313cbadd6ff5"
/>

## Known limitations

Tracing spans incorrectly report 0.0ms , tracking in
gfx-rs/wgpu#9414

No denoising support yet. Could theoretically use [MetalFX Ray
Reconstruction](https://crates.io/crates/objc2-metal-fx) in place of
DLSS for cross-platform denoising support in Solari. In the longer term
we are also looking at Open Image Denoise 3.0 that promises temporal
denoising support.

---------

Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
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.

Support binding_array of Storage Buffers on Metal

5 participants