Problem: the AV1 in-loop filters ran on one thread per frame (postfilter::apply
had an unused pool). On a single-tile stream they are a quarter to a third of a
frame, and since references are waited for as whole frames, that serial tail
sat on the critical path of every frame that references it.
Fix: the three filters run in bands of 64 luma rows on the decoder's existing
rayon pool, one join per filter per frame. CDEF and loop restoration write only
their own rows of a separate output. The loop filter's edges never touch the
same sample, and only a band's top horizontal edges reach into the band above:
each band refilters the vertical edges of a copy of the 8 rows above it, runs
those edges against the copy, and the samples they changed are written back.
With one thread the code path is the old one. Output stays bit-exact (1 vs 16
threads vs libdav1d; new 4:2:2 / 4:4:4 libaom fixtures). perf.rs gains a
low-delay 1080p and a single-tile 2160p fixture and the all-core stage
breakdown. 16 threads: 1080p GOP 1.16-1.30x, low delay 1.28x, 2160p 1.14x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CDEF and inter-prediction filter rows: fixed-size views through `get()` + `first_chunk(_mut)`
instead of slice + `try_into().expect()`.
- A missing reference frame predicts from the current frame instead of `expect`.
- Frame slots, output and in-flight queues, scalable-layer selection: no `expect`.
- `#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented,
clippy::todo, clippy::unreachable)]` outside tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sub-pixel filter passes run over fixed block widths; frame / region planes are recycled
through a bounded pool (zeroed on reuse) instead of fresh calloc / munmap per frame; the
palette mode-info arrays (40 bytes per 4x4) exist only when screen content tools are allowed;
CDF adaptation is branch-free. Draft mode skips deblocking, CDEF and loop restoration of shown
frames that refresh no reference slot. Media-stack test: VP9 / HEVC / AV1 draft playback only
reaches draft requests, and an export afterwards is bit-exact with ffmpeg.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tiles decode in parallel into private region buffers (Plane / MiInfo with a
frame-coordinate origin) that are merged afterwards; intra block copy frames
keep whole-frame buffers. Post-filter row bands run on a rayon pool (par.rs,
behind the `threads` feature, sequential on wasm32). Frames become jobs that
wait on the reference slots they read and run on frame worker threads, so
independent GOP layers decode concurrently; output stays in order and each
Picture carries the pts of its temporal unit (Decoder::decode_pts, flush,
flush_result). codecs::Av1Decoder passes pts through and drains on flush.
Oracle tests now compare 1 thread, many threads and libdav1d (bit-exact),
with new multi-tile fixtures and an ignored 1080p set. README records fps
before/after.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bit-exact restructuring of the per-sample spec processes (all oracle and conformance
vectors still match libdav1d):
- Inter prediction: unscaled references use one filter phase per block, so the 8-tap
filters run tap-outer over whole rows (no per-tap clamps inside the frame, identity
phases copy, zero-tap rows skipped); scratch buffers are reused, not allocated.
- CDEF: padded 12x12 block with availability; blocks whose taps are all available run
branch-free in i16 lanes; zero-strength blocks are skipped.
- Inverse transforms: generic over lanes, 4 or 8 rows/columns at a time in 32-bit
arithmetic (exact up to 10-bit per the spec's intermediate range); all-zero rows skipped;
only the used dequant area is cleared.
- Deblocking: early exits before strength lookup; per-length sample filters; the wide
filters as sliding-window sums.
- Symbol decoder: a 64-bit window of inverted bits instead of bit-at-a-time refills.
- Coefficient contexts from a zero-padded level buffer (no bounds checks); mode info
stored with row fills; reconstruction on row slices.
Single-threaded 1080p (thread CPU): GOP 20.7 -> ~52 fps, high-quality GOP 9.6 -> ~20 fps,
4x2-tile GOP 22 -> ~48 fps, 10-bit GOP 17 -> ~44 fps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decoder::stats() reports frames, tiles and the busy time of setup, tile decoding,
deblocking, CDEF, superres, loop restoration and output. The ignored perf test
measures single-threaded throughput with the thread CPU clock (robust to machine
load), prints ms/frame per stage, and the all-cores wall-clock rate next to the
load average. Adds high-quality and 4x2-tile 1080p fixtures. Decoder::with_threads
and flush() are added as the API the threaded decoder will use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Film grain (7.18.3) on output pictures, bit-exact against libdav1d on the libaom
23-film_grain-50 vectors (8/10-bit) and an SVT film-grain stream; super-resolution now
oracle-tested (SVT superres denominator 12) and bit-exact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inter mode info syntax, spatial/temporal MV candidate stacks, motion field estimation and MV
storage, scaled references, sub-pixel filters, global/local warp, OBMC, wedge/diffwtd/inter-intra
masks, distance weights; intra block copy; one output frame per temporal unit with spatial
layers. Bit-exact against libdav1d on SVT-AV1 GOPs and the libaom size/quantizer/cdfupdate/mv/
mfmv/svc/monochrome/intrabc vectors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Av1Decoder in filmcraft-codecs: feeds the av1C configuration OBUs before the
first sample (and after every reset), converts pictures to 8/16-bit planar
YUV with the stream's colour description, and reports random-access points
from the first frame header of each temporal unit (shown key frame) so GOP
seeking ignores bogus container sync flags. Matroska V_AV1 tracks map to an
av01 sample entry; MP4 labels show the AV1 profile.
All-intra 8-bit MP4 and 10-bit WebM files seek sample-exactly against
libdav1d. Inter frames still return "unsupported" until inter prediction
lands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Loop filter (7.14: all filter sizes, deltas, segment/ref/mode adjustments),
CDEF (7.15), superres upscaling (7.16) and loop restoration (7.17: Wiener,
self-guided, switchable, stripe handling) in decode_frame_wrapup.
All-intra libsvtav1 fixtures with every in-loop filter (64/128 superblocks,
edge filter on/off, 8/10-bit) and libaom's av1-1-b8-02-allintra conformance
vector (39 frames, downloaded on first use) are bit-exact against libdav1d.
Frames coded slightly larger than the sequence maximum are accepted, like
other decoders do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>