Skip to content

feat(bc5): inset-partition seed, regression refit, offset round - #14

Merged
verekia merged 1 commit into
mainfrom
feat/bc5-quality
Sep 24, 2026
Merged

verekia merged 1 commit into
mainfrom
feat/bc5-quality

Conversation

@verekia

@verekia verekia commented Sep 24, 2026

Copy link
Copy Markdown
Owner

BC5 error is 3.8–60% lower (MSE) on every real texture, and GPU time is unchanged. All three backends are updated: f16 WGSL, f32 WGSL and WebGL GLSL.

Changes

  • Inset pass-1 levels: pass 1 assigns pixels to levels against the min/max range narrowed by ~5.5/256 of the span on each side (scale ×7.3125/7, offset 11/32). The top and bottom levels now gather every pixel near the extremes, not just the single extreme pixel, so the least-squares refit lands much closer to the best endpoints. An exhaustive endpoint search showed the plain min/max seed leaving 0.7–6 dB on the table.
  • Lossless flat blocks: blocks spanning 7 or fewer values get a 7-wide endpoint window, so every value lands exactly on a palette level.
  • Cheaper refit: the least-squares solve is now a plain line fit from the pass-1 moments (~12 ops per channel). It is accepted whenever it stays in 6-interp mode. The old accept-if-better pricing changed nothing on the inset partition, and dropping it pays for the offset round.
  • Offset round: both endpoints shift by the rounded mean leftover error of the shipped levels. Pass 2, now a loop over quads, only has to add ΣL. A whole-level shift moves every palette entry equally, so a block can never get worse, whichever decoder reads it.

Results

Measured on /eval (M3, hardware-decoded quality, interleaved timing):

Content MSE vs main
Normal maps (rock/wood, 1K–4K) −3.8 … −6.3%
Colour / roughness / AO / packed −3.8 … −9%
Displacement (1K–4K) −9 … −60%
Synthetic normal test card −2.9% (−4.8% CPU-decoded)
  • GPU time: f16 +0.6% geomean, which is within timer quantization; f32 −1.1%.
  • WebGL: output is byte-identical to the f32 WGSL on 13 images, including odd sizes and flipY. The constants are exact dyadic values so both compilers fold them identically.
  • /test: 0 failures. BC5 PSNR floors are raised to the new levels, and the GPU now beats the CPU reference by 0.18–0.30 dB on every row checked against it.
  • bun test: 225 pass.

Notes

  • Apple GPUs decode BC4/BC5 with BC7-style weights (0, 9, 18, 27, 37, 46, 55, 64)/64 rather than exact sevenths, so hardware-decoded PSNR on M3 sits about 0.07 dB below CPU-decoded. The encoder still targets the spec palette.
  • A full second refit round would add about 0.1 dB more but costs 18% GPU time at 1K, so it was rejected. The offset round gets half that gain for almost nothing.

🤖 Generated with Claude Code

BC5 quality up 3.8–60% lower MSE on every real texture at GPU parity
(f16 +0.6% within timer noise, f32 −1.1%), all three backends.

- Pass-1 levels are assigned against the min/max range inset by ~5.5/256
  of the span (scale ×7.3125/7, offset 11/32 — exact dyadic constants), so
  the extreme levels gather every pixel near the extremes, not just the
  extreme pixel; the refit lands far closer to the optimum (exhaustive
  endpoint search showed the plain bbox seed leaving 0.7–6 dB).
- Spans ≤ 7 (incl. flat blocks) seed a 7-wide window: lossless.
- Refit is the plain least-squares line off the pass-1 moments (~12 ops
  per channel), accepted whenever it stays in 6-interp mode — the E(δ)
  pricing changed nothing on the inset partition and its cost pays for:
- Offset round: both endpoints shift by the rounded mean residual of the
  shipped levels (only ΣL is new in pass 2, now a loop over quads); never
  worse on those indices under any decoder's weights.

Hardware-decoded (/eval, M3): normals −3.8..−6.3% MSE, colour/roughness/
AO/packed −3.8..−9%, displacement −9..−60%; synthetic normal card −2.9%
(−4.8% CPU-decoded). WebGL GLSL byte-identical to the f32 WGSL (13
images incl. odd sizes and flipY). /test BC5 pins raised; 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@verekia
verekia merged commit b7bfc4b into main Sep 24, 2026
1 check passed
@verekia
verekia deleted the feat/bc5-quality branch September 24, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant