ADR-1209: --gpumask keeps rejecting negative values; the test script uses a positive mask¶
- Status: Proposed
- Date: 2026-09-06
- Deciders: Lusoris
- Tags: cli, testing, upstream-divergence, correctness
Context¶
core/tools/test/test_vmaf_cuda_gpumask.sh is inherited verbatim from upstream and invokes --gpumask -1 twice, commented "gpumask: use cpu". The fork's CLI rejects it:
The script runs under set -e, so test_vmaf_cuda_gpumask fails on any host that actually has an NVIDIA GPU. It reports green in CI only because the script exits 77 (meson SKIP) when nvidia-smi -L finds no device.
-1 only ever "worked" upstream by accident. Upstream's parse_unsigned calls strtoul directly, and POSIX strtoul silently converts "-1" to ULONG_MAX without setting errno; that then truncates to UINT_MAX. The fork deliberately closed that hole (core/tools/cli_parse.cpp::parse_unsigned rejects a leading '-' before calling strtoul, with a comment saying exactly why). So the CLI is behaving as designed and the script is the stale side.
The semantics make a positive mask the correct spelling anyway. gpumask is documented in libvmaf.h as: any non-zero value disables the GPU feature-extractor selection for both the CUDA and SYCL backends (the runtime falls back to the CPU implementation). It is not a per-op bitmask despite the <bitmask> placeholder in the usage string.
Measured on an RTX 4090 over the Netflix 576x324 pair, 4 frames:
| invocation | pooled VMAF |
|---|---|
--gpumask 0 | 88.80022305138327 |
--gpumask 1 | 88.8002154453433 |
--no_cuda --no_sycl | 88.8002154453433 |
--gpumask 1 is byte-identical to an explicit CPU run, which is exactly what the script's -1 was reaching for.
Decision¶
We will keep the CLI's rejection of negative --gpumask values and change the test script to use --gpumask 1, the documented "any non-zero" spelling. The --gpumask entry in docs/usage/cli.md is corrected at the same time: it described a per-op mask, which the option has never been.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Keep strict parsing, fix the script to 1 (chosen) | The CLI keeps failing loudly on input the caller did not mean; the script states its intent directly; matches the documented API semantics | Diverges from upstream's accidental acceptance of -1 | — |
Special-case --gpumask to accept negatives as "all bits set" | Restores upstream-compatible spelling | Reintroduces exactly the silent unsigned wraparound the fork removed on purpose, for one option; CERT INT and the fork's own coding standards forbid the implicit conversion | Rejected |
Relax parse_unsigned globally | One change covers any future case | Would silently accept -1 for --width, --threads, --frame_cnt and every other unsigned option — a much worse footgun | Rejected |
Delete the script's -1 invocations | Trivially green | Loses coverage of the CPU-fallback path, which is the thing the script exists to test | Rejected — never remove a user surface's coverage to make a gate pass |
Consequences¶
- Positive:
test_vmaf_cuda_gpumaskpasses on a GPU host instead of only skipping on a GPU-less one. Verified:rc=0on the RTX 4090 workstation, where it failed before. - Negative: anyone who scripted
--gpumask -1against upstream gets a hard error on this fork. That is pre-existing — the fork has rejected it sinceparse_unsignedwas hardened — and the error message names the constraint.docs/usage/cli.mdnow documents the divergence. - Neutral / follow-ups: the usage string still calls the argument
$bitmask. It is left alone here because changing the help text is a user-visible string change with its own compatibility surface; the reference table indocs/usage/cli.mdcarries the accurate description.
References¶
core/tools/cli_parse.cpp::parse_unsigned— the deliberate negative rejection and its rationale comment.core/include/libvmaf/libvmaf.h— thegpumask"any non-zero" contract.- Upstream ships the same script and the same
strtoul-based parser:libvmaf/tools/test/test_vmaf_cuda_gpumask.sh,libvmaf/tools/cli_parse.c. - Source:
req— user direction to fix the--gpumask -1regression found while running the full suite for ADR-1204 / ADR-1205.