Skip to content

fix(j2k): CPU batch regions of large images and at rounded tile edges - #301

Merged
jcwal1516 merged 3 commits into
mainfrom
fix/cpu-batch-region-windows
Oct 29, 2026
Merged

jcwal1516 merged 3 commits into
mainfrom
fix/cpu-batch-region-windows

Conversation

@jcwal1516

Copy link
Copy Markdown
Member

Fixes #299 and #298, two CpuBatchDecoder failures on region requests (separate commits). First of a three-PR stack; the next two build on this branch.

Region tiles of large images failed with ImageTooLarge (#299)

Prepared region plans select only the region's code-blocks, but their sub-band and IDWT rectangles span whole resolution levels, so the CPU executors sized scratch for whole levels. 1024 px tiles of an 18000 × 12000 RGB image at full and half resolution asked for more than the 512 MiB cap.

  • Preparation keeps the shared whole-level plan, which Metal and CUDA execute unchanged, and adds a CPU copy whose sub-bands and IDWT levels hold only what the region's stores read. The windows come from native region decode's IDWT window math (the same 16/40-sample margins RoiPlan uses), with each sub-band grown to the code-blocks it decodes.
  • The CPU uses the copy only when it at least halves the plan's coefficients, or when the whole-level plan holds 16 Mi coefficients or more. The windowed IDWT is slower per sample than the banded whole-level one, so small-tile regions that cover most of a tile keep whole levels.
  • The copy is built with fallible allocation; a failure is a preparation error, not a panic.

Four 1024 px tiles per level, default options, median ms (docs/benchmark-evidence.md has the command):

Input Level Before After Per-tile CPU
large-ht.j2c (HTJ2K) 0 ImageTooLarge 28 26
large-ht.j2c 1 ImageTooLarge 27 26
large-ht.j2c 2 48 24 22
large.jp2 (classic) 0 ImageTooLarge 152 149
large.jp2 1 ImageTooLarge 244 229
large.jp2 2 346 317 312
large-plt.jp2 (classic) 0 ImageTooLarge 136 133
large-plt.jp2 1 ImageTooLarge 224 220
large-plt.jp2 2 343 310 306

On 240 × 240 slide tiles (CMU-1-JP2K-33005), full requests are unchanged (9.8–10.0 ms) and region requests take 9.1–9.2 ms instead of 8.8–9.0 ms, 0.1 ms of it preparation. Without the half-the-coefficients rule they took 9.5–9.7 ms.

Reduced regions ending at a tile edge failed to prepare (#298)

Tile selection rounds tile bounds outward onto the reduced grid, so it can keep a boundary tile that contributes no samples. The referenced plan builders rejected a tile whose store copies nothing with "failed to decode code-block". They now leave it out, with its payload records, as single-image decode does. This affected the CPU and Metal batches alike.

On the 19 × 13 OpenJPH fixture in 11 × 7 tiles, 4,598 of the 17,290 half-scale regions failed to prepare; all now match single-image decode.

Testing

  • Every region in the issues' reports decodes and matches single-image region decode.
  • New tests: CPU batch region windows vs single-image decode for gray, RGB and RGBA, classic and HT, 5/3 and 9/7, 64 × 64 and 16 × 16 code-blocks, one tile and 128 × 96 tiles, eight regions × {region, half, quarter}; the windowed copy decodes exactly like the whole-level plan and nests its windows; whole-level geometry is kept for every backend; small images and near-full regions keep whole levels; every half-scale region of a tiled fixture prepares and decodes; regions ending at rounded tile edges for every OpenJPH fixture on the CPU, and for the 8-bit unsigned ones on Metal against the CPU.
  • The test oracle now reads signed 8-bit native output one byte per sample instead of as 16-bit pairs.
  • cargo test for j2k-types, j2k-native, j2k, j2k-ml; J2K_REQUIRE_METAL_RUNTIME=1 cargo test -p j2k-metal; cargo check -p j2k-cuda; cargo xtask fmt, clippy, clippy-strict, repo-lint, metal-compile, public-support, panic-surface, unsafe-audit; the quick Metal Clippy targets. CUDA execution was not run here (no CUDA host); its prepared geometry is unchanged, which a test pins.

CpuBatchDecoder region requests of large images failed with
ImageTooLarge: prepared region plans store only the region, but their
sub-bands and IDWT levels span whole resolution levels, so 1024 px tiles
of an 18000 x 12000 image asked for whole level-0 and level-1 planes.

Preparation keeps that whole-level geometry, which Metal and CUDA
execute, and adds a CPU copy whose bands hold only what each store
reads: native region decode's IDWT windows, with each sub-band grown to
the code-blocks it decodes. The copy is built through fallible
allocation and used only when it at least halves the plan's
coefficients or the whole-level plan holds 16 Mi or more, since the
windowed IDWT is slower per sample than the banded one.

Those tiles now decode with output identical to single-image region
decode; quarter-resolution tiles, which already fit, take 24 ms instead
of 48 ms (HTJ2K) and 310-317 ms instead of 343-346 ms (classic).

Fixes #299
Batch preparation failed with "failed to decode code-block" on reduced
regions that end where a tile begins on the reduced grid. Tile
selection rounds tile bounds outward, so it keeps a boundary tile that
contributes no samples, and the referenced plan builders rejected a tile
whose store copies nothing. They now leave such a tile out, with its
payload records, as single-image decode does.

On the 19 x 13 OpenJPH fixture in 11 x 7 tiles, 4,598 of the 17,290
half-scale regions failed to prepare on the CPU and Metal batches; all
now match single-image decode. The test oracle now reads signed 8-bit
native output one byte per sample.

Fixes #298
Record window_referenced_classic_plan_to_stores and
window_referenced_htj2k_plan_to_stores in the j2k-native hidden inventory,
refresh the reviewed diff report, and update the review's hidden count,
fingerprint and rationale.
@jcwal1516
jcwal1516 merged commit 101cd41 into main Oct 29, 2026
14 of 16 checks passed
@jcwal1516
jcwal1516 deleted the fix/cpu-batch-region-windows branch October 29, 2026 12:26
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.

CpuBatchDecoder fails with "image is too large" on 1024 px region tiles of large images

1 participant