- Default disk cache size 20 GB (was 100 GB); settings version 3 migrates the stored old
default and keeps a size the user chose.
- The cache keeps max(2 GB, 5% of the disk) free: sysinfo::disk_space (df -Pk / PowerShell
DriveInfo) is read in the background from memory_tick and caps the limit
(DiskCache::set_disk_space); cache.diskStats reports limitBytes.
- Eviction goes down to 90% of the limit, so the LRU sort runs once per tenth of the limit
written instead of on every write at the limit.
- Failed writes are counted (cache.diskStats failed) and the first is logged.
Also fixes#2063.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Much of a layer buffer is often transparent (a stroked ring, a glow-padded
text layer, a precomp canvas), yet every destination pixel sampled it
bilinearly, and 8/16 bpc comps then re-quantised the layer's whole rotated
bounding box.
- Bilinear warps skip 16x16 source tiles whose pixels (and a one-pixel
apron) all have zero alpha: their samples are transparent and were never
drawn.
- The fast affine warp reports the rectangle it changed and, given the
comp's levels, quantises each pixel as it blends it, so draw_comp has
nothing left to re-quantise for that layer. quantize_px moves to
effectcraft-raster for this.
- quantize_rect skips runs of four all-zero (+0.0) pixels, which quantise
to themselves.
- to_rgba8 / to_rgba8_over convert row by row instead of per 4-byte item.
Every pixel stays bit-identical: tests compare the warp against the
reference path, fused quantising against quantising after blending, and
the zero-run skip against per-pixel quantising.
With the previous commit, demo comp to H.264 (`cargo xtask bench --scenario
demo --format h264 --frames 300`, 11 interleaved runs on a 16-thread Ryzen):
wall 9.26 s -> 6.97 s median (-24.8%), 8.55 s -> 6.55 s min (-23.3%); CPU
time -23%. Output byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wiggle Paths had Size, Detail, Points, Wiggles/Second, Correlation,
Temporal Phase and Random Seed, but no Spatial Phase, which After
Effects lists among its properties (ADBE Vector Spatial Phase). Spatial
Phase now moves each point's own displacement along its subpath: 360
degrees is once around, and a fraction of a point is interpolated
between neighbors. At 0 degrees a path draws as before.
A project saved without the property gets it at 0 degrees, after
Temporal Phase, when it opens. Scripts can read and set every Wiggle
Paths property by its match name.
For a precomp layer whose composition had a proxy, the renderer
decoded the proxy file with frame_with and drew it as decoded.
Interpret Footage > Proxy's Invert Alpha, Interpret As Linear Light
and Separate Fields had no effect on the render, and neither did the
layer's Frame Blending. A footage item's proxy already goes through
footage_frame and gets all of them.
The composition proxy now goes through footage_frame too, so it is
decoded the way a footage proxy is.
The PCF tap jitter was keyed on the receiver's 1/8-texel cell in the
shadow map. The GPU computes the receiver's map position with last-bit
differences from the CPU, so a receiver next to a cell line could take
another cell's jitter on one backend, and every tap moved. In a wide
penumbra that changed the coverage by several 8-bit levels:
gpu_matches_cpu_wide_penumbrae failed on Windows with 15 of 4159
shadowed pixels on an NVIDIA RTX 5080 and 22 on an AMD Radeon iGPU,
against a budget of 8.
tap_jitter now blends the hashes at the four corners of the cell by
the receiver's position in it, in shade.rs and in advanced3d.wgsl. At
the lattice points the jitter is the per-cell hash it was before. A
CPU test checks that the coverage does not jump at a lattice line.
Poison recovery, OsStr::to_str and char predicates only; S4325 casts are
f64 to f32 and stay.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>