|
|
Donner SVG 0.8.0-pre
SVG editor and embeddable C++20 engine.
|
Status: Design\ Author: Claude Opus 5\ Created: 2026-08-24
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 a measured binary-size budget for the runtime. 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 |
| 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 | Frozen pixel identity, baselined adapter | PR-gated | baseline_pixels_tests, on an adapter with a committed baseline |
| macOS arm64 | Frozen pixel identity, unbaselined adapter | None (fails closed) | baseline_pixels_tests fails on an automated lane rather than skipping |
| 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 | Dev-host | msl_xcrun_validation_tests self-skips when the offline compiler is absent |
| 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 | Frozen pixel identity, software adapter | PR-gated | baseline_pixels_tests, against the committed lavapipe baseline |
| Linux, SPIR-V validation | spirv-val | PR-gated | spirv_val_validation_tests; only one lane installs SPIRV-Tools |
| Linux | Vulkan validation layers | None | No lane enables them; the design requires zero validation errors |
| 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 | 0053 requires physical iOS presentation checks |
Notes:
These are the combinations 0053's gates depend on that nothing currently 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.
-c opt -Os with function and data sections, macOS arm64, at revision e1fc7cdb2925464a39eed7b2b29fa1d2475426c6:
llvm-size reports __TEXT and __DATA per archive member; the figures below are their sum, which excludes the debug information the archive also carries. The tree has no size target for a native GPU artifact, because there is no binary that links the runtime without also linking the dependency it replaces, so the archive is the measurable unit today.
| Archive | __TEXT | __DATA | Code + data |
|---|---|---|---|
| //donner/gpu:gpu | 285,328 | 1,600 | 286,928 |
| //donner/gpu/shader:shader | 594,879 | 4,184 | 599,063 |
| //donner/gpu/shader:programs | 93,937 | 120 | 94,057 |
| //donner/gpu/metal:metal_device | 83,121 | 344 | 83,465 |
| //donner/gpu/vulkan:vulkan_device | 154,666 | 392 | 155,058 |
The shader archive splits into an IR core and three independent emitters, and a shipped configuration needs one emitter, so the split matters more than the total:
| Component | Code + data |
|---|---|
| Shader IR core (types, layout, module, expressions, serialization) | 349,192 |
| MSL emitter | 86,407 |
| SPIR-V emitter | 88,954 |
| WGSL emitter | 74,510 |
Runtime core plus the IR, one emitter, the shader programs, and one backend:
| Configuration | Measured code + data | Approx. |
|---|---|---|
| Metal | 900,049 | 879 KiB |
| Vulkan | 974,189 | 951 KiB |
| Browser bridge (no bridge yet) | 804,687 | 786 KiB |
This is roughly twice the 0.3-0.5 MB the original design guessed at, which is the reason 0053 refused to accept that number without measurement.
The browser product is the one GPU-carrying artifact that already ships and already has enforced budgets. Measured with the gate that enforces them, so the numbers and the gate cannot drift:
| Metric | Measured | Committed budget | Headroom |
|---|---|---|---|
| editor.wasm raw | 13,315,235 | 13,449,000 | 133,765 |
| editor.wasm gzip | 5,237,458 | 5,290,000 | 52,542 |
| editor.js raw | 175,205 | 176,600 | 1,395 |
| editor.js gzip | 49,318 | 49,710 | 392 |
| Package raw | 13,531,996 | 13,668,000 | 136,004 |
| Largest Wasm function body | 28,417 | 46,000 | 17,583 |
| Passive data segments | 3 | 64 | 61 |
These budgets are left exactly as they are. The two JavaScript gates are the tight ones, at 0.8% and 0.8% of headroom, which is the margin they were deliberately retuned to; the change that grows the glue retunes them, as every previous one did.
The comparison worth keeping in view: the whole native GPU runtime measures under a megabyte of code and data, against a browser package of 13.5 MB raw and 5.2 MB compressed. Removing the current dependency and adding the runtime is not a size problem for the browser product; the budgets below exist to keep it from becoming one.
The measured figure is a pre-link upper bound: the archives are built with function and data sections and the platform linker discards what a given artifact does not reach, so a linked artifact's contribution is smaller. A budget stated against the archive is therefore conservative in the safe direction, and it is measurable today, which a post-link figure is not until an artifact exists that links the runtime alone.
| Gate | Budget | Basis |
|---|---|---|
| Metal runtime, code + data | 990,000 | 10% over the measured 900,049 |
| Vulkan runtime, code + data | 1,072,000 | 10% over the measured 974,189 |
| Browser bridge runtime, code + data | 1,000,000 | 804,687 measured with no bridge implementation, leaving room for one |
| Shader IR core, code + data | 384,000 | 10% over the measured 349,192 |
| Any single emitter, code + data | 98,000 | 10% over the largest measured emitter |
Ten percent, rather than the one percent the editor Wasm gates use, because these components are still being written: the Wasm budgets sit above a finished artifact and exist to catch incidental growth, while these sit above a runtime whose backends are partially implemented. Tighten each one to a one percent margin when its platform reaches cutover, at which point the gate should also move to the linked artifact.
Deliberately not proposed: a budget on the shipped editor Wasm package. That artifact already has committed budgets that the changes moving it retune, and adding a second gate over the same bytes would only produce two numbers to keep in sync.
RecordingDevice is 61,995 bytes of code inside the runtime core, or roughly a fifth of it. It is the deterministic capture backend, and it is linked into every configuration that links the runtime. If the shipped product does not need to record command streams, excluding it is the single largest available reduction to the core, and it should be decided before the budgets above are tightened rather than after.
| Claim in this document | Enforced by |
|---|---|
| The frozen corpus's structural counters are reproducible | //donner/gpu/baseline:baseline_counters_tests |
| Frozen pixels are reproducible on a baselined adapter | //donner/gpu/baseline:baseline_pixels_tests |
| A frozen baseline names the revision it came from | //donner/gpu/baseline:baseline_counters_tests |
| The editor Wasm package fits its budgets | //donner/editor/wasm:wasm_geode_package_size_tests |
| The native runtime fits the budgets above | Nothing yet. See the open question below. |
The last row is the honest state: the numbers above are recorded measurements and proposed budgets, not an enforced gate. Making them a gate needs a size target over the archives or, better, over a linked artifact once one exists. Until then a reader should treat the budgets as a review checklist item, not as an invariant.