imazen / imazen/zenpipe

Prefer RGBX8 over RGB8 as opaque working format — RGB8 has zero SIMD in zenresize

Open
#20 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Summary

The pipeline should prefer RGBX8 (4 bytes/pixel, padding byte) over RGB8 (3 bytes/pixel) as the working format for opaque images. RGB8 hits severe SIMD penalties in zenresize and moderate penalties in zenfilters scatter/gather.

When the pipeline needs to drop alpha (e.g., RemoveAlpha for JPEG output), it should produce RGBX8 instead of RGB8. The encoder can strip the padding byte at write time — zenjpeg already accepts RGBX8 natively.

## Findings

### zenresize: 3-5x penalty for RGB8

- **Horizontal filter** (`simd/x86.rs:489`): `match channels { 4 => filter_h_u8_4ch (AVX2 ymm madd), _ => filter_h_u8_generic (pure scalar) }`. RGB8 gets **zero SIMD** — falls to nested scalar loops.
- **Vertical filter** (`simd/x86.rs:1258`): processes `as_chunks::<16>()`. RGBA8 = 4 pixels/chunk (aligned). RGB8 = 5.33 pixels/chunk (misaligned) → most of the row hits scalar tail processing.

### zenfilters: 10-20% penalty for RGB8

- Scatter/gather (`simd/x86.rs:618`) deinterleaves to planar Oklab f32 then does SIMD matrix math. The deinterleave step is scalar for both 3ch and 4ch, but 4ch loads are aligned.

### zenblend: no RGB8 support

- Requires premultiplied linear f32 RGBA. RGB8 must be upconverted regardless.

### linear-srgb, zenpixels-convert: no penalty

- Element-wise LUT operations — channel count doesn't affect SIMD throughput.

## Proposal

### zenpipe

1. **RemoveAlpha should emit RGBX8 instead of RGB8** when the result stays in the pipeline (not going directly to an encoder). The padding byte costs 33% more memory but avoids the 3-5x resize penalty.

2. **For WASM/canvas targets**, keep RGBA8 through the entire pipeline — `putImageData()` wants RGBA anyway, and skipping RemoveAlpha entirely avoids a pointless format round-trip.

3. **`ensure_fmt!` should prefer RGBX8 over RGB8** when converting opaque RGBA to a 3-channel target. Or better: never convert to RGB8 internally at all — let the encoder do it.

### zenpixels-convert

4. **RowConverter should have a fast RGBA8→RGBX8 path** (just leave the alpha byte, or memset to 0xFF). Currently RGBA8→RGB8 requires a per-pixel 4→3 byte shuffle.

5. **RowConverter should prefer RGBX8 as the negotiated format** when a downstream consumer accepts both RGB8 and RGBX8. The codec format negotiation in zencodecs should rank RGBX8 higher than RGB8.

### zenresize (long-term)

6. **Add internal RGB8→RGBX8 upconversion** before SIMD kernels, strip padding on output. This is cheaper than the scalar fallback and makes the 3-channel API not a performance trap.

## Downstream format acceptance

| Encoder | RGB8 | RGBX8 | Notes |
|---------|------|-------|-------|
| zenjpeg | Native | Native | Both in `ENCODE_DESCRIPTORS` |
| zenbitmaps BMP | Native | Via Bgrx8 | |
| Canvas/WebGL | Needs expand to RGBA | Set alpha=0xFF → RGBA | One memset |

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing RemoveAlpha and ensure_fmt! in zenpipe, then inspect RowConverter in zenpixels-convert and codec format negotiation in zencodecs. Compare the proposed RGBX8 handling across opaque pipeline output, WASM/canvas targets, and encoder acceptance; done requires an agreed scope and consistent format preference without regressing those downstream paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, wasm
Domain
computer-graphics, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.