Skip to content

Fix stale coder state when subsampling changes at the same image size - #115

Open
adam-ce wants to merge 4 commits into
CESNET:masterfrom
AlpineMapsOrgDependencies:feature/coder-reinit
Open

adam-ce wants to merge 4 commits into
CESNET:masterfrom
AlpineMapsOrgDependencies:feature/coder-reinit

Conversation

@adam-ce

@adam-ce adam-ce commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Two bugs when reusing an encoder/decoder with a different JPEG layout (e.g. subsampling) at the same image size:

  1. Encoder: preprocessor not reinitialized. gpujpeg_preprocessor_encoder_init() was only called on img_changed, so after 4:4:4 → 4:2:0 it kept the old component pointers, widths and sampling ratios. Result: wrong chroma in the JPEG; if gpujpeg_coder_init_image() reallocated the buffers (4:2:0 → 4:4:4), it writes to freed device memory. Now it is also reinitialized when gpujpeg_parameters_equals() reports a change (function made non-static).

  2. CPU Huffman host buffer overflow. data_quantized was allocated with data_size, but gpujpeg_coder_init_image() keeps it as long as the frame fits data_allocated_size. Example: 4:4:4 with restart markers (GPU Huffman, large device buffer), then 4:2:0 without restart markers (host buffer allocated at 4:2:0 size), then 4:4:4 without restart markers → 2× overflow. This affects the encoder with restart_interval == 0 and the decoder with < 32 segments. Now it is allocated with data_allocated_size.

Test: test_coder_reinit reuses one encoder and one decoder for 256×256 frames: 444/RI 8 → 420/RI 0 → 444/RI 0 → 420/RI 8, plus a step that changes only the decoder output format. After each step it compares the result with a fresh coder.

  • Upstream d67e37b fails at step 1 (reused encoder differs from a fresh one), so the overflow step is never reached there.
  • This branch: the test passes, all 4 CTest suites pass, and compute-sanitizer memcheck on the test reports 0 errors and 0 leaks (RTX 4090, CUDA 13.4).

The host buffer for CPU Huffman coding was allocated with the current
data_size but gpujpeg_coder_init_image() keeps it as long as the image
fits data_allocated_size (the device buffer). After switching to a layout
with larger data_size that still fits (eg. 4:2:0 -> 4:4:4 after a bigger
image was coded), the CPU Huffman decoder wrote past the buffer and the
host<->device copies exceeded it (encoder with restart interval 0, decoder
with < 32 segments, eg. any JPEG without restart markers).
The preprocessor (kernel selection and component buffer pointers) was
reinitialized only when the input image parameters changed. Changing eg.
only the subsampling for the same input produced a wrong JPEG or even
wrote to the freed device buffers (if they were reallocated).
Reuses one encoder and one decoder for a sequence of layouts (subsampling,
restart interval -> GPU/CPU Huffman coder) and an output format change and
compares the results with freshly created coders.
Compare while the fresh encoder lives and decode the reused encoder's
output, which stays valid until its next encode.
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