8000 metal: make a shader from a precompiled Metal library by benface · Pull Request #681 · not-fl3/miniquad · GitHub
[go: up one dir, main page]

Skip to content

metal: make a shader from a precompiled Metal library - #681

Open
benface wants to merge 2 commits into
not-fl3:masterfrom
benface:metal-library-shaders
Open

benface wants to merge 2 commits into
not-fl3:masterfrom
benface:metal-library-shaders

Conversation

@benface
@benface benface commented Oct 10, 2026 •
Copy link
Copy Markdown
Contributor

Why

On Metal, new_shader compiles MSL source at runtime (newLibraryWithSource:options:error:). On a device that hasn't cached a shader yet, that takes tens of milliseconds a shader: 21 to 155 ms, about 70 on average, on an iPhone 17 Pro. An app pays it for every shader on its first launch, after a system update, and for each shader an update changes.

Apple's answer is to compile shaders as the app builds, into a library the app loads with newLibraryWithData:error:. miniquad had no way to make a shader from one.

Change

ShaderSource::MetalLibrary { data: &[u8] } takes the bytes of a library compiled ahead of time, as xcrun metal writes one, holding vertexShader and fragmentShader, the same functions an MSL source must define. The Metal backend wraps the bytes in a dispatch_data_t (copied, so the slice needn't outlive the call), loads the library from them, and goes on exactly as it does for a library compiled from source. If Metal can't load the library, new_shader returns a ShaderError::LinkError with Metal's description, such as "Invalid library file", rather than panicking as it does on MSL that fails to compile: a library is a file the app ships, and if it's damaged, the app can still compile the shader's MSL. OpenGL panics on it, as it does on MSL source.

Adding a variant breaks an exhaustive match on ShaderSource outside miniquad. macroquad's QuadGl::make_pipeline has one, which looks for _ScreenTexture in the source; a library has none to look in, and _ScreenTexture doesn't work on Metal yet anyway. I'll open the one-line macroquad change once a miniquad release has this.

Measurements

A macroquad game (CHOMP) with 29 materials compiles and draws with each once on its loading screen. On an iPhone 17 Pro just after installing it, three launches each:

MSL source Libraries
Loading the materials 2.50 to 2.56 s 0.57 to 0.59 s

What's left is mostly Metal building the pipelines. Once the device has cached everything, it takes 25 to 28 ms either way.

Verification

  • The game's deterministic captures on macOS, 116 frames across every level type it has, are byte-identical whether its shaders come from source or from libraries built by xcrun --sdk macosx metal.
  • It runs on an iPhone 17 Pro with libraries built by xcrun --sdk iphoneos metal.
  • With every library damaged, the same captures compile each shader's MSL instead and draw the same pixels.
  • cargo check passes for aarch64-apple-darwin, aarch64-apple-ios and aarch64-apple-tvos.

🤖 Generated with Claude Code

benface and others added 2 commits October 10, 2026 00:46
`ShaderSource::MetalLibrary` takes the bytes of a library compiled
ahead of time, as `xcrun metal` writes one, holding `vertexShader`
and `fragmentShader`. The Metal backend loads it with
`newLibraryWithData:error:` instead of compiling MSL source with
`newLibraryWithSource:options:error:`. OpenGL panics on it, as it
does on MSL source.

A device that hasn't cached a shader compiles its MSL in tens of
milliseconds: 21 to 155 ms, about 70 on average, on an iPhone 17 Pro.
An app pays that for every shader on its first launch, after a system
update, and for each shader an update changes. A library compiled as
the app builds skips it, leaving only the pipelines to build.

A macroquad game (CHOMP) with 29 materials, which compiles and draws
each once on its loading screen: on an iPhone 17 Pro just after
installing it, that took 2.50 to 2.56 s from source and 0.57 to 0.59 s
from libraries. Its deterministic captures on macOS draw the same
pixels either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`new_shader` panicked when Metal couldn't load a `MetalLibrary`, as it
does on MSL that fails to compile. A library is a file the app ships,
though, and if it's damaged, the app can still compile the shader's MSL.
So it now returns a `ShaderError::LinkError` with Metal's description,
such as "Invalid library file".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.

1 participant

0