Status: Living catalog. The CPU-backend (RendererTinySkia) feature gaps and bugs are the active front, and lead this backlog. Geode runs the same resvg Params and thresholds as the CPU variants; backend-specific override tables are not part of the suite policy.
The triage backlog for 0022's Milestone 2 — working through the tests the suite upgrade pulled in and either fixing the underlying gap or recording why a Params::Skip(...) is the correct state. Each entry corresponds to one or more skips/threshold-overrides in resvg_test_suite.cc.
When a gap is fixed, delete its entry here and un-skip the tests in the same PR. Golden overrides (where Donner is right and resvg's golden is wrong) live in 0009, not here.
Conventions:
There are five supported ways the suite records a known gap. All of them must be expressed through the normal Params path close to the affected tests:
| State | Count | Meaning |
|---|---|---|
| Params::Skip("reason") | 133 | Not run. Feature gap or known bug. The bulk of this doc. |
| Params::RenderOnly("reason") | 58 | Rendered, not compared. Used for UB/deprecated cases where no-crash coverage is still useful. |
| Commented-out INSTANTIATE_TEST_SUITE_P | 0 | Every vendored category is instantiated. |
| Params::WithThreshold(…, maxPx) / local max-pixel budget | 103 | Passes with an explicit threshold or pixel budget. Large non-text budgets remain suspect; see Masked bugs behind inflated CPU thresholds. |
| Geode-disabled local Params entries | 0 | Geode now runs every active resvg case. Verified analytic edge residuals use exact per-backend goldens instead of disabling the backend or inflating thresholds. |
| Count | |
|---|---|
| Params::Skip(...) | 133 (grep -o 'Params::Skip(') |
| Params::RenderOnly(...) | 58 (render-must-not-crash, no pixel compare) |
| WithThreshold / max-pixel overrides | 103 call sites: 79 WithThreshold, 11 WithMaxPixels, and 13 direct withMaxPixelsDifferent calls. Large non-text budgets remain masked-bug candidates. |
| Geode-disabled local Params entries | 0 (all active cases now run on Geode) |
| Commented-out category blocks | 0 |
Landed 2026-05-25 from a parallel CPU-backend debugging sweep. IDs are burned (not reused).
Highest-value first. "Out of scope" rows are correct-as-skipped and listed at the bottom for completeness.
Recently fixed (PRs #608–#611, in review) — see Recently fixed. F2 (transform-origin regression), B1 (intrinsic sizing), B5 (feMorphology), B6 (feImage resampling) are resolved; their IDs are burned. The rows below are what's left.
| ID | Gap | Impact | Kind |
|---|---|---|---|
| B2 | filters/filter-functions coverage | DONE (26/43 files compare) | Category active; 17 mismatches are explicit triage skips. |
| B3 | structure/image golden kernel-era mismatch | 13 | Golden refresh + <image> upscale-kernel decision (see B3) |
| F12 | transform-origin on <textPath> baseline | DONE | Resolved by #868; all category cases are active. |
| F7 | paint-order rendering | DONE (14/14) | Shapes and text run on both backends; on-tspan uses a project-owned oracle because the vendored PNG breaks cross-span kerning. |
| F9 | textLength + lengthAdjust stretch/compress | 8 | Feature |
| F10 | textPath SVG2 attributes (path/side/method/spacing) | 8 | Feature |
| F11 | BiDi / RTL text shaping | ~8 | Feature (needs text-full) |
| B7 | font substitution — missing bundled families (masked by fat thresholds) | ~9 | Triage: bundle fonts vs. document as known gap |
| — | masking edge cases (mask 3, clipPath 5) | 8 | mask-type and vector text clip children are active on both backends; remaining cases are units, transforms, color interpolation, bitmap text clips, nested intersections, and shorthand geometry. |
| — | uncertain Bug? entries (need triage) | ~12 | Needs investigation |
| F1 | enable-background + in=Background* | 23 | Out of scope (deprecated) |
| — | other deprecated/UB skips | ~30 | Out of scope |
Resolved as a coverage gap. All 43 files are instantiated. A combined TinySkia and Geode run executes 86 comparisons: 53 passed before exception classification, representing 26 files on both backends plus one TinySkia-only pass. Seventeen unique mismatching files now carry explicit normal-path Triage: skips for blur, color-adjust, drop-shadow, or URL-chain cases. No category remains dark, but these skips are not yet classified as Donner defects.
Next step: classify the 17 mismatches against the normative grammar and independent browser pixels, then fix only confirmed Donner defects through their owning blur, color-adjust, drop-shadow, and URL-chain paths. Remove each local skip only after that classification, a focused regression, and both comparison modes pass; do not restore the category-level disable.
These tests pass, but only because maxMismatchedPixels was raised far above the suite default (100). pixelmatch already excludes anti-aliased pixels, so a multi-thousand-px diff on the CPU backend is a real rendering difference. "AA drift" is not a valid reason for these magnitudes. The full audit list lives in the test file. B6 (feImage resampling) is now fixed — see Recently fixed; the real cause was a bilinear-vs-bicubic kernel, not the suspected transform bug, and the 3 "transform" tests were never broken (their fat thresholds were over-inflation, now removed). The remaining structural cluster is below.
Impact: ~9 text/font-family/ tests at maxPx 600–5200 (serif 4200, sans-serif 1900, monospace 600, cursive 5000, fantasy 5200, bold-sans-serif 5200, source-sans-pro 1300, font-list 1300, fallback-2 1000), plus text/text/xml-lang=ja (19100, CJK) and structure/defs/ style-inheritance-on-text (6500).
Symptom: the diffs are whole-glyph — Donner substitutes a different font than the golden was rendered with (the suite's cursive/fantasy/CJK families aren't bundled), so every glyph outline differs. This is not a renderer bug; it's a font-availability gap currently silently absorbed by a fat threshold.
Next step (triage decision): either (a) bundle the missing families and tighten the thresholds to default, or (b) reclassify these as explicit Skip("font not bundled: <family>") so the gap is visible instead of hidden. Do not leave them as unexplained fat thresholds. Decide per-family; serif/sans-serif/monospace likely map to already-bundled Noto faces (real diff to chase), while cursive/fantasy are genuinely missing.
The remaining sub-1000-px CPU thresholds (feColorMatrix matrix/saturate variants, feDropShadow, text-decoration rotate-lists, pattern AA) are small enough to be plausible coverage-geometry differences; audit opportunistically but they are not promoted bugs.
B1 (intrinsic sizing + percent on non-square viewBox) is now fixed — see Recently fixed. It was three coupled causes, not just the suspected transformPosition→transformVector (also per-axis percent extent + <marker> % parsing).
B4 (<use> referencing inline <svg> elements) is now fixed. Two coupled causes: (1) the <use> width/height override + viewport machinery (LayoutSystem::createShadowSizedElementComponent, ComputedShadowSizedElementComponent) only accepted <symbol> targets, never <svg>, so no instance viewport (clip) was created and the shadow content transform dropped the referenced svg's x/y when it had no viewBox; (2) a CSS shadow-tree bug — ShadowedElementAdapter::parentElement() looked up ElementTypeComponent on the raw tree entity, so a shadow entity whose parent was also a shadow entity appeared parentless, matched :root, and the UA rule svg:not(:root) { overflow: hidden } never clipped nested <use> → <use> → <svg> chains (descendant combinators through shadow parents were broken generally). 5 tests un-skipped (70k–130k px → pass).
Re-triaged (2026-07-03): the old "embedded data URLs render at wrong size" description was wrong. Donner's <image> placement, intrinsic sizing (no-width/no-height/auto), MIME sniffing, GIF decode, and preserveAspectRatio alignment are all correct (verified per-test against the goldens: alignment diffs are zero-displacement, residuals hug resampled edges only). The real gap: the vendored 2023 resvg-test-suite goldens were generated across several resvg eras with different <image> upscale kernels:
Plus one policy case: embedded-svg-with-text — resvg parses <image>-embedded SVGs with an empty fontdb, so its golden renders no text; Donner renders the text (browser-consistent).
Next step (needs a maintainer decision): refresh the whole structure/image golden set from current resvg and adopt Mitchell-bicubic in RendererTinySkia::drawImage/drawBitmap (+ the Geode sampler) to match current resvg, or keep bilinear and leave the 13 mismatched-era tests skipped. Per-test threshold inflation is not an option.
B5 (feMorphology degenerate radius) is now fixed — see Recently fixed.
Resolved. All 14 tests in painting/paint-order/ are active. Shape marker/fill/stroke ordering and text whole-run fill/stroke ordering run on both backends. The vendored on-tspan PNG loses kerning across a paint-only span boundary, so Donner uses explicit CPU and Geode goldens that preserve continuous shaping while still gating paint order.
Impact: ~6 (text/textLength 2 + text/lengthAdjust 3 + text/text-decoration interaction). Text stretching/compressing to a target length (spacing and spacingAndGlyphs). The arabic/arabic-with-lengthAdjust cases pass on text-full builds and are enabled with .onlyTextFull().
Zero target length is applied to spacing in the text engine and has focused unit coverage. The resvg zero.svg case remains disabled with a 397-pixel residual concentrated on the overlapping glyph raster; its layout is no longer ignored, but the remaining oracle difference still needs independent browser triage before the exception can be removed.
Per-span adjustment now updates the current text position inherited by following runs and stops at the first explicit inline-axis position, including a reset later inside a span. The on-a-single-tspan.svg layout is active in focused helper and DOM geometry tests; its resvg entry remains classified for a 499-pixel small-text raster residual. Nested-container aggregation, decoration extents, vertical layout, and textPath ordering remain implementation work.
Impact: 8 in text/textPath/: path attribute, side=right, method=stretch, spacing=auto, path+xlink:href combinations, filter on textPath, plus modern vertical writing-mode cases. Obsolete SVG 1.1 writing-mode aliases are classification work, not new feature work.
Impact: ~8 across text/direction (2), text/unicode-bidi (1), text/text/bidi-reordering, text/tspan/bidi-reordering, text/letter-spacing/mixed-scripts, text/textLength Arabic. Needs the BiDi algorithm + RTL shaping (text-full). Group as one workstream.
Resolved by #868. All structure/transform-origin/ cases, including on-text-path, are active.
Gradient/pattern paint-servers and <image>/text apply the transform-origin pivot as Translate(-origin)·M·Translate(origin) (matching the shape path; Donner's operator* is left-first). For paint-servers the pivot is recomputed in the renderers from each entity's ComputedLocalTransformComponent — RendererTinySkia::resolveGradientTransform, RendererGeode::resolveGradientTransform, and the shared pattern transform in RendererDriver — not via the getRawEntityFromParentTransform accessor, which is unrelated. For <image>/text the layout composes the resolved origin with the content-placement transform.
on-text-path still renders the baseline path without the pivot, so a rotated <textPath> samples its glyphs off-screen → #624.
| Category | Tests | Gap |
|---|---|---|
| structure/svg | 0 | structure/svg/funcIRI-with-quotes.svg compares with ordinary budgets after the paint URL parsing fix, and structure/svg/nested-svg-one-with-rect-and-one-with-viewBox.svg compares with ordinary budgets after the viewport sizing fix. |
| structure/style | 1 | CSS @import / external CSS remains unsupported: imports are diagnosed and ignored, with no target requests; UnsupportedCssImportsNeverRequestConfiguredResponses in //donner/svg/renderer/tests:renderer_public_api_tests enforces that. |
| structure/symbol | 1 | transform on <symbol> (SVG2) |
| painting/image-rendering | 1 | Both backends implement the typed values, including two-stage non-integer pixelated scaling. on-feImage uses an independently derived nearest reference; optimizeSpeed remains pending reference qualification at allowed nearest-sampling ties, rather than missing SVG support. |
| masking/clipPath | 5 | Nested clip intersections and shorthand edge cases remain. Vector text children use shared placed glyph outlines. The transformed-text vendored golden uses ink bounds, unlike SVG 2 full-glyph-cell bounds and Firefox, so it remains classified pending broader browser review. |
| masking/mask | 0 | Transformed group and rotated mask regions compare with ordinary budgets; mask-type alpha/luminance and mask color-interpolation sRGB/linearRGB are active. |
| text/font | 2 | font shorthand; canvas-size mismatch (test harness) |
| text/tspan | 3 | tspan interaction with clip-path/filter/mask |
| painting/stroke-dasharray | 4 | 0 n dash patterns with caps; 40 0 closed-rect dash-seam (see note) |
| painting/marker | 3 | multiple closepaths, recursive-5 (rounded-rect corner fixed, #623) |
painting/stroke-dasharray/n-0 (40 0) — root-caused under #623 and intentionally left skipped: an SVG <rect> is a closed contour, so tiny-skia (the faithful Rust-tiny-skia port) seam-joins the first and last 40-unit dash across the start vertex into one continuous dash, making the start corner an interior MITER. resvg's golden butt-caps that corner because usvg flattens the rect to a non-closed path before dashing. Donner's mitered closed-contour seam is the spec-conformant behavior (matches Skia/Chrome/Firefox); the diff is a resvg-pipeline difference, not a Donner/tiny-skia bug. Pinned by RendererTests.DashSeamClosedContourMitersStartCorner.
painting/marker/marker-on-rounded-rect — fixed under #623: Path::vertices() now emits the arrival marker-mid at a rounded rect's zero-length-close start corner (stacking start + mid + end, matching resvg), while still excluding smooth all-curve loops (circle/ellipse). | text/writing-mode | ~7 | vertical-lr / vertical-rl edge cases and mixed-script (upright CJK plus rotated Latin) column geometry. Obsolete tb* SVG 1.1 values should be classified or mapped to their required compatibility aliases, not implemented as distinct modes. |
An feImage referencing an SVG element uses the resolved primitive subregion origin for content placement, including individually omitted x or y. Placement uses the unclipped user-space origin; clipping the primitive or filter region does not move the referenced artwork. Normal referenced geometry retains its natural dimensions. The CPU and Geode paths share this rule. filters/feImage/with-x-y.svg and both with-x-y-and-protruding-subregion comparisons use ordinary budgets. The authored renderer oracle pins image-rendering="crisp-edges" and compares exact pixels for complete/partial coordinates, protrusion, and translated source content. Arbitrary host scale, rotation, skew, and reflection are not established by this bounded coverage; their existing exceptions remain tracked separately.
The exact placement oracle is FeImageFragmentUsesItsOwnSubregionOrigin in //donner/svg/renderer/tests:renderer_public_api_tests; the configured GPU target is //donner/svg/renderer/tests:renderer_public_api_tests_geode. The ordinary-budget corpus gates are //donner/svg/renderer/tests:resvg_test_suite_default_text and //donner/svg/renderer/tests:resvg_test_suite_geode; full-text uses the API with --config=text-full and //donner/svg/renderer/tests:resvg_test_suite_max. See Filter Effects.
Unknown SVG elements retain their DOM descendants but exclude the subtree from rendering, including after incremental source insertion. The sample viewer uses a real g for its selection chrome. structure/svg/rect-inside-a-non-SVG-element.svg and xmlns-validation.svg use ordinary comparisons. The constructors and namespace-aware source projection live in SVGUnknownElement.cc and SVGDocument.cc; renderer_public_api_tests covers parsed and edited wrappers, foreign namespaces, and promotion of a known SVG container. Namespace declaration edits re-evaluate the affected descendants while retaining their typed state for restoration; foreign shapes, text, stylesheets, paint references, filter children, and clip geometry are excluded from their consumers. This does not purge previously loaded or queued font faces from the existing resource cache.
The named rendering gates are //donner/svg/renderer/tests:renderer_public_api_tests and its configured renderer_public_api_tests_geode counterpart. Corpus enforcement uses //donner/svg/renderer/tests:resvg_test_suite_default_text, resvg_test_suite_geode, and resvg_test_suite_max for the configured text lane.
The XML byte parser accepts UTF-8 input and rejects declarations naming another encoding rather than interpreting those bytes as UTF-8. structure/svg/not-UTF-8-encoding.svg is an explicitly unsupported Windows-1251 input: transcode the bytes and update or remove the declaration first. //donner/base/xml:xml_tests and //donner/svg/parser:parser_tests cover rejection, UTF-8 BOM and case-insensitive declaration acceptance, and unrelated encoding-like content.
These have a question-mark reason in the file and need a root-cause pass to decide bug vs. out-of-scope:
| Category | Tests | Why |
|---|---|---|
| filters/enable-background | 21 | Category default Params::RenderOnly(...): deprecated in SVG 2 (→ <filter> chains / backdrop-filter). See unsupported_svg1_features.md. |
| filters/filter in=Background* | 2 | Same deprecation (BackgroundImage/BackgroundAlpha inputs). |
| text/tref | 9 (+1 display) | <tref> removed in SVG 2. |
| text/kerning | 2 | kerning attribute deprecated SVG 1.1. |
| text/glyph-orientation-* | 2 | deprecated SVG 1.1. |
| paint-servers/radialGradient | 1 | test-suite bug (fr> default — SVG2 behavior changed). focal-point-correction now passes and is enabled. |
| structure/style-attribute | 1 | <svg version="1.1"> disables geometry-in-style (SVG 1.1 behavior). |
| Other RenderOnly UB cases | 51 | Implementation-defined output; we verify no-crash only (per project policy, kept RenderOnly not Skip). |
Geode is part of the same resvg test matrix as the CPU variants. It should use the same ImageComparisonParams thresholds, render-only state, skips, and golden overrides as the other renderers. Build capability is recorded through normal Params feature requirements, never through parity-only backend disables or side-table gates.
Policy:
The practical goal is fewer overrides over time. A large override map is a signal to either fix the feature, classify it as a clear unsupported/deprecated case, or write a focused non-resvg regression that exercises the root cause directly.
RESOLVED. Geode now uses official Slug analytic dual-ray coverage at 1 sample/pixel on every adapter (4× MSAA and the Intel-Arc alpha-coverage fallback deleted; Mac/Linux unified; GeodeTinyParity retired). See 0041 (as-built).
The earlier theory in this section — that ~16 Geode gates shared one "slug_fill edge-coverage quantization" root cause — was wrong, and is preserved here only as a caution: the analytic rewrite left those tests byte-identical, proving coverage was never the cause. They were three real, separate bugs plus two legitimate per-backend goldens, all now fixed/closed:
Lesson: a large diff amplified by a filter/matrix is not evidence of a coverage problem — inspect whether a coverage change actually moves it before attributing it. No active resvg case is backend-disabled. Verified analytic-vs-finite-sample edge residuals, including feGaussianBlur/complex-transform, paint-order text, and the rotated pattern case, use exact Geode goldens so the backend remains fully gated.