Repository navigation
feat(scene): keyframes that play from when an item's scene goes on - #43
Conversation
Quest-Claim: e94fafb24b1b4ee7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es draw Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review checkpoint (session round 4): the maintainer picked continue (iterate, then merge). Round 5 reviews 68c00c7 with no design change. (Written by Claude Opus 5.5) |
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 19 seconds. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 68c00c7
- [P2] Keep both sides of an equal-time jump distinct in the draw cache (
rs/moq-scene/src/collection.rs:612–615). With two keyframes at 1s,offset()uses the first before 1s and the last afterward, butsettled()returns the same cache time on both sides. A settled preview can populate the opaque pose, then a newly cut program reuses it during its first second when the first pose should apply. Preserve the before-first versus at/after-first distinction and add a preview-to-program regression. - [P2] Hit-test at the displayed preview frame's time (
rs/moq-studio/src/ui.rs:755–756, also 793). Input and arranging use the current time while the texture shows the last program tick. At 1 fps a fast-moving item can visibly remain at its old position while a click there tests against its later position and misses. Use the elapsed time of the last rendered preview for hit testing and handles.
Scene-owned offsets and separate preview/program clocks fit the feature. Verification: GitHub-only static review; I did not run tests or GPU playback. Hosted Check was still running when inspected.
There was a problem hiding this comment.
Important
An item with equal-time keyframes (a deliberate jump) can show its after-jump pose on program for the first part of the scene, because the prepared-draw cache conflates the before-first and after-last holds.
Reviewed changes
- Keyframes in the scene:
Itemgains orderedkeyframes,Keyframe::towardmixes offsets/crop/filters,Item::posedholds the first and last, and validation rejects top-layer keyframes, bad crops, non-finite offsets, and source-reading filters. - Clocks in the compositor: preview and program get separate starts; a cut restarts the program clock, a transition starts the incoming keyframes at the click, and a per-render prepared cache reuses items at settled times.
- Studio editing: a Keyframes tab adds, edits, removes, and replays keyframes; arranging and browser page input pose items at the preview time; clips carry keyframes and paste validation covers them.
- Docs and quests: the README documents keyframes with an example, the animation quest is removed, and the stinger and layout quests describe the landed behavior.
big-pickle (free) | 𝕏
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
On the OpenAI review of 68c00c7:
Codex reviewed 6cd8a28 locally with no findings. (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 6cd8a28
Both earlier findings are fixed: the cache distinguishes before/at/after poses for equal-time jumps, and interaction and arranging use the displayed preview's recorded render time. New tests cover the jump and stored timestamp. No new actionable findings in the update; the targeted changes fit the existing rendering model.
Verification: GitHub-only static review. I did not run tests or GPU playback; the timestamp assertion is not a full interactive low-frame-rate test.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Replaced the clamped
Durationprepared-draw key withSettled { Before, At(Duration), After }, so both sides of an equal-time jump get distinct cache entries. - Recorded the preview elapsed time at each render and made
Compositor::preview_since()return it, so arranging and browser input hit-test the frame the texture actually shows. - Added the
keyframes_at_one_moment_jumpunit test, thea_jump_draws_each_side_on_its_own_outputGPU test, and apreview_since()assertion inkeyframes_play_from_when_the_scene_goes_on.
Both prior findings are addressed: the cache-key collision is fixed by Settled, and the render/hit-test skew is fixed by preview_shown. The new classification cannot conflate two poses (only over-split shares), and no other callers of the old preview_since(now) signature remain.
big-pickle (free) | 𝕏

Finishes quest/m0/animation.md and deletes it.
Problem
An item could only sit where it was placed. A switcher needs items that fly in, fade or blur in, and cameras that push in. The animation has to land on the same frames in the program, a recording, and a replay.
Approach
moq_scene::Itemgetskeyframes: Vec<Keyframe>. Each keyframe has:at: the time after the item's scene goes on.ease: how the item eases into it (linear,in,out, orin_out, the default; cubic, the same curve transitions use).x,y,width, andheightin canvas pixels added torect, acropof what the item's crop leaves, andfiltersapplied after the item's own.Item::posed(since)returns the item as drawn: offsets mixed between the two keyframes aroundsince, the first keyframe held before it starts, and the last held after it ends. Filters mix their settings (Filter::toward) when the two lists pair up by kind, in order. Otherwise they switch halfway, as a move transition does. An item without keyframes is borrowed, not cloned.renderis given. Each output has its own start time:set_preview, even with the same scene).Item::settled(since)(the time clamped to the keyframes' span). The two outputs share a draw once both have settled.Draft::set_item, so they undo like any edit.Transition::progressnow callsEase, andCrop::towardreplaces the crop mix in the move transition.Frame time
1920x1080 on an Apple M4 (Metal, release). Each figure is
renderplus a GPU wait over 1000 frames, measured with a temporary test that is not committed. The scene has a full-canvas fill, a 640x360 picture-in-picture with a drop shadow, and a 400x120 bar. In the keyframed version, the picture-in-picture moves and crops and the bar moves.render(loaded)Impact
keyframeslist. This is additive, so existing files load unchanged.Keyframe,Ease,Item::keyframes,Item::posed, andCompositor::preview_since(the moment of the keyframes the last rendered preview shows).Itemliterals need the new field (or..Item::new(..)). No wire impact.Alternatives
scaleabout the center, instead ofwidthandheightoffsets: pixel offsets compose with the preview's edge and corner drags, so grabbing the shown rect and moving the rest stays exact. They also cover non-uniform growth.opacityoffset: dropped. Anopacityfilter in the keyframe does the same, and Add keyframe copies the last keyframe's filters so they pair.Choices made without asking
set_preview, so the first frame drawn is t = 0 on the program clock. A transition starts at the instant it is given, as before.Follow-ups
None. Keyframes in the layout track belong to quest/m1/compose/layout.md, whose Plan now says the layout carries when each scene went on program.
Validation
just cion 6cd8a28: passed (lints, quest tree, 207 tests).cargo nextest run -p moq-scene --lib --run-ignored all compositor::tests::on Metal (M4), on the 6cd8a28 tree: 28 passed. The new ones arekeyframes_play_from_when_the_scene_goes_on,a_transition_starts_the_incoming_keyframes, anda_jump_draws_each_side_on_its_own_output.Review
Codex
gpt-6.1-sol, medium effort, viaquest review --base origin/main --branch quest/m0/animation. Session rounds 2 to 7 (round 1 was #33's, which used the same branch name). Reviewed head: 6cd8a28, no findings.Settled { Before, At, After }; the thread is resolved.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)