CNA-GAP-013: METAL runs custom ShaderEffects only through SpriteBatch, refuses a partial MultiSampleMask for custom and compiled effects, and presents PresentInterval.Two as One
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c1c316b9; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
Since the Apple campaign METAL has MRT, occlusion queries, instancing, readback, MSAA and compiled effects; a ShaderEffect on a 3D draw is still refused, as is a partial MultiSampleMask for custom and compiled effects, and Two presents as One.
- Identifier
CNA-GAP-013- Category
- Functional gap
- Subsystem
- Graphics & renderers
- Status
- Narrowed (partially fixed; describes only what survives)
- Verified against
- CNA
c1c316b9(c1c316b9c7a846ce8002809c151fcd1af14942c9) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- ShaderEffect on 3D draws, BlendState.MultiSampleMask with custom or compiled effects, and PresentationParameters.PresentationInterval on the METAL renderer
Expected behaviour
A renderer identity is expected to support the XNA HiDef surface or refuse clearly. XNA draws any Effect on any primitive draw, applies BlendState.MultiSampleMask to every draw on a multisampled target, and PresentInterval.Two presents every second vertical retrace.
Actual behaviour at TARGET
- Fixed since first recorded, by the Apple campaign: multiple render targets (AM4-097), occlusion queries (AM4-038), instancing and multi-stream vertex input (AM4-143), back-buffer readback (AM4-025), multisampling chosen per device (AM4-141), sampler
MaxMipLeveland LOD bias (AM4-034; the bias only from macOS/iOS 26),ColorWriteChannelson every target (AM4-079, AM4-146),MultiSampleMaskon single-sample targets (AM4-135), SpriteBatch custom effects in MSL (AM4-077) and compiled XNA effects through MojoShader and SPIRV-Cross (AM4-144, opt-in withCNA_METAL_COMPILED_EFFECTS).MetalSupportsCapabilityinMetalPolicy.hppnow answers true for all of them. - Still refused, with
System::NotSupportedException, inMetalRenderer.mm: a 3D draw with aShaderEffectbound (‘Metal custom effects are a SpriteBatch facility … 3D draws with one are not supported’;metal-shader-effect-contract.md), and aMultiSampleMaskthat keeps only some samples of a multisampled target when the draw uses a SpriteBatch custom effect or a compiled effect, because neither kind of MSL is given a[[sample_mask]]output (AM4-141). - Still approximated:
SetSwapIntervalmaps the interval ontoCAMetalLayer.displaySyncEnabled, which is a boolean, soPresentInterval.Twopresents likeOne, without an error.
Source locations
modules/renderers/metal/src/MetalRenderer.mm— the remaining NotSupportedException refusals (custom effect on a 3D draw; partial MultiSampleMask for SpriteBatch custom and compiled effects) and SetSwapInterval (displaySyncEnabled)modules/renderers/metal/include/CNA/Internal/Renderers/Metal/MetalPolicy.hpp— MetalSupportsCapability: MRT, occlusion, custom effects, multi-stream input and instancing now truedocs/metal-shader-effect-contract.md— custom ShaderEffects are a SpriteBatch facilitydocs/apple-platforms.md— macOS status table: what is left for METALmodules/renderers/metal/examples/CMakeLists.txt— the registered Metal testsmodules/renderers/metal/examples/metal_spritebatch_customeffect_test.cpp— the SpriteBatch custom-effect pixel test, now registered
Evidence
Checked by reading the named sources at c1c316b9; nothing was built or executed for this entry. The refusals are explicit and documented in the messages; macOS was not available.
Re-verified at c1c316b9: read MetalSupportsCapability, every NotSupportedException in MetalRenderer.mm, SetSwapInterval and the macOS status table of apple-platforms.md, whose ‘Left’ column for METAL names the same SpriteBatch-scoped custom effects and the PresentInterval.Two mapping; not executed. CNA's own records (AM4-208) report the full METAL test tree passing on a Mac mini M4.
Independently reported as CNA-VGAP-050 (folded into this entry by the adversarial audit): The Metal examples now register the SpriteBatch custom-effect pixel test and MetalRenderer runs SpriteBatch custom effects in MSL, but a 3D draw with a custom effect is still refused.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
The Metal examples now register 18 Metal tests of their own (among them the formerly unregistered SpriteBatch custom-effect test), and the shared pixel fixtures and renderer-neutral oracles run on Metal; MetalPolicyTests.cpp pins the multisample-mask judgement. No test draws a 3D primitive with a ShaderEffect on Metal or measures the cadence of PresentInterval.Two.
Regression test
A Metal pixel test that draws a 3D primitive with a custom effect, one that applies a partial MultiSampleMask under a compiled effect on a multisampled target, and a presentation-cadence check for PresentInterval.Two, each enabled with the feature.
Blast radius
Games on macOS or iOS with the METAL renderer that draw 3D geometry with a ShaderEffect, combine a custom or compiled effect with a partial MultiSampleMask, or rely on PresentInterval.Two halving the frame rate. Stock effects, SpriteBatch with or without custom effects, and every other renderer are unaffected.
Workaround
Write 3D custom shading as a compiled XNA effect (CNA_METAL_COMPILED_EFFECTS=ON) rather than a ShaderEffect, avoid partial multisample masks with custom effects, and pace frames in the game when a 30 Hz cap is needed; or use another renderer on Apple platforms.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Draw-call internals: other backends
- Known issues
- Functional gap index