Say which GPU can do Vulkan Video, and why not when it can't #89

Merged
enricobuehler merged 1 commits from worktree-vulkan-decode-probe into main 2026-08-07 10:50:27 +00:00
Owner

Follow-up to #85, from the first on-glass run on the Intel Arc + NVIDIA laptop (.221): pinning the Vulkan rung on the Arc iGPU silently produced D3D11VA, and there was no way to tell whether the build had even tried. That ambiguity was ours, in three places.

The three defects

1. The "unavailable" log printed three of the five conjuncts. video_ok is an AND of five: Vulkan 1.3, the three features, a decode queue family, both base extensions, at least one codec extension. The log named the first three. A device with 1.3, the features and a decode family — but no codec extension — logged dev_is_13=true features_ok=true decode_family=true next to the word "unavailable", naming nothing anyone could act on. It now prints all five, plus which base extensions are missing, which codec extensions are present, the decode family's own advertised codec operations, and the device name and vendor id.

2. The native-vulkan pin refusal logged video_decode alone. On a device that decodes something but not this codec, that reads as a contradiction: refused, yet video_decode=true. It now carries the caps mask and the codec bit that was wanted — which is the difference between "your GPU can't do this" and "we asked for the wrong thing", and only the second is our bug.

3. On Windows it named the wrong fallback. It said "VAAPI/software". The rung below on Windows is D3D11VA.

New: punktfunk-session --probe-decode

Per-adapter Vulkan Video capability with no session, no surface and no logical device. For each GPU: usable yes/no, the driver's own decode ops, the extensions, and when the answer is no, which conjunct failed, in words.

Separate flag from --list-adapters on purpose — the desktop shells parse that one line-by-line for their GPU picker, so it keeps printing bare names.

The part that is probably the actual field answer

The listing is ordered like pick_device (discrete first) and marks entry 0 as the default presenter, because those two facts together likely explain the report:

  • pick_device ranks DISCRETE_GPU above INTEGRATED_GPU.
  • Vulkan Video decodes on the presenter's device — that is what makes it zero-copy.
  • PUNKTFUNK_DECODER selects the rung, not the device.

So on a hybrid laptop, pinning the decoder while the dGPU is presenting probes the wrong GPU entirely. PUNKTFUNK_VK_DEVICE=<index> is the knob that moves it, and the index the probe prints is that value.

Anti-drift

VIDEO_BASE and VIDEO_CODECS moved to module scope and the five-way AND became video_decode_gate(), called by both the probe and real device creation. A probe holding its own copy of the rule is one that eventually reports a capability the session then refuses — which reads to everyone as a decoder bug rather than a probe bug.

Gates

fmt --check clean; clippy -D warnings over punktfunk-client-session, pf-presenter and pf-client-core in the Linux container, against current main.

No behaviour change to decode selection — this commit only adds a diagnostic flag and makes existing log lines say more.

Follow-up to #85, from the first on-glass run on the Intel Arc + NVIDIA laptop (.221): pinning the Vulkan rung on the Arc iGPU silently produced D3D11VA, and **there was no way to tell whether the build had even tried**. That ambiguity was ours, in three places. ## The three defects **1. The "unavailable" log printed three of the five conjuncts.** `video_ok` is an AND of five: Vulkan 1.3, the three features, a decode queue family, both base extensions, at least one codec extension. The log named the first three. A device with 1.3, the features and a decode family — but no codec extension — logged `dev_is_13=true features_ok=true decode_family=true` next to the word "unavailable", naming nothing anyone could act on. It now prints all five, plus which base extensions are missing, which codec extensions are present, the decode family's own advertised codec operations, and the device name and vendor id. **2. The `native-vulkan` pin refusal logged `video_decode` alone.** On a device that decodes something but not *this* codec, that reads as a contradiction: refused, yet `video_decode=true`. It now carries the caps mask and the codec bit that was wanted — which is the difference between "your GPU can't do this" and "we asked for the wrong thing", and only the second is our bug. **3. On Windows it named the wrong fallback.** It said "VAAPI/software". The rung below on Windows is D3D11VA. ## New: `punktfunk-session --probe-decode` Per-adapter Vulkan Video capability with no session, no surface and no logical device. For each GPU: usable yes/no, the driver's own decode ops, the extensions, and when the answer is no, **which conjunct failed, in words**. Separate flag from `--list-adapters` on purpose — the desktop shells parse that one line-by-line for their GPU picker, so it keeps printing bare names. ## The part that is probably the actual field answer The listing is ordered like `pick_device` (discrete first) and marks entry 0 as the default presenter, because those two facts together likely explain the report: - `pick_device` ranks `DISCRETE_GPU` above `INTEGRATED_GPU`. - Vulkan Video decodes on the **presenter's** device — that is what makes it zero-copy. - `PUNKTFUNK_DECODER` selects the *rung*, not the *device*. So on a hybrid laptop, pinning the decoder while the dGPU is presenting probes the wrong GPU entirely. `PUNKTFUNK_VK_DEVICE=<index>` is the knob that moves it, and the index the probe prints is that value. ## Anti-drift `VIDEO_BASE` and `VIDEO_CODECS` moved to module scope and the five-way AND became `video_decode_gate()`, called by both the probe and real device creation. A probe holding its own copy of the rule is one that eventually reports a capability the session then refuses — which reads to everyone as a decoder bug rather than a probe bug. ## Gates `fmt --check` clean; `clippy -D warnings` over `punktfunk-client-session`, `pf-presenter` and `pf-client-core` in the Linux container, against current main. No behaviour change to decode selection — this commit only adds a diagnostic flag and makes existing log lines say more.
enricobuehler added 1 commit 2026-08-07 10:44:09 +00:00
feat(client): say which GPU can do Vulkan Video, and why not when it can't
ci / bun-nix (pull_request) Successful in 29s
ci / rust-arm64 (pull_request) Successful in 1m44s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m5s
apple / swift (pull_request) Successful in 1m35s
apple / screenshots (pull_request) Skipped
android / android (pull_request) Successful in 3m21s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m1s
ci / rust (pull_request) Successful in 4m27s
ci / docs-site (pull_request) Failing after 16m24s
ci / web (pull_request) Failing after 16m25s
413a3e3e76
Field report from an Intel Arc + NVIDIA laptop: pinning the Vulkan rung on the Arc
iGPU silently produced D3D11VA, and there was no way to tell whether the build had
tried at all. That ambiguity was ours, in three places.

The "unavailable" log printed three of the FIVE conjuncts that gate Vulkan Video.
A device with 1.3, the features and a decode queue family — but no codec extension
— logged dev_is_13=true features_ok=true decode_family=true next to the word
"unavailable" and named nothing actionable. It now prints all five, plus which
base extensions are missing, which codec extensions are present, the decode
family's own advertised codec operations, and the device name and vendor. It also
no longer says "VAAPI/software" on Windows, where the rung below is D3D11VA.

The native-vulkan PIN refusal logged `video_decode` alone. On a device that
decodes something but not THIS codec, that reads as a contradiction: refused, yet
video_decode=true. It now carries the caps mask and the codec bit that was wanted,
so "your GPU can't" is distinguishable from "we asked for the wrong thing" — only
the second is our bug.

And `--probe-decode` is new: per-adapter Vulkan Video capability with no session,
no surface and no logical device. For each GPU it answers usable yes/no, the
driver's own decode ops, the extensions, and — when the answer is no — which
conjunct failed, in words. Separate from --list-adapters, which the desktop shells
parse line-by-line for their GPU picker and which therefore keeps printing bare
names.

The listing is ordered like pick_device (discrete first) and marks entry 0 as the
default presenter, because that ordering is very likely the reporter's actual
answer: pick_device ranks DISCRETE_GPU above INTEGRATED_GPU, Vulkan Video decodes
on the PRESENTER's device by design (that is what makes it zero-copy), and
PUNKTFUNK_DECODER does not move the presenter. So on a hybrid laptop, pinning the
decoder while the dGPU presents probes the wrong GPU entirely —
PUNKTFUNK_VK_DEVICE=<index> is the knob that moves it, and the index printed is
that value.

To keep the probe honest, VIDEO_BASE and VIDEO_CODECS moved to module scope and
the five-way AND became video_decode_gate(), called by both the probe and device
creation. A probe holding its own copy of the rule is one that eventually reports
a capability the session then refuses — which reads to everyone as a decoder bug
rather than a probe bug.

Gates: fmt clean; clippy -D warnings over punktfunk-client-session and
pf-presenter. The Linux container was unavailable (the host's disk filled and took
the docker daemon with it), so this ran on the macOS host target only — the
container leg is owed, and CI covers it on the PR.
enricobuehler merged commit 2830cbe912 into main 2026-08-07 10:50:27 +00:00
enricobuehler deleted branch worktree-vulkan-decode-probe 2026-08-07 10:50:31 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#89