Commit Graph
3793 Commits
Author SHA1 Message Date
Matej Kozlovský 6e69b91a80 Export: H.264 frames convert to YUV 4:2:0 on the GPU
Accelerator::comp_frame_yuv420 (default None) renders a top-level comp frame and
converts it over the background to the H.264 export's 8-bit BT.709 limited-range
4:2:0, every byte within one level of the CPU conversion (the GPU kernel follows
it operation for operation, including NaN and half-away-from-zero ties, but a
driver may contract its arithmetic). A 4K frame then reads back 12 MB of planes
instead of 133 MB of RGBA f32.

The H.264 export asks the accelerator for planes when a frame needs nothing after
the render (progressive, project depth, no crop/resize, rendered at the output
size) and otherwise renders and converts on the CPU as before. Deferred
readbacks (a browser worker) keep the pass-based path. Sizes the kernel can't
take (width not a multiple of 8, odd height) are declined before any GPU work.

Like Cx::frames, h264_frames caps GPU frames in flight only while the
accelerator is up (map_frames_while with the same liveness check): once the GPU
stops for good, the remaining frames render on the CPU side by side instead of
one at a time.

rgba_to_yuv420 moves to effectcraft_raster::yuv so the export and the GPU tests
share one reference. The GPU tests skip without an adapter (tests::gpu()), as
the crate's others do.
2026-10-28 13:19:24 +01:00
Matej Kozlovský 4407e82c62 Cryptomatte pick: keep a hover probe, built off the UI thread (#473)
effect.cryptomatteAt rendered the effect's input on every call to learn its pixel grid (up
to ~1 s per hover on a 3D precomp). The aux channels and that grid are now kept in a
CryptoProbe on the Session, keyed by the project revision, layer, effect, frame, scale and
render prefs (nested switches, draft shadows). On footage with nothing animated before the
Cryptomatte effect the frame is the source frame, so a still or a held frame keeps one
probe for every comp time. The effect's own Selection doesn't count: the probe never reads
it, so an animated Selection keeps the probe too.

Session::crypto_probe_ready says whether a query answers from the kept probe and changes
nothing. Session::crypto_probe_job builds it on another thread for the viewer; asking for a
job is starting it. Each started job has a generation: it stores its result only while it
is the newest, a newer job or an edit cancels it, and a panic in the build becomes the
probe's error. A cancelled build checks its generation between its stages (reading the IDs,
rendering the effect's input), so it stops early and lets go of what it read. `probeOnly`
makes cryptomatteAt / pickCryptomatte answer {pending} instead of building on the caller.

A pick re-keys the probe (it only edits the Selection), and undo, redo and history jumps
over picks keep it too (each pick is labelled "Pick Cryptomatte"). Any other edit, footage
changes, Open / New and Session::drop_crypto_probe (the eyedropper disarmed) let it go. The
slot's type is crate-private (the Session field stays public, doc(hidden), so other crates
can still build a Session); crypto_probe_stats and crypto_probe_jobs (jobs run, cancelled
ones included) are doc(hidden), for tests.
2026-10-28 12:29:41 +01:00
ashokram-si 56ffc2e642 Test: the ellipse's Size chain keeps its ratio when linked
Review follow-up: the shape Size test linked only the rectangle, so a
change that handled rect and not ellipse would still pass. Link the
ellipse too and check that its height follows its width.
2026-10-28 16:08:46 +05:30
ashokram-si 17e88507f9 Merge remote-tracking branch 'origin/main' into feat/shape-size-chain-2489
# Conflicts:
#	crates/ui-egui/src/panels/timeline.rs
2026-10-28 16:08:46 +05:30
ashokram-si b97cc92a61 feat(ui): Rectangle and Ellipse Size get the Constrain Proportions chain (#2489)
Resizing a rectangle or ellipse while keeping its proportions (a circle
that stays a circle, a 16:9 card that stays 16:9) needed W and H typed
or keyed separately. The Timeline's Size row of a shape's Rectangle
Path and Ellipse Path now has the chain link that Scale and Mask
Feather have. It starts off; turned on, typing or scrubbing W or H
keeps the ratio (Alt still edits one value).

Scale and Mask Feather keep their linked-by-default state in `unlinked`;
Size, which starts unlinked, records a turned-on link in a new `linked`
set, so neither default changes the other's saved state.
2026-10-28 14:52:33 +05:30
NetterSilverandClaude Haiku 5.5 3cfd7e461f Fix Align Video to Data panic on multi-byte char in timestamp
Found while testing crates/engine/src/cmd/align_data.rs:75 (parse_stamp)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 09:09:43 +01:00
NetterSilverandClaude Haiku 5.5 10e173c0e4 Skip whole CSS at-rule blocks in the SVG parser by brace depth
Found while testing crates/svg/src/lib.rs:510-520 (parse_css)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 09:00:16 +01:00
Dylan M. Taylor 6194a157d7 Refactor automation tests for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 6e72ab3b57 Refactor ui-egui for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor f20d22bf36 Refactor engine for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor d01871516a Refactor effects for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 5d71dce7fc Refactor render for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 7926b8bd31 Refactor gpu for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 1d2c0d4248 Refactor script for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor c8994a2cfe Refactor hevcenc for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 3bc15c7c17 Refactor vp9enc for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 8e67bfce47 Refactor av1enc for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 7c27d88030 Refactor opusenc for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 072f2619bd Refactor export for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor d171d6cf88 Refactor project for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor f26f30871c Refactor track for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 51b910657d Refactor pdf for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 388ebc10ac Refactor psd for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 87e42c150c Refactor segment for strict Clippy checks 2026-10-28 06:09:21 +00:00
Dylan M. Taylor 7c74960c6f Refactor text for strict Clippy checks 2026-10-28 06:09:20 +00:00
Dylan M. Taylor 8d34f1d449 Refactor media for strict Clippy checks 2026-10-28 06:09:20 +00:00
Dylan M. Taylor 2fd6c448c5 Refactor expr for strict Clippy checks 2026-10-28 06:09:20 +00:00
Dylan M. Taylor e55e288397 Refactor raster for strict Clippy checks 2026-10-28 06:09:20 +00:00
Dylan M. Taylor d562e10acf Refactor path for strict Clippy checks 2026-10-28 06:09:20 +00:00
dotCooCoo d4e33b2bda Fix #341: the platform quit runs last in App::logic
The merge of main put craft_quit::on_close_request inside
EffectcraftApp::tick, so the control channel, autosave, screenshots and the
desktop host's file dialogs still ran after it in the same logic callback.
The tick keeps the unsaved-changes check; each desktop host's logic now calls
panels::unsaved::quit_on_close last, after the device-loss close as well.
2026-10-27 22:46:27 -07:00
tylerjenningsw 0378816923 Export: pin the H.264 movie bytes the FilmCraft encoder this branch uses writes (#1701) 2026-10-27 19:54:42 -04:00
tylerjenningsw d302aaeb14 Merge branch 'fix/particle-birth-history' into fix/particle-birth-history-ps2-mercury
# Conflicts:
#	crates/effects/src/sim.rs
2026-10-27 19:50:52 -04:00
tylerjenningsw 902b58f0d7 Particles: format the simulation test imports (#1373) 2026-10-27 19:50:44 -04:00
tylerjenningsw c44d25b9ea Clipboard: format the keyframe text imports (#1908) 2026-10-27 19:50:41 -04:00
NetterSilverandClaude Haiku 5.5 d6308c6eed Reject '+' signs in Lottie text stroke colour hex pairs
Found while testing crates/lottie/src/import.rs:48-51 (hex_color)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 00:43:35 +01:00
dotCooCoo 49ba71b0ce Merge main into fix/macos-quit-crash 2026-10-27 14:21:30 -07:00
Claude 1c4e647d90 G1: dither only renders above 8 bpc (#2022)
An 8 bpc render has no finer values to dither, so its exports stay byte for byte what
they were (the pinned H.264 and ProRes files) and keep their size; 16 and 32 bpc renders
are still dithered on the way to 8 bits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYXhNN7W7nB6sKjwwU7CDM
2026-10-27 16:05:02 -05:00
dotCooCoo 6cb51e42c1 Scripts: socket tests wait for the buffers to stay full on macOS
`backpressure` stopped writing at the first WouldBlock. On macOS the
accepted stream does not keep the listener's 4 KiB receive buffer (it
gets about 114 KiB), so that WouldBlock only meant the 4 KiB send buffer
was full; it drained into the peer, and the 64 KiB write in
a_write_to_a_peer_that_stops_reading_fails_after_the_timeout and
a_blocked_socket_doesnt_hold_up_the_others_and_close_interrupts_it then
succeeded instead of blocking. Both tests failed on every run.

The helper now stops only after writes have stayed blocked for 200 ms,
still within its 16 MiB and 5 s limits. The socket code is unchanged.
2026-10-27 13:59:51 -07:00
tylerjenningsw 8d008e53da Merge branch 'fix/particle-birth-history' into fix/particle-birth-history-ps2-mercury
# Conflicts:
#	crates/effects/src/sim.rs
2026-10-27 16:59:26 -04:00
Claude 60146e86c4 G3: keep valid ROI crops on wide frames while still refusing overflow (#1577)
The ROI crop clamped every converted edge to +/-20_000 px, which changed
valid crops: a supported 30_000 x 100 frame at scale 1 with ROI
[10000, 0, 20000, 100] produced 10_000 x 100 instead of 20_000 x 100.
Valid edges reach about 120_000 px (30_000 px at render scale 4.0).

Clamp converted edges to +/-1_000_000 instead, subtract from the frame size
in i64 and narrow to i32 with saturation, and widen the arithmetic in
OutputModule::frame_size to match. Hostile quads (huge, inf, NaN) still
degrade to a valid 1 px frame without overflow.

Regression test next to hostile_roi_cannot_overflow_crop_math covers the
30_000 x 100 case, a scale 4.0 wide case and hostile inputs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYXhNN7W7nB6sKjwwU7CDM
2026-10-27 15:38:30 -05:00
Claude fa14090e72 G1: dither the final 8-bit conversion so gradients don't band (#2022)
Exports converted the float frame to 8 bits by plain rounding, so a smooth
gradient rendered at 16 or 32 bpc came out as flat bands, the same as at
8 bpc: raising the depth changed nothing visible. After Effects dithers
colours converted to 8 bpc for render and export; do the same.

- raster: dither_u8 and Image::to_rgba8_dithered / to_rgba8_over_dithered
  round each channel up or down against a hash of the pixel position with
  a fixed seed (deterministic, no clock or global RNG). A value already on
  an 8-bit code, as everything rendered at 8 bpc is, is unchanged.
- export: Cx::pixels dithers the colour for every 8-bit output (stills,
  H.264, ProRes, HEVC, AV1, VP9); GIF keeps plain rounding (palette).
- export: the Render Settings' Color Depth now overrides the project's
  depth for the whole render instead of only quantising the finished
  frame, so a 16 bpc render of an 8 bpc project keeps its precision.

Tests: crates/export/tests/it/banding.rs (ramp export at 8/16 bpc and with
Color Depth overrides), raster unit tests for dither_u8.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYXhNN7W7nB6sKjwwU7CDM
2026-10-27 15:38:08 -05:00
Kristian Myram 039e7ad1c1 #2459: use text-field Undo and Redo labels while typing 2026-10-27 20:35:53 +00:00
Claude 80b9aacea7 G1: Turbulent Displace Evolution changes the output (#943)
Turbulent Displace read its noise through value_noise, whose quintic fade also
eased the evolution axis. Near every whole revolution the pattern hardly moved:
Evolution 0 and 37 degrees (or 360 and 400) differed by well under a hundredth of
the frame, so different Evolution values looked identical, and the motion sped up
and slowed down within each revolution (the #1816 symptom).

Add value_noise_linear_z (x and y keep the fade, z is linear) on the CPU and in the
WGSL kernels and use it for Turbulent Displace on both paths. value_noise is
unchanged, so the other effects and the pinned simulation goldens are unaffected.

Tests: CPU test that non-equivalent Evolution values (fractional, past 360, past
720) all differ and that Cycle Evolution still matches 0/360; unit test of the
linear z axis; GPU-vs-CPU cases at small and large Evolution.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYXhNN7W7nB6sKjwwU7CDM
2026-10-27 15:34:34 -05:00
Kristian Myram 335d1f56ac #2459: reject raw opens through docking command aliases 2026-10-27 20:30:13 +00:00
minhero 9cdce36f9f Merge branch 'storytold:main' into fix/environment-light-no-effect-cast-shadows 2026-10-27 21:23:56 +01:00
Kristian Myram 82123f0d02 #696: reject workspace saves while window positions migrate 2026-10-27 20:23:24 +00:00
minhero 9d44da761d Merge branch 'storytold:main' into feat/2100-instance3d-layer-create 2026-10-27 21:23:13 +01:00
dotCooCoo e2e54c347d Apply explicit Cineon settings through menu dispatch 2026-10-27 12:05:52 -07:00
dotCooCoo 5e583c1549 Prompt to save before running Shutdown Scripts 2026-10-27 12:05:51 -07:00
Anubhav RaiandClaude Opus 5 06a1e6d27b engine: entry names are compared the way a filesystem might (#2123)
Two names that differ only in case are one file on Windows and on macOS as they
come, and Win32 drops a trailing dot or space, so `clip.png` with `CLIP.png`,
and `a.png` with `a.png.`, still landed on each other: ten entries extracted to
nine files on APFS, one footage item holding the other's picture. Collisions are
now looked up by a key with the case folded and those characters off the end,
while the name written stays as the entry asked.

Found by CodeRabbit on the pull request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-28 00:32:34 +05:30