Commit Graph
120 Commits
Author SHA1 Message Date
Brandon Thomas cd7f932853 Merge remote-tracking branch 'origin/main' into fix-865 2026-10-11 22:22:32 -04:00
Brandon ThomasandClaude Opus 5.5 121e7ba27e render: Median radius stays within the parameter's range (0–100 source pixels)
The filter costs radius² per pixel; with only the image size as a cap, a
project file's radius could make one 4K frame take hours.
Regression test: median_radius_stays_in_the_parameter_range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:21:27 -04:00
Brandon Thomas 5f7745d592 Merge remote-tracking branch 'origin/main' into fix-1046 2026-10-11 22:19:41 -04:00
Brandon Thomas 1f8c97b92a Merge pull request #1047 from whatever-industries/fix-extract-and-caption-targets
Fix Extract hard cutoffs and clipped caption font targets
2026-10-11 19:15:57 -04:00
Brandon Thomas 9a2b41a1e9 Merge pull request #1017 from JordyUSA/fix/965-hdr-playback
#965: HDR and wide-gamut media play on the GPU path
2026-10-11 19:15:56 -04:00
Brandon ThomasandClaude Opus 5.5 039dd06110 render: as_chunks in the Extract edge test (clippy::chunks_exact_with_const_size)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:12:10 -04:00
Brandon Thomas 064c9b92e3 Merge pull request #1036 from ossini/fix/51-stereo-track-downmix
fix(render): 5.1 sources on a stereo track fold down instead of losing C and surrounds
2026-10-11 18:53:01 -04:00
Vikram Bhaskaran 6251b39b8f #907: apply Median radius in source pixels without a 4px cap
Radius was clamped to 4 working pixels, so values above 4 matched radius 4
at full resolution. Scale by px_scale and cap only to the image size.
2026-10-11 22:48:24 +11:00
whatever-industries f10fda5883 release-gate: Fix Extract edges and caption font targeting
Handle zero-softness Extract as a hard cutoff in both CPU and GPU paths.
The former 0.0001-wide substitute fade introduced grey edge pixels and
amplified transfer-function rounding beyond the existing parity tolerance.
Retain the positive-softness calculation and add cutoff, inversion and
premultiplied-alpha regressions without relaxing the comparison tolerance.

Constrain caption font automation targets to the visible part of each
row. A partly clipped row could previously expose a click point outside
the popup, leaving the selected font unchanged. Preserve the existing
font-loading behavior, assets and automation IDs.
2026-10-11 04:06:14 -07:00
Oskar faeca7aeab audio: regression test for 5.1 sources on stereo tracks 2026-10-11 12:58:23 +02:00
Oskar d22775dd41 audio: 5.1 sources on a stereo track fold down instead of losing C and surrounds
A six-channel clip on a stereo track played only source channels 0 and 1,
so the centre (dialogue in film mixes), LFE and surrounds were dropped.
Fold it down with ITU-R BS.775, as the Mix and the Source Monitor already
do. A six-channel Modify > Audio Channels pick folds the same way; mono
and stereo picks keep playing exactly the picked channels.
2026-10-11 12:58:23 +02:00
JordyUSAandClaude Opus 5.5 2d4422f408 #965: HDR and wide-gamut media play on the GPU path
On a Rec. 709 sequence every PQ, HLG, BT.2020 or P3 clip left the GPU
path: each frame was converted to a float image on the CPU (curve, HLG
OOTF, gamut matrix, BT.2390 tone mapping, desaturation) and resampled
there. A 2160x3840 10-bit HLG clip took 90 ms per frame (48 ms scaled to
fit a 1080x1920 sequence) against 33 ms at 30 fps; the same clip in SDR
took 18 ms.

The compositor's shader now runs that input transform itself:
InputTransform::gpu_params gives its stages as numbers (log curves and
camera-gamut compression stay on the CPU), PlanLayer::input carries the
transform for Y'CbCr layers on a plain pipeline, and execute_cpu applies
the same transform as the CPU reference. HDR clips now plan in 15-19 ms
per frame, and the GPU result is within one output code of the CPU's for
HLG, PQ (with and without a mastering peak) and BT.2020 SDR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:57:45 +07:00
Nigel Ihza Firdausy 4969601e9a render: advance Noise grain from exact frame ticks 2026-10-11 11:06:35 +07:00
Earl Co dcabbda07d M7: optimize settled mixer DSP and isolate throughput gate 2026-10-11 02:17:02 +02:00
vangel76andClaude Opus 5.5 50ce8b090c #805: Morph Cut morphs along optical flow instead of cross-dissolving
Problem: Morph Cut rendered as a plain Cross Dissolve, so a jump cut in a
talking-head shot showed the subject twice at half opacity during the
transition, and the GPU plan mixed it as a dissolve too.

Fix: Morph Cut warps both frames along the dense optical flow between them
(the Lucas-Kanade flow of #780, crates/render/src/flow.rs) and blends, so the
subject moves from its place in the outgoing shot to its place in the incoming
one; where the frames do not correspond it stays the cross dissolve. Frames
inside a Morph Cut are planned whole on the CPU (no longer a GPU-mixable
"simple" transition) and cost as a complex transition in the preview estimate.
Morph Cut goldens re-blessed. No face tracking yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:32:08 +02:00
vangel76andClaude Opus 5.5 e56ddd0e36 #780: Optical Flow time interpolation synthesises motion-compensated frames
Problem: a speed-changed clip set to Time Interpolation > Optical Flow
rendered exactly like Frame Blending, a cross-fade of the two neighbouring
source frames, so moving objects showed double images.

Fix: new filmcraft_render::flow. It computes dense coarse-to-fine
Lucas-Kanade flow both ways on a luma pyramid at most 640 px wide, drops
motion wherever standing still explains the window as well (flicker on
static content), forward-splats both flows to the in-between time
(Baker et al. 2011), and warps both frames there. Each fetch is weighted
by how well its own flow agrees, so uncovered background comes from the
frame that shows it. Pixels that look the same in both frames, or where
neither fetch agrees, fade to the plain cross-fade. media_image uses it
for OpticalFlow clips. docs/gaps.md G18 and target-app-parity updated
(also drops the #415 / #458 items, already fixed on main).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:50:15 +02:00
Brandon Thomas 74963e0e7e Merge pull request #760 from maccagnit/gpu-export-cpu-frames
GPU export: a frame planned as one CPU image skips the renderer pool (Auto was slower than Off)
2026-10-10 17:01:07 -04:00
Brandon Thomas 7c919da28e Merge pull request #747 from vangel76/fix-719-lumetri-vignette-roundness
#719: positive Roundness rounds the Lumetri vignette
2026-10-10 15:52:49 -04:00
Tommaso MaccagniandClaude Opus 5.5 13d8e73e8e export: a frame planned as one CPU image does not take a pooled GPU renderer
With GPU rendering on, a frame the planner hands back whole (an adjustment
layer, a complex transition, an HDR / wide-gamut sequence) was still rendered
by render_sequence, but inside RendererPool::with: as many frames at a time as
there are pooled renderers (2 to 4) instead of one per export thread. A
timeline graded on an adjustment layer exported slower with Auto than with Off.

Pipeline::frame asks the planner first (plan::is_cpu_frame, the test
plan_frame itself makes) and renders such a frame on the CPU directly. It
counts as a fallback now, not as a GPU frame. The picture does not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:40:37 +02:00
Brandon Thomas 334bccde88 Revert "Merge pull request #674 from vbasky/fix/542-limiter-lookahead"
This reverts commit 62a0eea519, reversing
changes made to 23ac344798.
2026-10-10 15:23:52 -04:00
Brandon ThomasandClaude Opus 5.5 2140d9c9ab Restore green after the community batch: rustfmt, clippy as_chunks_mut in webm_alpha, zh-cn caption-track and es shutter-angle rows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:07:59 -04:00
Brandon Thomas ea33e3cb00 Merge pull request #679 from vbasky/fix/415-transform-shutter
Apply Transform shutter angle when the override is on
2026-10-10 14:54:38 -04:00
Brandon Thomas db352fc963 Merge pull request #675 from vbasky/fix/458-ultra-key-setting
Apply Ultra Key Setting as a parameter preset
2026-10-10 14:54:27 -04:00
vangel76andClaude Opus 5.5 dfbcadb88c #719: positive Roundness rounds the Lumetri vignette
Problem: the Lumetri vignette scaled its horizontal coordinate by
`1 + vround * 0.0` and applied aspect correction only for negative
Roundness, so 0 … +100 rendered identically (CPU path, the GPU op and the
GPU shader parameters alike). Negative values moved towards a circle,
the direction positive values should go.

Fix: one helper, `gpufx::lumetri_vignette_aspect`, scales the horizontal
coordinate by aspect^roundness: 0 keeps the ellipse that follows the
frame, +100 is a circle, negative values widen the oval. Non-finite or
out-of-range values fall back safely. The CPU path, the CPU FxOp and the
GPU parameter packing all use it, so they stay in parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:53:19 +02:00
Vikram Bhaskaran 9e77d9d786 Apply Transform shutter angle when the override is on.
The Transform effect listed a shutter angle but had no override, so motion blur never used it. Checking Use Effect Shutter Angle now samples the animated transform across that angle.
2026-10-11 02:48:52 +11:00
Vikram Bhaskaran 80db16ebd5 Apply Ultra Key Setting as a parameter preset.
Relaxed and Aggressive previously only scaled two internal multipliers, so the matte stayed at Default. Switching Setting now writes Premiere-like matte values and the renderer uses that set, including Custom for hand-tuned keys.
2026-10-11 02:34:08 +11:00
Vikram Bhaskaran 6d1c4fdb84 Pass Hard Limiter look-ahead into the DSP limiter.
The project Look-Ahead Time parameter was stored but never applied, so 0 ms and 30 ms mixed the same clip. The limiter now sizes its look-ahead from that parameter and pre-rolls for the chosen window.
2026-10-11 02:28:52 +11:00
Brandon ThomasandClaude Opus 5.5 762344769b Merge origin/main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:21:39 -04:00
Brandon ThomasandClaude Opus 5.5 4304cd9382 Merge origin/main into #421; GPU export opt-in, wgpu error handler, drop panicked renderers
- GpuRendering defaults to Off (opt-in per export) until measured on Windows and macOS;
  docs and ROADMAP say so.
- The shared export device installs an uncaptured-error handler and a device-lost callback
  that log once and retire GPU export for the session (frames go to the CPU) instead of
  wgpu's default panic.
- RendererPool drops a renderer that panicked and retires the pool, so the rest of the
  export renders on the CPU (regression test in gpu_hook_scenarios); the duplicate
  hostile-size test that raced on the global registry is folded into it.
- Rebased onto main's render_plan/transitions: render_export_prepared reads the linear
  accumulator after render_plan; transition (Composite) plans fall back to the CPU.
- es/ja rows for the new Export strings; web_time::Instant in the export pipeline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:21:38 -04:00
Brandon ThomasandClaude Opus 5.5 57bd528769 Merge origin/main into #445; PAR test fixture uses MediaInfo::audio_streams
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:20:09 -04:00
Brandon Thomas a8e38c4e07 Merge remote-tracking branch 'origin/main' into fix-445 2026-10-10 05:47:31 -04:00
Brandon Thomas c76d7931ca Merge pull request #514 from vbasky/feature/graphic-gradient
Fill graphic shapes and text with a linear gradient
2026-10-10 05:02:04 -04:00
MarekSpodBiedry 09f0cf1cc7 Parametric Equalizer: use the full EQ so master gain, bands 1/2/4/5 and pass filters reach the audio (#540) 2026-10-10 09:14:46 +02:00
Vikram Bhaskaran ac83bae223 Blend graphic gradients in linear light and keep per-character fills solid.
Stops were mixed in encoded sRGB, and any run with its own fill turned the whole layer into a solid colour. The ramp now blends in linear light, turns with the layer, and leaves a per-character fill solid.
2026-10-10 13:18:45 +11:00
Vikram Bhaskaran d55ebe94e5 Fill graphic shapes and text with a linear gradient.
Appearance › Fill Type paints a ramp across the layer bounds, and the ramp turns with the layer. A per-character fill colour still paints those characters as a solid.
2026-10-10 12:05:03 +11:00
lloydandClaude Opus 5.5 ff8728af08 Anamorphic media: pixel aspect ratio in placement, monitors and New Sequence From Clip
A 1440x1080 clip with 4:3 pixels showed squeezed: the demuxers read its
pixel aspect ratio, but nothing after import used it. New Sequence From
Clip made a 1440x1080 sequence with square pixels, the Motion transform
sized clips from storage pixels (pillarboxed at 1440 wide in a 1920x1080
sequence), and the monitors drew the frame at its storage aspect.

- project: checked_par / sane_par / par_ratio / conformed_size treat
  file and project ratios as hostile (a zero term, or more than 8:1
  either way, is square). MediaClip::pixel_aspect (Interpret Footage
  wins over the file), Project::source_par (subclips follow the parent,
  nested sequences have their own, graphics and adjustment layers have
  none) and Project::conformed_source_size.
- render: motion_matrix takes the source's ratio and works in square
  display units (anchor in source pixels, position in sequence pixels,
  scale and rotation at the display aspect); Scale to Frame Size fits
  the display size. CPU, GPU plan, adjustment regions and the mask /
  graphics overlays share it.
- engine: New Sequence From Clip, New Sequence from an item and
  multicam sequences copy the clip's ratio; default media scaling and
  Fit / Fill frame use the display size; generators made for a
  non-square sequence get its pixels; transcodes render pixel for
  pixel. clip.interpretFootage takes pixelAspect ([num, den] or "file").
- ui: the Source and Program monitors draw the picture at its display
  aspect; overlays map both axes; Sequence Settings shows the display
  aspect (16:9 for 1440x1080 at 4:3).

Tests: render par_tests (fill, GPU plan parity, Scale to Frame Size,
non-square sequences, rotation, Interpret Footage, hostile ratios),
engine par_tests (New Sequence From Clip, overwrite into 1920x1080,
default scaling, Fit / Fill, Interpret Footage with undo and refused
values, hostile file ratios, generators, and ffmpeg-made MP4 and MKV
files), project unit tests, and the headless par_ui monitor test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:28:56 +02:00
Brandon Thomas b32d1cd5ed Merge remote-tracking branch 'origin/main' into fix-361 2026-10-09 10:17:58 -04:00
Brandon ThomasandClaude Opus 5.5 701d7c7111 Merge origin/main into multi-stream audio
Keeps #359's contiguous_starts audio timing, now per stream in Mp4Audio /
MkvAudio (starts and preroll in source sample frames). MP4 audio streams and
their decoders are built from the same (track, info) pairs so they stay
index-aligned. Adapts new main code to audio_streams / TrackItem::audio_stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:08:09 -04:00
Brandon Thomas d84fdc1268 Merge remote-tracking branch 'origin/main' into fix-337 2026-10-09 10:04:46 -04:00
Brandon ThomasandClaude Opus 5.5 faa4bf953d Render plan: restore the detailed module doc and add the Push / PlanStep paragraph
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:03:38 -04:00
Brandon Thomas a6ac2d7eff Merge remote-tracking branch 'origin/main' into fix-308
# Conflicts:
#	crates/ui-egui/src/perf.rs
2026-10-09 10:01:25 -04:00
Brandon ThomasandClaude Opus 5.5 a66be150e3 WINCI: ignore the mixer throughput benchmark only where it times wall clock
On Unix the benchmark measures the test thread's CPU time, so suite load
does not move it and it keeps running. Without a per-thread CPU clock
(Windows) it falls back to wall time, which is load-dependent and flaky on
shared CI runners; ignore it only there and document how to run it.
The GPU effect-source hunks were dropped in the merge in favour of #376's
fs_fx_source, now on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:59:45 -04:00
Brandon Thomas efa89a7d2d Merge remote-tracking branch 'origin/main' into fix-337
# Conflicts:
#	crates/gpu/src/fx_tests.rs
2026-10-09 09:59:26 -04:00
Brandon Thomas bf0f2bd8ff Merge pull request #305 from Pathos0925/fix/296-mono-loudness
#296: Auto-Match measures a mono clip as one channel
2026-10-09 09:24:45 -04:00
Manuel Camacho 5ee4faf2d2 GPU7: Lumetri GPU op matches fn lumetri exactly (test); doc comments restored (#30)
Add CPU parity test verifying FxOp::Lumetri::apply matches original fn lumetri
exactly (0.00e0 sample difference) across all parity cases. Restore doc comments
on fn on and fn grade_space.

🤖 Written by agy (Antigravity Agent, Gemini 3.8 Flash)
2026-10-09 06:18:18 -07:00
Manuel Camacho 029e9bb040 GPU7: Lumetri Color (basic, creative, vignette) on the GPU compositor (#30)
Port the common slice of Lumetri Color (Basic Correction, Creative without
sharpen, and Vignette) to the GPU effect stage (FxOp::Lumetri, OP_LUMETRI = 31).

Lumetri instances with unsupported sections (input LUT, Look / look LUT,
sharpen, curves, color wheels, HSL secondary, or HDR grading) fall back to CPU.

Parity tests pass within max relative difference 4.28e-7 and 0.000% flips.

🤖 Written by agy (Antigravity Agent, Gemini 3.8 Flash)
2026-10-09 06:18:18 -07:00
Manuel Camacho ab334b2adf GPU5: Video Limiter on the GPU compositor (#30)
Move Video Limiter from CPU (crates/render/src/vfx/color.rs) to the GPU
compositor (filmcraft_render::gpufx::FxOp, crates/gpu/src/fx.rs, crates/gpu/src/fx.wgsl).
Parity matches CPU reference within 9.28e-6 across all axis modes, clip levels,
compressions, and gamut warning color.

🤖 Written by agy (Antigravity Agent, Gemini 3.8 Flash)
2026-10-09 06:18:17 -07:00
Manuel Camacho d2b608605e GPU4: Vignette on the GPU compositor (#30)
Add Vignette effect to the GPU effect stage (render::gpufx, gpu::fx,
fx.wgsl). Evaluated parameters are shared between the CPU reference and
GPU compute passes. Replaces old CPU vignette body in vfx::color with
gpufx::FxOp::eval and gpufx::FxOp::apply.

Gates: fmt, clippy, test -p filmcraft-render, test -p filmcraft-gpu,
layers, wasm all verified.

🤖 Written by agy (Antigravity Agent, Gemini 3.8 Flash)
2026-10-09 06:18:17 -07:00
Brandon Thomas 28235e25b8 Merge pull request #394 from txiki739/fix/reverse-source-frames
#318: a reversed clip shows exactly its source range
2026-10-09 08:17:01 -04:00
txiki739andClaude Opus 5.5 dfd67b2774 #318: a reversed clip shows exactly its source range
A reversed clip's source time is the instant just before its mirrored
position (source_out - rel - 1 tick). The MP4 reader rounds a request to
the nearest unit of the track timescale before picking the frame at or
before it, so one tick below a frame boundary landed on the next frame:
a clip trimmed to source frames 10-29 played 30 down to 11.

video_source_time snaps a reversed clip's video request down to the
start of the media frame that contains it (clamped to the source In and
the unsnapped time). Both places that ask a source for a clip's picture
use it: media_frame and the plan path. source_time_at is unchanged for
audio and nested sequences, and forward clips and frame holds keep
their request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:28:24 +02:00