Add shared SVG animation runtime and host playback backends#481
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 982e46bb46
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a736db88a2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e5c315dd9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 094d28ebf5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13f80475d0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| else if (!string.IsNullOrWhiteSpace(binding.BaseValueString)) | ||
| { | ||
| resolved.Add(binding.BaseValueString!); |
There was a problem hiding this comment.
Seed
to-only animations with implicit base value
When values/from are absent, this path only seeds resolved from binding.BaseValueString if it is non-whitespace; for attributes like transform, the underlying base can be represented as an empty string (identity), so a valid to-only animation (for example <animateTransform type="translate" to="10 0" dur="2s"/>) ends up with a single value. TryResolveTransformValue then treats it as discrete (values.Count == 1) and applies the final transform immediately instead of interpolating from the base state.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f2bc1cf9f3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| instances.AddRange(eventTimes.Select(eventTime => new ResolvedTimingInstance( | ||
| eventTime + spec.Offset, |
There was a problem hiding this comment.
Guard event-time offset additions against TimeSpan overflow
When resolving event-based begin/end specs, eventTime + spec.Offset can throw OverflowException for large timestamps (for example, if callers seek/advance the clock near TimeSpan.MaxValue and use a positive offset like click+1s). This happens during frame-state evaluation and aborts animation processing instead of treating the timing instance as out-of-range. Wrapping this addition in overflow-safe logic (clamp or skip invalid instance) avoids hard failures on extreme but reachable runtime times.
Useful? React with 👍 / 👎.
| { | ||
| var totalDuration = ComputeTotalDuration(animation, simpleDuration, explicitEnd, begin); | ||
| return totalDuration.HasValue | ||
| ? begin + totalDuration.Value |
There was a problem hiding this comment.
Saturate active-end calculation instead of overflowing
ComputeActiveEnd returns begin + totalDuration, but totalDuration is already allowed to saturate to TimeSpan.MaxValue in Multiply; with any positive begin (e.g., begin="1s" plus very large repeatCount), this addition overflows and throws during interval resolution. That turns a valid animation timing configuration into a runtime crash; the active-end computation should clamp/saturate at TimeSpan.MaxValue rather than relying on unchecked TimeSpan addition.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af1a8baab0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!node.IsRenderable) | ||
| { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Continue traversing children of non-renderable static nodes
RenderStaticNodeToCanvas returns immediately when !node.IsRenderable, which skips the entire subtree during animation-layer static rendering. In the main retained renderer (SvgSceneRenderer.RenderNodeToCanvas), non-renderable containers are still traversed so descendants can render (for example a visibility="hidden" group with a descendant that sets visibility="visible"). With layer caching enabled, those descendants disappear from the static layer, so animated documents can render differently from the normal retained path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34deb36cc1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (SvgAnimationParser.TryParseInvariantDouble(trimmed.AsSpan(), out repeatCount)) | ||
| { | ||
| repeatCount = Math.Max(0d, repeatCount); | ||
| return RepeatCountMode.Finite; |
There was a problem hiding this comment.
Reject negative repeatCount values
ParseRepeatCount currently clamps any negative numeric repeatCount to 0, which makes malformed inputs like repeatCount="-1" resolve to a zero active duration and immediately force end-state behavior at begin. This is a runtime correctness issue for invalid-but-common authoring mistakes: instead of falling back to normal timing semantics, animations can jump to final state or appear to not run at all.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cf7d85ca0e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| var additiveBaseValue = resolved.Count > 0 ? resolved[resolved.Count - 1] : binding.BaseValueString; | ||
| if (additiveBaseValue is { } && TryAddValue(binding, additiveBaseValue, byValue, out var sumValue)) | ||
| { |
There was a problem hiding this comment.
Seed implicit base for by-only animation resolution
When values/from/to are absent, this by path uses binding.BaseValueString directly as the additive seed; for attributes whose underlying base is implicit (commonly transform identity represented as empty/null), TryAddValue(...) fails and resolved stays empty, so valid by-only animations are dropped entirely. A case like <animateTransform type="translate" by="10 0" dur="2s"/> on an element without an explicit transform never animates even though SVG timing expects interpolation from the underlying value.
Useful? React with 👍 / 👎.
PR Summary: SVG Animation Runtime, Retained Scene Graph, and Rendering Cutover
Overview
This branch started as shared SVG animation and interaction work, then grew into a larger rendering-architecture change.
It adds SVG 1.1 animation DOM support in
Svg.Custom, shared pointer interaction and geometry-aware hit testing, a shared animation runtime inSKSvg, host playback backends for Avalonia and Uno, and an Avalonia retainedNativeCompositionpath for supported scenes.On top of that original scope, the branch introduces a retained scene-graph runtime, migrates rendering/editor/animation flows onto that retained runtime, splits the retained scene graph into its own
Svg.SceneGraphproject, and then splits the shared animation runtime into a newSvg.Animationproject. The branch also removes several unshipped compatibility shims and moves the rendering pipeline toward the retained scene graph as the single runtime representation for the next major release.Base branch:
masterMerge-base:
076e0a22e3fe0220db951710d7799dc92b42576dBranch diff vs
master:163files changed25,165insertions624deletionsCommit Progression After The Last PR Summary Update
The earlier PR summary stopped at the first animation/native-composition/doc pass. Since then the branch added:
Svg.SceneGraphsplit into a separate projectSvg.Animationsplit into a separate projectRelevant later commits include:
f106e50aImplement subtree animation invalidationa736db88Prefer default animation backends20058d0aFix animation timing and additive state9e5c315dGate native composition to renderable roots7c3b5edfAdd retained scene graph foundation9e8bccf1Migrate editor workflows to retained nodes65c23848Route interaction through retained scene796370f3Use retained scenes for animation fallback6e86c57aRemove retained compiler drawable bridge41f4b71eFix retained mask hit testing537eb2efFix event timing instance handling7865aab6Prune hidden retained hit-test subtrees1cf14385Remove drawable editor fallbacksaaa16665Migrate retained animation runtime APIs0bba357fRemove animation reflection access60767e4dDrop obsolete retained API shims5fc313deSplit retained scene graph project754035caCut over rendering to retained sceneseb990cc4Fix retained filter parity and hit testinge06ca06fSplit animation runtime intoSvg.AnimationMain Functional Changes
1. SVG animation DOM and shared runtime
The branch adds local SVG 1.1 animation element support in
Svg.Customand a shared animation runtime inSKSvg.This includes:
set,animate,animateColor,animateTransform, andanimateMotionThe runtime was later cleaned up by:
Svg.AnimationprojectKey files:
src/Svg.Custom/Animation/*src/Svg.Animation/Animation/*src/Svg.Animation/SvgAnimationInvalidation.cssrc/Svg.Skia/SKSvg.AnimationLayers.cssrc/Svg.Controls.Skia.Avalonia/Svg.cssrc/Svg.Controls.Skia.Uno/Svg.cs2. Shared interaction and geometry-aware hit testing
The branch adds typed
pointer-eventssupport, routed pointer dispatch, geometry-aware hit testing, and retained-scene-first hit testing.This includes:
Key files:
src/Svg.Custom/Interaction/SvgPointerEvents.cssrc/Svg.Skia/Interaction/SvgInteractionDispatcher.cssrc/Svg.SceneGraph/SvgSceneHitTestService.cstests/Svg.Skia.UnitTests/HitTestTests.cs3. Avalonia/Uno host playback and native composition
The host controls now expose:
AnimationBackendAnimationFrameIntervalAnimationPlaybackRateActualAnimationBackendAvalonia additionally gained a retained native-composition path for supported scenes, plus TestApp support for selecting and exercising it.
This work also includes follow-up fixes for:
Key files:
src/Svg.Controls.Skia.Avalonia/Svg.cssrc/Svg.Controls.Skia.Avalonia/Composition/SvgCompositionVisualScene.cssrc/Svg.Controls.Skia.Uno/Svg.cssamples/TestApp/Views/MainView.axamlsamples/TestApp/Views/MainView.axaml.cs4. Retained scene graph foundation
The branch introduces a retained scene graph as a real runtime representation, not just a transient export format.
The retained layer now covers:
The compiler progressively moved away from drawable-bridge fallbacks and toward direct retained compilation for core shapes, structural wrappers, text, masks, filters,
use,switch, and image-backed scenarios.Key files:
src/Svg.SceneGraph/SvgSceneCompiler.cssrc/Svg.SceneGraph/SvgSceneDocument.cssrc/Svg.SceneGraph/SvgSceneNode.cssrc/Svg.SceneGraph/SvgSceneResource.cssrc/Svg.SceneGraph/SvgSceneRenderer.cssrc/Svg.SceneGraph/SvgSceneRuntime.cs5. Major-release rendering cutover away from drawable-first public rendering APIs
Because this work targets a future major release, the branch starts removing drawable-first public rendering flows instead of preserving backward-compatible shims.
This cutover includes:
SKSvgrender/model creation through retained scene compilationSvgService.ToDrawable(...)/SvgService.ToModel(...)Svg.Controls.Avalonia, source generation, and CLI/sample consumers to retained-scene model generationKey files:
src/Svg.Skia/SKSvg.Model.cssrc/Svg.Model/Services/SvgService.cssrc/Svg.Controls.Avalonia/SvgSource.cssrc/Svg.SourceGenerator.Skia/SvgSourceGenerator.cssamples/SvgToPng/ViewModels/MainWindowViewModel.cssamples/svgc/Program.cs6. Animation performance and invalidation follow-up
The branch adds layered animation redraw and then pushes further toward retained-scene incremental behavior.
This includes:
Key files:
src/Svg.Skia/SKSvg.AnimationLayers.cstests/Svg.Skia.Benchmarks/SvgAnimationFrameBenchmarks.cs7. Project structure cleanup
The branch splits the new architecture into dedicated projects:
src/Svg.SceneGraph/Svg.SceneGraph.csprojsrc/Svg.Animation/Svg.Animation.csprojThis keeps:
Svg.SceneGraphSvg.AnimationSKSvgfaçade code inSvg.SkiaIt also removes unshipped obsolete compatibility APIs and replaces reflection-based internal access with explicit runtime bridges where possible.
Documentation And Planning Artifacts
The branch adds and updates implementation docs and plans for the new architecture, including:
plan/svg-interaction-animation-phased-implementation.mdplan/svg-retained-scene-graph-rewrite-spec.mdplan/remaining-scene-graph-animation-work-plan.mdplan/scene-graph-major-release-cutover-plan.mdplan/animation-project-split-plan.mdUser-facing docs were also refreshed earlier in the branch, including:
README.mdCHANGELOG.mdsite/articles/guides/interaction-and-animation.mdSvg.Custom,Svg.Skia,Svg.Controls.Skia.Avalonia, andSvg.Controls.Skia.UnoValidation
Branch work included repeated validation with:
dotnet format Svg.Skia.slnx --no-restoredotnet build Svg.Skia.slnx -c Releasedotnet test Svg.Skia.slnx -c ReleaseRecent validation specifically covered:
Svg.SceneGraphsplitSvg.Animationsplite-feDiffuseLightingand malformede-feConvolveMatrixExpanded tests include work in:
tests/Svg.Model.UnitTests/*tests/Svg.Skia.UnitTests/HitTestTests.cstests/Svg.Skia.UnitTests/SvgAnimationControllerTests.cstests/Svg.Skia.UnitTests/SvgRetainedSceneGraphTests.cstests/Svg.Skia.UnitTests/SKSvgNativeCompositionTests.cstests/Svg.Controls.Avalonia.UnitTests/SvgSourceTests.cstests/Svg.Controls.Skia.Avalonia.UnitTests/SvgSourceTests.csArchitectural Outcome
The branch is no longer just “animation support.”
It now delivers:
Svg.SceneGraphandSvg.AnimationprojectsThe remaining long-term direction is clear:
SvgDocumentremains the DOM/source modelSvg.SceneGraphis the retained render/runtime representationSvg.Animationowns shared animation timing/evaluation/runtime behaviorSvg.Skiabecomes the Skia/host integration layer on top of those shared runtimes