Commit Graph
27 Commits
Author SHA1 Message Date
vangel76 88c02a953e Merge remote-tracking branch 'upstream/main' into q2400-merge
# Conflicts:
#	crates/av1enc/src/decide.rs
2026-10-29 20:32:16 +01:00
BFlat 6e3c864c98 Merge pull request #2396 from vangel76/av1enc-transform-specialise
av1enc: specialise the inverse transform per size, size the forward tmp buffer
2026-10-30 01:22:20 +07:00
BFlat fc4f9579b2 Merge pull request #2409 from vangel76/av1enc-first-frame-probe
av1enc: find the first frame's quantizer in fewer passes
2026-10-30 01:16:57 +07:00
BFlat ec4f3ed8b1 Merge pull request #2408 from vangel76/av1enc-rate-accuracy
av1enc: fix the bitrate overshoot of the bitrate mode rate control
2026-10-30 01:14:21 +07:00
bflatasticandClaude Opus 5.5 c99b20cac3 av1enc: inline the DCT stage helpers so const-generic sizes fold through them
The strict-clippy split of inverse_dct (#2496) passes n at run time; inlining keeps
inverse_dct_n's compile-time sizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:15:48 +07:00
bflatastic d1b736c594 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-29 23:15:17 +07:00
bflatasticandClaude Opus 5.5 5013012dab av1enc: decide.rs imports half_planes after the strict-clippy import list (#2496)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 10:34:57 +07:00
bflatastic 372c707e8c Merge remote-tracking branch 'origin/main' into HEAD 2026-10-29 10:34:55 +07:00
vangel76andClaude Sonnet 5.5 bcc21b939c Merge upstream/main into av1enc-scratch-buffers
Resolve crates/av1enc/src/decide.rs: main replaced the long try_tx/code_tx
argument lists with the TransformBlock struct. try_tx and code_tx keep the
branch's scratch-buffer design (TxScratch taken out of the decider) and take
a TransformBlock; reconstruct now takes its three output buffers as a
ReconBuffers struct to satisfy the denied clippy::too_many_arguments.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 15:39:44 +01:00
Dylan M. Taylor 8e67bfce47 Refactor av1enc for strict Clippy checks 2026-10-28 06:09:21 +00:00
vangel76andClaude Sonnet 5.5 c82633c8d7 av1enc: find the first frame's quantizer in fewer passes
Bitrate mode coded the first frame four times with a fixed gain. Now the
first pass still gets the old correction (30 quantizer steps per halving
of the size, which fits the starting range), and from the second
correction on the next quantizer is placed in the log domain of the
quantizer step, where the frame size is close to a line: false position
between the nearest passes that enclose the target, otherwise a secant
step with the measured slope. The search stops as soon as the size is
within 3 % of the target, when no untried quantizer is left between the
enclosing passes, or when it would repeat a quantizer (a target the
content cannot reach, where the old code coded the same frame at the
lowest quantizer up to four times). At most four passes remain.

The first frame is within 3 % of the target on the same cells as before
and nearer to it on most of the others; two cells where the size jumps
by a factor of 4 or more between neighbouring quantizers (noisy content)
land further away than with the old search. Total passes over the 28
test cells: 112 -> 70.

Part of #2390

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 13:38:38 +01:00
vangel76andClaude Sonnet 5.5 80af9b45e2 av1enc: fix the bitrate overshoot of the bitrate mode rate control
Bitrate mode gave key frames keyint-dependent extra bits (about 6.8 frame
budgets at the 60 frame default) but left the inter frames at the full
frame budget, so every group of frames overshot by (boost - 1) / keyint,
9.6 % at keyint 60 (up to +187 % at keyint 5 on some content). The
accumulated deviation was tracked against the boosted budget, so the
feedback never saw it.

Now a group of keyint frames adds up to keyint * frame budget: inter
frames get (keyint - boost) / (keyint - 1) of it. Further changes to the
frame level control:

- The quantizer follows the size error with a measured slope (quantizer
  steps per halving of the size, taken from consecutive inter frames)
  instead of a fixed 12 steps per doubling. On noisy content the curve is
  several times steeper than that and the old gain oscillated between
  1 kB and 42 kB frames.
- Key frames have their own quantizer offset, adapted from key frame
  sizes only, instead of feeding the key frame error into the inter
  quantizer. A key frame after the first that misses its budget by more
  than 19 % is coded again (at most twice more), with a corrected
  quantizer found in the log domain of the quantizer step.
- A deviation from the budget is paid back over 16 frames, bounded to
  0.35x..2x of the budget.

Constant quantizer mode is untouched (byte identical output). The
encoded stream in bitrate mode changes.

Adds a test that the rate stays within 5 % of the target over 180
frames for three 8 bit and one 10 bit setting.

Part of #2391

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 13:38:37 +01:00
vangel76andClaude Sonnet 5.5 590fd32055 av1enc: reuse scratch buffers in the transform path, the coefficient writer and the level search
try_tx and reconstruct allocated six Vecs per candidate (residual, coefficients,
levels, dequantised coefficients, inverse residual, reconstruction), the
coefficient writer allocated its level map per transform block, and the loop
filter level search cloned the luma plane for every candidate level.

- Decider keeps a boxed TxScratch (32x32 arrays) that try_tx and reconstruct
  write into; the levels and the reconstruction of a candidate stay there and
  the caller copies out only what it keeps (the winning intra candidate, or the
  levels that go into the leaf). intra_leaf uses stack arrays for its
  predictions and a fixed array for the mode ranking and transform types.
- TileWriter reuses one level-map Vec, and the partition CDF at frame edges is
  read through a slice instead of a copy.
- The level search filters one working copy of the luma plane and restores it
  from the reconstruction before each candidate.

The encoded stream is unchanged.

Fixes #2374

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:26:27 +01:00
vangel76andClaude Sonnet 5.5 b4b7c2d339 av1enc: build the three half-sample reference planes in one pass
Decider::new built the half-sample planes (8,0), (0,8) and (8,8) with
three full 2-D 8-tap passes (subpel_plane), each with per-tap clamping.
half_planes computes them together: the horizontal half-sample pass is
shared by (8,0) and (8,8), the phase-0 kernels are exact shifts
(Round2(128*v, 11) = Round2(v, 4), Round2(16*s, 11) = Round2(s, 7)), and
interior pixels skip the clamps.

subpel_plane is kept as the test-only reference; a new unit test checks
half_planes against it bit for bit (including 1x1, 5x3 and 10 bit).
The only change in decide.rs is the call in Decider::new.

Output is bit-identical.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:15:23 +01:00
vangel76andClaude Sonnet 5.5 12af1c19cb av1enc: truncate instead of floor, log2 lookup table, skip division in the dead zone
The quantiser in try_tx called libm floorf for every coefficient, estimate_bits
and the motion-vector cost called f64::log2, and coefficients that quantise to
zero still went through an f32 division.

- (x / q + rnd).floor() as i32 becomes (x / q + rnd) as i32: the operand is
  never negative, so truncation equals floor.
- log2 of small integers comes from a table built with the same f64::log2
  call (estimate_bits and the MV cost in motion_search), so the values are
  bit-identical.
- Coefficients below an exact per-(q, rounding offset) threshold are zero
  without dividing. The threshold is found once per Decider by a binary
  search over f32 bit patterns of the monotone quantiser expression.

The encoded stream is unchanged. Unit tests compare the new quantiser with the
old expression at the threshold boundary and on random values for every
quantiser index, and the table with f64::log2.

Fixes #2373

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:13:44 +01:00
vangel76andClaude Sonnet 5.5 18ce00da88 av1enc: specialise the inverse transform per size, size the forward tmp buffer
The inverse transform computed its bit-reversal permutations and loop
bounds at run time and went through one generic path for every size.
inverse_dct, the ADST permutations and inverse_2d are now const generic
over the size and inlined, so the permutations fold at compile time.

forward_2d zeroed a 4 KiB tmp buffer on every call, even for 4x4. It now
dispatches to a fixed-size version for 4, 8 and 16 (tmp is exactly n*n
floats); 32 keeps the run-time version, which measured faster than a
const 32 variant. The float operation order is unchanged.

Output is bit-identical.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:02:23 +01:00
aiongg 0173abde8c CI: dev-profile gate, one test binary per crate, nextest, line tables only (#2235)
* Dev profile: line tables only for workspace crates, no debug info for dependencies

* xtask ci: dev-profile gate, nextest, tests without examples, wasm --keep-going

* One integration-test binary per crate

* xtask ci: run effectcraft-gpu's shared-device tests in one cargo test process

* xtask ci: run every stage and report all failures

* xtask ci: name the stage type (clippy type_complexity)

* xtask ci: run every test pass and report all failures

* cargo fmt
2026-10-23 12:59:51 -04:00
Dexter 39e6907b5f G2: improve editing reliability and Windows rendering fidelity 2026-10-06 00:00:31 -05:00
Brandon ThomasandClaude Opus 5.5 ee90d87742 Never crash: AV1 encoder without expect()/unreachable!()
Replaces the encoder's 11 expect()/unreachable!() calls on internal invariants with handling
that can't panic: the rate-control state is passed in rather than re-borrowed, inter decisions
without a reference frame fall back to intra, per-plane results are bound directly, and the
writer's impossible branches become debug_asserts. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:22:59 -04:00
Brandon ThomasandClaude Opus 5.5 32c53aa35e av1enc tests: decode with a single-threaded filmcraft-av1 decoder
FilmCraft's AV1 decoder now frame-threads by default, so a picture may
come out of a later decode call; the round-trip check wants each
packet's picture from its own call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:15:39 -04:00
Brandon ThomasandClaude Opus 5.5 bfc81ef34a M13.4: av1enc README wording
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:13:05 -04:00
Brandon ThomasandClaude Opus 5.5 04e2e6b898 M13.4: av1enc SATD intra mode ranking, intra benches
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:12:59 -04:00
Brandon ThomasandClaude Opus 5.5 88ca7b45fc M13.4: av1enc speedups (vectorisable forward transform, separable sub-sample paths, cheaper loop filter search), more oracle cases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:40:03 -04:00
Brandon ThomasandClaude Opus 5.5 22ec8daf65 M13.4: av1enc frame-to-frame CDF carry-over, intra mode and transform type RD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:30:08 -04:00
Brandon ThomasandClaude Opus 5.5 917e5f7ae8 M13.4: av1enc sub-sample motion search, deblocking loop filter
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:26:15 -04:00
Brandon ThomasandClaude Opus 5.5 4130172bbe M13.4: av1enc key and inter frames, bit-exact with libdav1d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:11:08 -04:00
Brandon ThomasandClaude Opus 5.5 3a8b401e81 M13.4: hevcenc / av1enc crate skeletons (API contract, layers)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:42:12 -04:00