Strokes carry an object (0 for older projects, nothing written for 0). The
tracker follows every object in one pass and the mattes are united; the
confidence of a frame is the least sure object's. Object picker in the Layer
panel's bar; roto.options/stroke/clearStrokes/status take or report objects.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Hiera configs and size detection from the weights, registry entries pinned to
commits with verified SHA-256, the loader picks the size by checksum. The GPU
encoder submits once per block so Large fits in memory, and uploads no longer
panic on a lost device. Reference tests run per size on the CPU and GPU.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Simulated user paints a foreground stroke through the true object and a background
stroke on the biggest false positive on the first frame under IoU 0.9, up to 6
rounds. SAM 2 needs none on every clip; classical and MobileSAM need more than 6 on
occlusion and re-entry. All trackers now take strokes on any frame.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Memory attention (1.8e-5 vs PyTorch) and the memory encoder (2.6e-6) as wgpu kernels,
convolution and layer-norm+GELU kernels, VideoModel::load_auto picks the GPU when
there is one (EFFECTCRAFT_SAM2_DEVICE overrides) and falls back to the CPU on any GPU
error; disable_gpu switches it off mid-clip. One process-wide device (several at once
crashed the Vulkan loader). A tracked frame: 4.3 s CPU, 0.7 s on the RX 7900 GRE.
Clips tracked on the GPU match Sam2VideoModel (worst logit error 0.0032).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Candle adds a bias on one thread; the Linear layers now add it (and apply GELU
for the MLP in) in one parallel pass. Encode 2.4 s -> 2.1 s, memory attention
2.4 -> 2.3 s; reference tests unchanged. gpu_matmul example: a tiled WGSL
matmul on the RX 7900 GRE reaches 1.9-3.6 TFLOPS against 0.16-0.47 for the CPU.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A tracked frame is the encode (2.4 s) plus memory attention (2.4 s with a full
bank), about 5 s on 16 threads, not one encode as the docs said. Docs corrected;
chunked attention tried and reverted.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
MaskModel::idle_secs (SAM 2 tracks its use); the engine frees a model idle for
ten minutes when no job runs, keeps the choice, and reloads it before a stroke
or a Propagate needs it. Docs: memory and limits.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Chain::track tracks the frames of the span without a tracked result, in a window
that restarts at the nearest prompted frame; unchanged spans are not tracked
again. Test with a recording tracker: first pass 0..20, no second pass, then a
correction on frame 8 tracks 8..20 with that prompt only.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The tracked frames lived only in memory, so a saved project lost them: a render in another
process (effectcraft-cli render-frame) segmented the frames again with the classic engine.
A propagate with a tracker now saves the frames it followed with the instance, in a hidden
parameter beside Freeze's (TrackCache: each frame's matte, Refine Edge band, colour models and
confidence, keyed by the inputs it was tracked from).
- They apply while the strokes and span they came from are unchanged, whichever model Settings
choose, so a project renders the same when it reopens, without the model loaded or chosen.
- A stroke or span change makes them stale until the next Propagate. A Propagate replaces them;
with the classic engine it leaves none.
- roto.status reports tracker.saved (the model they came from) and a notice when Settings
choose another model.
- The save at job completion is not an undo step (the frames are a cache of the strokes) and is
skipped if the strokes changed while tracking. Decoding checks the saved size, so a corrupt
project is ignored rather than overflowing.
Object Matte saves nothing. Projects saved before this change open unchanged (the parameter is
filled in on open).
Tests: a saved project reopens with the model unloaded and renders the saved frames through
Chain::ensure; choosing the classic engine keeps them until a classic Propagate replaces them; a
stroke change makes them stale. The SAM 2 tests and the failing-tracker test take the Roto test
lock.
Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
With SAM 2 (or MobileSAM) chosen, a propagate that started before the model had loaded ran
the whole job on the classic engine and reported nothing. The chosen model is now in use
before a job that segments with it starts (Roto Brush 2.0 / 3.0 and Object Matte):
- roto.propagate and the other Roto jobs wait for a load or download under way when called with
wait: true (bounded to 10 minutes). Without wait they refuse while the model is still loading,
and start the load in the background. A model that is not installed, or failed to load, is
refused either way.
- roto.status reports model {selected, installed, active, loading, error} and a notice while the
chosen model is not the one in use. source is "model" for a frame the model segmented on its own,
instead of "classical".
- Auto-propagate keeps edited instances pending while the model loads, and shows errors as toasts
instead of dropping them.
- Docs: what propagate does now; tracked frames live in the session that ran propagate and are not
saved in the project file, so a separate render process does not show them; roto.model.select
lists sam2.1-hiera-tiny.
Tests: a selected model that is not installed refuses to propagate (no weights needed); SAM 2
selected without wait is loaded before propagate (EFFECTCRAFT_SAM2). The SAM 2 tests and the
failing-tracker test take the Roto test lock, since they share the process-wide model slot.
Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
--mobilesam FILE; results recorded in docs/roto-brush-sam2.md (re-entry clip:
0.632 classical, 0.499 MobileSAM, 0.975 SAM 2).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Apache-2.0 entry pinned to a Hugging Face commit with its SHA-256 and size,
listed in Settings > Roto Brush and installable with the existing verified
install flow. No weights are bundled.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Since #2204, ci.yml runs `cargo xtask ci` through craft-common-actions' shared gate on every
pull request and push to main; gaps.md still listed "No CI gate on pull requests" as gap 4 and
in the Stability row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main routes viewer scrolling through viewer_scroll, so a raw-event scrub fought the wheel zoom. With general.viewerWheelScrubbing on, an unmodified wheel steps frames and the rest of that notch does not zoom or pan. The preference stays off, so the wheel still zooms.
header.rs, render_queue.rs: main's drag-toggle checkboxes report `.changed()`; the
Snapping, Notify and per-item Render check boxes keep this PR's AccessKit names and use
`.changed()`, the per-item one inside main's `add_enabled_ui`. The Render button keeps its
name with main's translated label.
effects_presets.rs: kept this PR's row_focus/nav_click and main's rows_before_presets;
main's reveal of Animation Presets with this PR's folder_row (nav, level), and the nav's
offset follows the scroll it moves; the search field keeps its name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a project that was never saved, renderQueue.add without output queued
an item whose default output was the home folder, outside every
automation root (rendering it was refused, but the queue held a path the
agent may not write). While file access is confined, such an add is now
refused with a hint to give output inside a write root; a saved
project's default output (beside it) is checked when the queue renders,
as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the two conflicts the panel catalogs and the Composition Settings
rewrite left:
- i18n/ui.rs: keep both sides' rows in the Simplified and Traditional Chinese
catalogs, which grew at the same anchor (the PR's 301 panel/dialog rows, then
main's 43 tool and Preview-panel rows). The tables are grouped by feature, not
sorted by key, so the two blocks sit one after the other. Both sides also added
"Frame Rate" once: keep one row per catalog, main's 影格速率 in Traditional
(matching the table's existing 影格速率:) and 帧速率 in Simplified.
- comp_settings.rs: take main's rewritten tab_body (it returns () and matches on
the tab, and show() measures the tabs) and re-apply the wrapping the old layout
had: the three tab labels go through tr(), as Basic, Advanced and 3D Renderer
already have rows. The old horizontal layout and its out accumulator are gone.
cargo test -p effectcraft-ui-egui --lib: 336 passed, 0 failed, 3 ignored.