A table of After Effects' index and unit for the effect parameters its keyframe text gives differently from EffectCraft's rules, read from After Effects 2019's text for 230 effects. All 1,530 parameters the two share now match; 949 did before.
Found while testing crates/keyframe/src/value.rs:79-95 (ShapePath::feather_after_remove, called from crates/engine/src/commands/mask.rs:330)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A 2-D point property took any array: `[1, 2, 3]` and `[1, 2, 3, 4]` were cut to `[1, 2]` and
`[1]` was broadcast to `[1, 1]`, each reported as success, so a script holding a wrong value
silently did something else instead of failing. After Effects raises "Array is not of the
correct dimension" and leaves the property unchanged.
The length must now match the property's dimension, with the one exception After Effects also
makes: two values for a 3-D property, where z stays 0, so `position.setValue([x, y])` keeps
working.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session::bump took the disk cache's footage salt at every edit, which reads the size and time of
every footage file, each frame of an image sequence included. With three 5000-frame sequences a
caret move or a typed character in a text field took ~0.6 s on the UI thread (9 ms with the disk
cache off). The salt is now taken again only when the footage items changed, or when a project
is opened, footage is reloaded or the disk cache attached.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: styled() kept the buffers it rendered instead of the ones LayerCache::insert returned, so
without a blending-space conversion concurrent misses still returned separate body and pass
buffers (and the GPU uploaded each).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Alt/Option-drag a Project item onto a Timeline layer, or press Ctrl+Alt+/ (Cmd+Opt+/) with layers and an item selected, to swap the layer's source. The layer keeps its name, timing, transforms, keyframes, expressions, effects, masks, switches, parenting and track matte; a source with audio adds Audio properties when missing. Precompositions can't be nested inside themselves.
AVLayer.replaceSource now runs the same engine command and closes the replaceSource part of #813.
Preview workers that start together miss the layer cache together, and each kept its own copy
of every layer; on the GPU each copy was uploaded and packed into its own Classic 3D atlas, which
ran the device out of memory on a cold start. LayerCache::insert now keeps the first buffer of a
key and returns it, and the renderer goes on with the returned buffer, so the workers share one.
Renders don't wait for each other: on the shared frame pool a blocking wait could end up beneath
the work it waits for (tried, it stalled playback for the wait's timeout).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Looking up to the whole RAM preview for uncached frames let the workers spread over the preview
while the playhead waited. On the GPU each worker then built a Classic 3D atlas for its own set
of layers, and on a cold start of the #1812 project the device ran out of memory far more often
(first pass 1-56 % of real time instead of 63-81 %). The window now keeps its old width but
starts at the first frame ahead that isn't cached, so a loop or a restart still renders on at
once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: two footage items of one file with different nominal sizes could share a sized entry,
though one is resampled and the other kept as decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edit > Copy with keys or Transform properties selected puts the After Effects keyframe text on the system clipboard, for other applications such as AE2Blend, in place of the 'EffectCraft: N keyframes' note. Ctrl+V in EffectCraft still pastes from its own clipboard.
Copying keys or unanimated properties of one layer records them as After Effects keyframe text: its Transform properties, one row per key with the frame in comp time (start offset and stretch applied) or a frameless row for a static value. EffectCraft's own keyframe clipboard is unchanged.
Review: mask motion blur draws an animated mask at the shutter's other times, but the layer key
hashed its value at this time only. Two frames with the same mask value (a hold before a move)
shared a cached layer, with the wrong blur. This applied to solids, text and shapes already, and
to stills since this PR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: still_at_size returned a smart object's pixels unresampled when they differed from the
footage's nominal size. It now follows footage_buf: pixels larger than nominal are resampled to
the requested size, smaller ones are left for the renderer to scale up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Index-units test now opens the Units popup in the Timeline and picks
Index, asserts the stored Units value, then reads the painted Start, End
and Offset, so it also fails when the Units control doesn't update the
session.
Review: frame_at_size at a still's native size made a second cache key, so the pool decoded
and kept a second full-size copy of a frame thumbnail() or frame() already had.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The playback prefetch only looked ahead*3 frames (96 with 16 workers) for uncached frames.
After a loop or a restart from the start those were all cached, so the workers stood idle until
the playhead came within that window of the end of the cached run, then the preview stalled.
A playing preview now looks as far ahead as the RAM preview holds (and the range spans) for
the next uncached frames, still queuing at most ahead at a time; eviction already spares the
frames the playhead reaches first (#595).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Footage layers had no layer cache key, so a still made a new layer buffer every frame on every
preview worker, and the GPU compositor uploaded it again each time (its upload cache is keyed
by buffer). Stills (without a proxy) are now keyed like text and shape layers, without the
source time; movies and sequences are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The media pool honoured the requested size only for movies; stills came back
at full size and the renderer resampled them on every frame. A 21120 px wide
plate at Quarter was re-filtered each frame, and four of them (1.85 GB as f32)
did not fit the 1 GiB pool, so they were also decoded again and again.
Stills are now resampled once with effectcraft_raster::resample (the same
call the renderer made, so pixels are unchanged) and the pool keeps the
reduced copy under the sized key.
#1812 project at Quarter, CLI bench: 410 ms -> 33 ms a frame (26 ms with the
layer cache); desktop playback 2% -> 60-99% of real time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>