The implementation boundary follows the ownership already established by the workspace:
jxr-coreownsDecodeRequest, backend request/result reporting,PreparedPlan, macroblock-majorCoefficientArena, typed output contracts, and checkedSurfaceLayout. Those types are the device-neutral ABI; CUDA adds no parser or codec data model.jxr-nativeowns raw T.832 and Annex-A parsing, entropy decoding, DC/LP prediction, sparse ROI coefficient production, the scalar reconstruction oracle, output-policy validation, and typed host packing. CUDA begins only afterPreparedReconstructionhas obtained these validated coefficients.jxrowns public routing and native batch contracts. Its prepared-image cache shares one coefficient handoff among duplicate inputs; homogeneous groups preserve original indices, per-image metadata, ROI regions, and group-local errors. Explicit CUDA selection is rejected when the feature or an attached session is absent, whileAutocan remain on CPU before a submission exists.jxr-metalestablished the five reconstruction phases, resident/caller-owned outputs, asynchronous completion, reusable allocation and upload caches, and dense batches. The CUDA crate mirrors those responsibility and lifecycle boundaries, while using CUDA contexts, nonblocking streams, events, and device allocations rather than exposing Metal types or duplicating its platform adapter.jxr-math/data/reconstruction.abiis the canonical transform permutation and descriptor schema. Its build script now emits both MSL and CUDA C declarations; Rust layout assertions keep the host descriptors tied to the same source.- Existing tests place scalar math in
jxr-math, syntax/reconstruction/output behavior injxr-native, public routing and batches injxr, and official T.834/T.835 discovery/comparison injxr-test-support. CUDA therefore adds device-free contract/preflight tests beside the adapter and ignored device differential tests beside the existing conformance harness. - Regular CI already exercises formatting, workspace Clippy/tests, minimal features, strict rustdoc, and fuzz-package compilation on Linux and macOS. Metal hardware validation is a serialized self-hosted workflow. CUDA follows that split: dynamic-loading builds stay in regular CI, while compilation by NVRTC, device execution, the 517-case differential suite, and benchmarks are confined to the serialized NVIDIA runner.
- The existing pathology benchmarks separate preparation, submission, synchronization, and readback while checking output checksums. The CUDA benchmark retains that measurement model and adds transfer and device-pool counters across the requested batch/ROI matrix.
The Annex-A writer remains outside every accelerator path. It still accepts an already encoded codestream and packages metadata and optional encoded alpha; no pixel-to-codestream encoder is introduced.
jxr-cuda pins cudarc 0.19.9 with only std, driver, nvrtc,
dynamic-loading, and the CUDA 11.4 ABI baseline enabled. The decision is based
on the following primary project and vendor documentation:
cudarc0.19.9 API documentation documents safe context, stream, event, slice, Driver API, and NVRTC wrappers; its dynamic-loading mode requires no CUDA libraries at build time. The project is MIT OR Apache-2.0 and its release history shows current CUDA 13.x maintenance.cust0.3.2 documentation describes a capable Driver API wrapper, but requires CUDA development libraries on the build system. The upstream Rust-CUDA README also characterizes the rebooted project as early development with expected bugs and safety issues. Its Rust-device toolchain would add nightly/LLVM and build-time CUDA requirements that conflict with this workspace's portable all-feature checks.- The original
RustaCUDA repository is no longer the
maintained direction; its ecosystem moved toward
cust/Rust-CUDA. - NVIDIA documents NVRTC as the supported in-process CUDA C++ to PTX boundary and states that its PTX can be loaded through the Driver API (NVRTC guide). NVIDIA also defines streams and events as the core asynchronous execution primitives (CUDA Programming Guide) and documents stream-ordered allocation and memory-pool capability discovery in the Driver API.
The selected dependency adds no codec, image, neural-network, BLAS, or CUDA
Runtime API layer. Disabling default cudarc features avoids cuBLAS, cuDNN,
cuRAND, and their transitive/runtime surfaces. The lockfile adds cudarc,
ISC-licensed libloading, cfg-if, and the Windows loader shim; every license
is compatible with distribution under this workspace's terms. This keeps
compile time and binary surface substantially smaller than a general GPU
framework.
Maintenance risk remains concentrated in one FFI wrapper and NVIDIA's Driver
API/NVRTC compatibility. The version is pinned so an upstream ABI or safety
change cannot silently alter builds. The dependency's dual license matches the
workspace. Runtime loader failures and missing symbols become explicit session
initialization errors; CPU-only builds and ordinary jxr builds do not link,
load, or probe CUDA.
Enabling the feature embeds CUDA C source and compiles the small Rust/FFI
adapter, but runs neither nvcc nor NVRTC at build time. Creating a session
does incur one NVRTC compile and one Driver API module load for that device;
cloning and reusing the session amortizes that startup cost. Linux and Windows
use the NVIDIA libraries found by the platform loader, while macOS can compile
the Rust feature but reports CUDA unavailable at runtime. Dynamic loading keeps
the toolkit out of CPU deployments, but inherits the operating system's library
search-path trust boundary: production processes must not search user-writable
directories for cuda/nvcuda or nvrtc. Unsafe FFI representation is kept in
the focused backend and cudarc; checked Rust constructors validate buffer
extents, contexts, ABI widths, and kernel indexing metadata before submission.
The CPU remains the parser, entropy decoder, predictor, portable reconstruction
oracle, and automatic fallback. jxr-cuda accepts the same
PreparedReconstruction coefficient arenas used by Metal. It does not parse a
codestream, walk entropy packets, or encode JPEG XR.
An executable CUDA plan validates the coefficient arena, sparse ROI window, component geometry, tile partition, crop, output policy, and every 32-bit device ABI offset before allocation or submission. Five ordered phases implement:
- DC/LP dequantization and the first inverse transform;
- first-level full or subsampled overlap for overlap mode two;
- CPU-selected HP prediction, HP dequantization, and the second transform;
- second-level overlap for modes one and two; and
- chroma reconstruction, color conversion, alpha handling, crop, clipping, and typed or packed output storage.
CUDA C kernels and Rust descriptors are generated from the same canonical ABI
schema used by Metal. Checked 64-bit intermediates set a device status word on
any operation that would exceed the scalar i32 contract. That status is read
only after the completion event; it is never converted into a CPU retry.
Separately encoded alpha retains its own overlap mode and hard/soft tile
partition rather than inheriting the primary codestream's boundary policy.
CudaDecoderSession owns one primary context, four nonblocking streams, a lazily
reused exact-size scratch pool capped at 256 MiB, and an immutable coefficient
upload cache capped at 256 entries/512 MiB. The cache is keyed by coefficient
Arc identity and stream so coefficients stay contiguous and are never repacked.
Submissions retain every upload, descriptor, scratch allocation, status word,
output, and completion event. Dropping a pending submission waits before
recycling resources, preventing device use-after-free.
Single images, caller-owned outputs, independent native batches, and homogeneous dense batches are supported. Dense batches keep all pixels in one device allocation. ROI plans upload only the CPU-produced selected coefficient arena and reconstruct its required overlap/chroma halo. Resident paths perform no host pixel readback; host conversion copies the final native bytes exactly once.
Explicit BackendRequest::Cuda is strict. Auto chooses CPU for an absent or
uncompiled backend, native reduced output, or work below the provisional
threshold, all before CUDA submission. Unsupported CUDA plan combinations and
resource rejections are explicit errors rather than silent semantic changes. A
driver, launch, kernel-arithmetic, synchronization, or transfer failure after
submission is likewise returned to the caller and is never retried on CPU.
The regular CI matrix compiles and lints the CUDA feature on CPU-only Linux and
macOS hosts through dynamic loading. .github/workflows/cuda-hardware.yml is a
serialized, manually dispatched self-hosted NVIDIA workflow. It records the GPU
and toolkit, runs ignored lifecycle/destination tests, differentially compares
all 517 in-scope T.834 cases and ROI boundary variants with CPU, compares the
complete suite with T.835, and retains reports.
The reproducible pathology command is:
JXR_BENCH_ITERATIONS=10 cargo run --release -p jxr-test-support --features cuda --bin jxr-cuda-pathology-bench -- SMALL.jxr LARGE.jxr
It covers full-image and centered ROI paths for batch sizes 1, 8, 32, 64, and 128, reporting preparation, asynchronous submission, synchronization, and device-to-host median latency plus total p95 latency; image throughput; scratch allocation misses; immutable host-to-device bytes; and output transfer bytes. Paths, iteration count, and stream count are printed with the result.
The workflow passed on the self-hosted Cuda runner on 2026-09-05. All 517
in-scope T.834/T.835 cases passed, as did the CPU/ROI differential suite,
lifecycle tests, and checksum-checked benchmark. The
workflow run
records the tested commit and hardware. The benchmark used two small conformance
fixtures, 32×32 and 145×130 pixels. This validates that NVIDIA configuration;
it does not establish performance on other devices, representative pathology
images, or a general speedup over CPU reconstruction.