Status: Design\ Author: Claude Opus 5\ Created: 2026-08-24\ Updated: 2026-10-06
0053: Native GPU runtime makes a platform native only when it passes a per-platform cutover gate, and two of the inputs to that gate did not exist: the list of platform-and-driver combinations a release is actually blocked on, and measured binary-size budgets for the shipped products. This document supplies both.
The matrix below is derived from the lanes and targets in this repository, not from intent. Every row says which of three things is true today: the combination is exercised by a CI lane, it is only reachable on a developer machine, or nothing anywhere runs it. Several rows in the third category are combinations 0053 names as release-blocking, and stating that plainly is the point of the document. A fourth label separates the combinations the project has decided not to ship from the ones it has not gotten to: an uncovered target is a gap, a non-target is not.
| Label | Meaning |
|---|---|
| PR-gated | Executed on every pull request that reaches the lane, and blocks merge. |
| Conditional | PR-gated only when a path filter or label matches. |
| Scheduled | Executed on a nightly or weekly schedule; a regression lands before it fires. |
| Compile-only | Built by a lane that never executes it. Proves it links, proves nothing else. |
| Dev-host | Runnable, but only by a person on a machine; no lane executes it. |
| None | Nothing in the repository executes this combination. |
| Out of scope | Not a target: the project has decided not to ship it, so it is not a gap. |
| Fails closed | Not covered, and an automated lane says so by going red instead of green. |
A target that self-skips when its device is missing is only coverage on a lane that actually has the device, and Bazel reports an all-skipped target as passing. Where that distinction matters it is called out in the notes; a label here describes what a lane asserts, not what it reports.
| Platform | Driver / adapter | Coverage | Where |
|---|---|---|---|
| macOS arm64, deployment 13.3+ | Apple Silicon integrated | PR-gated | metal_solid_fill_tests, which fails rather than skips without a device and compares against the literal-fill solid golden |
| macOS arm64 | Apple Silicon, Geode variant wrappers | PR-gated, device-conditional | The *_geode wrappers on the macOS lane |
| macOS arm64 | Apple Silicon, ImGui/editor presentation | PR-gated, device-conditional | The macOS --config=geode editor lane's five explicit targets |
| macOS arm64 | Apple Silicon, literal-fill goldens | PR-gated, device-conditional | renderer_geode_golden_tests literal-fill cases, one golden per scene |
| macOS arm64 | Two or more Apple GPU generations | None | One macOS lane runs per pull request; nothing compares generations |
| macOS x86_64 | Intel integrated / AMD discrete | None | Every macOS runner label in the tree is arm64 |
| macOS arm64, Metal API validation | Apple Silicon | PR-gated | MTL_DEBUG_LAYER and MTL_SHADER_VALIDATION on the Metal slice target |
| macOS, offline MSL compilation | Platform Metal toolchain | PR-gated (operator PRs and main) | msl_xcrun_validation_tests; hosted PRs exclude it by tag because that image has no offline compiler |
| iOS / iPadOS | Apple Silicon | None | No target, no lane, no runner |
Notes:
| Platform | Driver / adapter | Coverage | Where |
|---|---|---|---|
| Linux x86_64 | Mesa lavapipe (software) | PR-gated | vulkan_solid_fill_tests, ICD pinned to lvp_icd.json |
| Linux arm64 | Mesa lavapipe (software) | PR-gated | The self-hosted Linux routing, when it is the selected lane |
| Linux x86_64/arm64 | Mesa lavapipe, literal-fill goldens | PR-gated | renderer_geode_golden_tests literal-fill cases and vulkan_solid_fill_tests, one golden per scene |
| Every lane, SPIR-V validation | spirv-val | PR-gated | spirv_val_validation_tests; Bazel builds the validator from source and hands it over in runfiles, so no lane can be missing it |
| Linux | Vulkan validation layers | None | No lane enables them; the cutover relied on development runs |
| Linux x86_64/arm64 | Intel physical | None | No lane has a GPU device; the shared executor advertises none |
| Linux x86_64 | AMD physical | None | As above |
| Linux x86_64 | NVIDIA physical | None | As above |
| Linux, Geode + ASan | Mesa lavapipe | Conditional | Fires only when the Geode renderer paths change |
| Linux, Geode fuzzing | Mesa lavapipe | Scheduled | Nightly |
| Windows | Any Vulkan driver | Out of scope | Windows is not a target platform; nothing is planned for it |
Notes:
| Browser | Host | Coverage | Where |
|---|---|---|---|
| Chromium, headless | macOS arm64 | Conditional | Browser suites, path-filtered pull requests |
| Chromium, headless | macOS arm64 | Scheduled | The same suites, nightly |
| Chromium, headed on the platform GPU | macOS arm64 | Conditional | The composited-output lane, ANGLE over Metal |
| Firefox | macOS arm64 | Conditional | Resize and composited-invariant projects |
| WebKit (Playwright) | macOS arm64 | Conditional | Carousel project |
| Safari (the shipping browser) | macOS | Dev-host | A regression script exists; no workflow invokes it |
| Any browser | Linux | None | The pixel-presenting smoke target is macOS-arm64 only |
| Any browser | Windows | Out of scope | Windows is not a target platform |
| Mobile Safari | iOS / iPadOS | None | Outside the cutover GPU qualification matrix |
Notes:
These are the combinations that no CI lane executes. Windows is not among them: it is a non-target, listed as out of scope in the tables above rather than counted as a gap.
The native budgets are stated against the shipped products as linked: the macOS editor, the Linux editor, the GPU command-line render tool, //examples:svg_to_png built with Geode, and the released donner-svg CLI. //tools/ci:shipped_editor, //tools/ci:shipped_svg_to_png and //tools/ci:shipped_donner_svg build each one in the configuration it ships in: optimized (-c opt), with the Geode renderer, without the Tracy profiler client that development builds link, and with the default text tier. The editor applies its own full text tier on top, as it does in every build. donner-svg links the CPU renderer under every configuration, so its shipped build is the same program as the release build; //donner/svg/tool:donner_svg_excludes_geode_audit fails if the CLI gains a dependency on the Geode renderer.
tools/ci/native_linked_size.py measures a product as the program its executable maps from the file, split into read-only bytes (code, constants, unwind tables) and writable initialized data. For an ELF executable that is the allocated program sections, split by the write flag; for a Mach-O executable it is the sections of the __TEXT segment and of the data segments. Dynamic-linking metadata (ELF symbol, string, hash, version and relocation tables, which Mach-O keeps in __LINKEDIT), notes, debug information and zero-filled sections are excluded, so the figure follows the program the linker kept rather than how the file was stripped or linked. Both products link Donner and its dependencies statically; only system libraries are dynamic.
bazel test //tools/ci:native_linked_size_budget_test runs the same check with the committed budgets and prints one native-linked-size line per product.
Each budget is about 3% over the product's measured size, rounded up to a thousand bytes. All rows are read with tools/ci/native_linked_size.py, and read-only includes the shader artifacts. The Linux x86_64 and macOS arm64 rows are measured in the shipped configuration by the native Perf workflow, including declarative animation and CLI GIF export.
Optimized Linux links of the shipped binaries fold identical code (--icf=safe), as Apple's linker does by default, and the Linux rows are measured with it. --//build_defs:linker_icf=false turns it off for a host whose linker does not support it.
| Product | Platform | Read-only | Writable | Total | Budget |
|---|---|---|---|---|---|
| Editor | macOS arm64 | 10,315,561 | 2,586,340 | 12,901,901 | 13,289,000 |
| svg_to_png | macOS arm64 | 4,342,143 | 186,696 | 4,528,839 | 4,665,000 |
| Editor | Linux x86_64 | 11,891,859 | 2,500,312 | 14,392,171 | 14,824,000 |
| svg_to_png | Linux x86_64 | 5,143,175 | 117,120 | 5,260,295 | 5,419,000 |
| donner-svg | macOS arm64 | 3,746,741 | 149,792 | 3,896,533 | 4,014,000 |
| donner-svg | Linux x86_64 | 4,484,219 | 115,144 | 4,599,363 | 4,738,000 |
| Editor | Linux arm64 | 11,195,371 | 2,534,096 | 13,729,467 | Not gated |
| svg_to_png | Linux arm64 | 4,716,219 | 144,000 | 4,860,219 | Not gated |
The six gated rows bind to source 3b23358a21f9d5827cb472ab11be4e8f59dc196a and the native Perf configuration. Qualification run 39126317782 contains both platforms' measurements with compilation completed before serial uncached tests. The ceilings retain approximately 3% headroom; latency gates are independent.
The Linux arm64 figures are a reference measurement with the hermetic LLVM toolchain (--config=latest_llvm); no lane builds the optimized products there, so the gate does not apply to that platform. Neither product links the runtime shader emitters (//donner/gpu/shader:msl_emitter, :spirv_emitter, :wgsl_emitter) or //donner/gpu:recording_device: shader artifacts are compiled during constant evaluation, and all four libraries are testonly, so Bazel analysis rejects them in either product.
//tools/ci:native_linked_size_budget_test checks every product against the budgets for the platform it runs on. It is tagged perf and perf_linux, so the nightly Perf workflow builds and runs it in its macOS arm64 and Linux x86_64 jobs, which already build with -c opt. It is also tagged manual, so bazel test //... does not build the optimized products. No pull-request lane builds the editor or svg_to_png in the shipped configuration, so the gate is Scheduled: a regression lands before it fires. Gating pull requests would cost an optimized editor build per run on each native lane.
The browser product is enforced by //donner/editor/wasm:wasm_geode_package_size_tests in the Editor Wasm workflow. Measured with the gate that enforces it:
The size ceilings are about 2% over the measured package, rounded up.
| Metric | Measured | Ceiling |
|---|---|---|
| editor.wasm raw | 9,741,597 | 9,937,000 |
| editor.wasm gzip | 3,208,966 | 3,274,000 |
| editor.js raw | 188,005 | 191,800 |
| editor.js gzip | 51,894 | 53,000 |
| Package raw | 11,809,457 | 12,046,000 |
| Largest Wasm function body | 33,236 | 46,000 |
| Passive data segments | 3 | 64 |
The function-body and data-segment limits are structural limits and are not scaled with the package.
| Claim in this document | Enforced by |
|---|---|
| Each literal-fill scene matches its one golden on every lane that runs it | //donner/svg/renderer/tests:renderer_geode_golden_tests |
| The backend solid-fill renders match the same golden | //donner/gpu/metal/tests:metal_solid_fill_tests, //donner/gpu/vulkan/tests:vulkan_solid_fill_tests |
| The editor Wasm package fits its budgets | //donner/editor/wasm:wasm_geode_package_size_tests |
| The shipped native products fit the budgets above | //tools/ci:native_linked_size_budget_test |
| donner-svg never links the Geode renderer | //donner/svg/tool:donner_svg_excludes_geode_audit |
The native gate is Scheduled: it runs in the nightly Perf workflow, so a regression lands before it fires.