Repository navigation
Conversation
Contributor
Author
|
The Android check failed before it checked out the code: Docker Hub refused to pull the |
A Metal pipeline state is built for one set of attachment formats, and
Metal only draws with it into a render pass whose attachments match.
Every pipeline state was built for the view's: its color format, its
depth/stencil format and its sample count. A render target that
differs in any of them failed validation as soon as a pipeline was
applied in it. On macOS, where the view has a depth/stencil buffer,
that's every render target made without a depth texture:
For depth attachment, the renderPipelineState pixelFormat must be
MTLPixelFormatInvalid, as no texture is set.
For stencil attachment, the renderPipelineState pixelFormat must
be MTLPixelFormatInvalid, as no texture is set.
With `sample_count: 4`, every 1x render target failed too, and with
the default of 1, every multisampled one:
For color attachment 0, the texture sample count (1) does not
match the renderPipelineState colorSampleCount (4).
A pipeline now keeps its descriptor. It still builds the view's
pipeline state when it's created, so an app that only draws to the
screen compiles the same state at the same time as before. The first
time a pass whose attachments differ applies it, it builds a state for
those formats too, and keeps it for later passes.
Only other formats are built on first use: a pipeline doesn't know
which render targets it will draw into, and building every combination
up front would make every app compile states it never uses. An app
that doesn't want a compile mid-frame can draw into each kind of
target once while it loads.
Verified on macOS with `MTL_DEBUG_LAYER=1`: with the view at 1x and at
4x, drawing into the window and into 1x and 4x render targets, with
and without depth, in the same frame raises no validation error. With
only the window drawn into, no pipeline state is built after
`new_pipeline`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
benface
force-pushed
the
metal-pipeline-target-formats
branch
from
October 18, 2026 18:08
5336a21 to
f6d66e7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A Metal pipeline state is built for one set of attachment formats, and Metal only draws with it into a render pass whose attachments match. Every pipeline state is built for the view's: its color format, its depth/stencil format and its sample count. So a render target that differs in any of them fails validation as soon as a pipeline is applied in it.
On macOS, where the view has a depth/stencil buffer, that's every render target made without a depth texture, macroquad's
render_targetamong them:With
sample_count: 4, every 1x render target fails too, and with the default of 1, every multisampled one:Change
new_pipelinestill builds the view's pipeline state. So an app that only draws to the screen compiles the same state at the same time as before, and nothing moves into its first frame.begin_passreads the pass's formats from its attachments. The first time a pass whose formats differ applies the pipeline, it builds a state for those formats and keeps it for later passes.Why only render targets' formats are built on first use
A pipeline doesn't know which render targets it will draw into. Building every combination of color format, depth/stencil format and sample count up front would make every app compile states it never uses, and each compile takes milliseconds: 3–25 ms on an iPhone 17 Pro with nothing cached, in a macroquad game (CHOMP). The view's formats are known when the pipeline is made, so its state is still built there. An app that wants no compile mid-frame can draw into each kind of render target once while it loads.
Verification
On macOS with
MTL_DEBUG_LAYER=1, a small program draws a triangle into the window and into 1x and 4x render targets, with and without depth, in the same frame. It's a debug build with #673 applied, since without it the first draw fails validation for another reason.new_pipeline, one for each render target whose formats differ from the view's during the first frame, and none after.new_pipeline.CHOMP draws into multisampled render targets without depth every frame, 8000 for its shadow masks and its recordings. With this change, five of its levels, 2,579 frames, play through under validation on macOS with no error.
cargo checkpasses foraarch64-apple-darwin,aarch64-apple-iosandaarch64-apple-tvos.🤖 Generated with Claude Code