Skip to content

Add opt-in compact storage for Animate timelines - #57

Open
Tutez64 wants to merge 1 commit into
openfl:masterfrom
Tutez64:optimize/compact-animate-timelines
Open

Add opt-in compact storage for Animate timelines#57
Tutez64 wants to merge 1 commit into
openfl:masterfrom
Tutez64:optimize/compact-animate-timelines

Conversation

@Tutez64

@Tutez64 Tutez64 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds an opt-in swf_compact_animate_timelines define that stores
Animate timeline events in compact numeric buffers instead of eagerly creating
an object graph containing one AnimateFrame, AnimateFrameObject, and
optional matrix, color-transform, filter, and array objects per serialized
event.

The compact representation:

  • stores frame boundaries as offsets into a flat Vector<Float>;
  • keeps non-numeric values in a separate sparse reference array;
  • decodes event properties directly during playback;
  • updates native transforms without allocating temporary Matrix objects;
  • preserves the existing Animate asset format and standard playback behavior.

The legacy representation remains the default.

This is opt-in because code that accesses AnimateSpriteSymbol.frames directly
will not receive the eagerly expanded legacy metadata.

Motivation

The expanded timeline metadata remains reachable for the lifetime of an
Animate library. Large libraries can therefore retain hundreds of thousands of
small managed objects, increasing both heap usage and the amount of graph
traversal performed by full garbage collections.

Deterministic benchmark

SWFAnimateTimelineMemoryRepro.zip
It parses and retains 400 sprites containing 300 frames and
eight events per frame, for a total of 960,000 timeline events.

Three alternating runs on Linux x86_64 with Haxe 4.3.6 produced:

Metric Legacy Compact
Parse time 492–543 ms 497–500 ms
Retained hxcpp heap 218.6 MiB 93.5 MiB
First full GC 34–38 ms 4 ms
Median forced compacting GC 66-79 ms 1 ms

The retained heap decreased by 57%. The benchmark intentionally amplifies
timeline metadata costs; application results depend on the amount and
complexity of loaded animation data.

Real-world validation

These values should be considered indicative rather than a controlled benchmark:

Metric Legacy Compact
Marking time per GC 39.57 ms 27.13 ms
Full-scan marking average 109.66 ms 24.82 ms
Full-scan marking maximum 233.78 ms 40.20 ms
Maximum GC pause 322.16 ms 140.15 ms
GC time fraction 0.792% 0.636%

At a comparable heap census, the compact run contained approximately 4,171
AnimateTimelineData objects instead of 40,949 AnimateFrame and 63,076
AnimateFrameObject objects. Retained Matrix instances decreased from
138,766 to 78,802.

Tests

  • 57/57 Neko tests pass with the legacy representation.
  • 57/57 Neko tests pass with swf_compact_animate_timelines.
  • Native C++ builds and extended runtime sessions completed successfully.
  • Timeline playback tests cover creation, updates, destruction, transforms,
    color transforms, filters, names, visibility, and frame labels.

Integration notes for pending PRs

This branch is intentionally based directly on upstream/master and does not include other pending changes. Depending on merge order, the following adaptations must be preserved when resolving conflicts:

  • Timeline instance cleanup: compact timeline instances must set __removedChildCleanupDelay to Math.POSITIVE_INFINITY when created.
  • Timeline masks: masks from compact timelines must also be marked through __markTimelineMask. This can be handled in the shared instance-arrangement path.
  • Clip-depth deduplication: compact instance reuse must include clipDepth in its compatibility check.
  • Cached instance fields: preserve the reset-dependent filter clearing behavior when merging the shared display-object update code.
  • Hardware bitmap cache (Add opt-in hardware cache for Animate bitmaps #56): when compact timelines are enabled, the readability scan must inspect compactTimeline.objects, since the legacy frames array is intentionally left empty.

These integrations are already implemented and tested together on the downstream master branch; they are omitted here only to keep this PR independent and reviewable.

Note: until 159db83 from #55 is merged, the Haxe 3 CI jobs may still fail earlier in AnimateTimeline.hx, independently of this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant