A Nobara 44 field box on 0.27.0 got a working Game Mode takeover — right mode, 1080p120, the perf overlay in the stream — and then every game it launched played sound and took input over a black picture, with no error on either side.
The host log is clean for the whole six minutes the game was up: gamescope came up at 07:55:47, capture attached, frames flowed continuously (NV12 zero-copy into the AV1 Vulkan encoder, ABR climbing 20 → 62 Mbps), and the next warning of any kind is the user switching back to KDE at 08:02:12. The host was streaming faithfully; the black came from inside the session.
Root cause
The log line at 07:55:47.731 says we disabled the distro's Vulkan WSI layer. That was true of the systemd unit and false of everything running in it.
gamescope-session-plus runs an unconditional export ENABLE_GAMESCOPE_WSI=1 near the top of the script, before it launches anything. So #144's --setenv=ENABLE_GAMESCOPE_WSI=0 survived exactly as long as it took the script to start, and gamescope, Steam and every game Steam launched got the layer back. The layer speaks the distro gamescope's gamescope_swapchain protocol; ours rejects its swapchain_feedback and kills the client.
Nothing looked wrong because the casualty is Vulkan clients specifically — Steam's Big Picture UI is not one, so it painted normally and the session looked healthy right up until a game started.
Worth noting for anyone re-verifying this area: #144's original A/B ran vkcube from a shell, which inherits the unit environment where =0 really is set. It never covered a Vulkan client that Steam launched, which is the path that fails.
Fix
Send DISABLE_GAMESCOPE_WSI=1 as well. The Vulkan loader resolves an implicit layer's two manifest knobs in a fixed order (loader_implicit_layer_is_enabled): enable_environment must equal exactly "1" to switch the layer on, and disable_environment is consulted last —
// The disable_environment has priority over everything else. If it is defined, the layer is always disabled.
— where the mere presence of the variable, at any value, forces it off. The session script never mentions that one, so it is the only one of the two that survives it. (Corollary: DISABLE_GAMESCOPE_WSI=0 also disables. It must be absent, not zeroed.)
Both spellings now go out through a single WSI_OFF_ENV + wsi_off_setenv_args() / wsi_off_unit_lines(), so the transient unit (launch_session) and the box's own session drop-in (write_session_plus_dropin) cannot drift apart — the same shape SessionBind already uses for the bind. ENABLE_GAMESCOPE_WSI=0 stays alongside for a layer built without a disable_environment, and because it is what an operator reads the unit for.
Verification
cargo fmt --all --check clean; Linux clippy via scripts/xcheck.sh linux clippy clean.
pf-vdisplay suite run for real in a Linux container: 150 passed, 0 failed, with the new test confirmed executing by name (gamescope::tests::the_wsi_opt_out_carries_the_variable_the_session_script_cannot_clobber).
⚠ Not yet reproduced on hardware. The Nobara testbox .136 and its node .44 are both down. The causal chain is proven from the two sources — the session script's export, and the loader's precedence — and it matches the symptom exactly, but a field confirmation is still owed.
Field workaround, no new build needed
The script sources ~/.config/gamescope-session-plus/sessions.d/$CLIENTafter the export, under set -a:
User-owned, no root, survives package updates. If that fixes the box, it confirms the diagnosis and this PR does the same thing automatically.
Considered and not done
Teaching our gamescope to speak the distro's variant of gamescope_swapchain. Rejected: the variant is unidentified (the obvious candidate — upstream 6a4d150dac added a vk_engine_name string to swapchain_feedback in 2024-12 with no interface version bump — is ruled out, since both 3.16.23.2 and 3.16.25 carry that arg), one wl_interface version is one signature table validated by libwayland before dispatch, and the target is open-ended across distros.
The general fix is to build and ship our own layer beside our own compositor, so the two agree by construction. That composes with this PR — the script's own ENABLE_GAMESCOPE_WSI=1 would enable ours while DISABLE_GAMESCOPE_WSI=1 disables theirs — and is scoped separately, because it wants the Nobara box back for both identification and verification.
Two loose ends left for that follow-up, neither of which this PR needs to touch:
packaging/gamescope/build-punktfunk-gamescope.sh turns the layer build off (-Denable_gamescope_wsi_layer=false) on a comment claiming the layer "is version-independent of the compositor binary". That claim is what made this a shipped bug, and it goes when the layer build comes on.
The script's HDR gate reads ENABLE_GAMESCOPE_WSI, which the script itself set to 1, so it still exports DXVK_HDR=1 while no layer loads. DXVK should fall back to SDR cleanly.
A Nobara 44 field box on 0.27.0 got a working Game Mode takeover — right mode, 1080p120, the perf overlay in the stream — and then **every game it launched played sound and took input over a black picture**, with no error on either side.
The host log is clean for the whole six minutes the game was up: gamescope came up at `07:55:47`, capture attached, frames flowed continuously (NV12 zero-copy into the AV1 Vulkan encoder, ABR climbing 20 → 62 Mbps), and the next warning of any kind is the user switching back to KDE at `08:02:12`. The host was streaming faithfully; the black came from inside the session.
## Root cause
The log line at `07:55:47.731` says we disabled the distro's Vulkan WSI layer. **That was true of the systemd unit and false of everything running in it.**
`gamescope-session-plus` runs an unconditional `export ENABLE_GAMESCOPE_WSI=1` near the top of the script, before it launches anything. So #144's `--setenv=ENABLE_GAMESCOPE_WSI=0` survived exactly as long as it took the script to start, and gamescope, Steam and every game Steam launched got the layer back. The layer speaks the *distro* gamescope's `gamescope_swapchain` protocol; ours rejects its `swapchain_feedback` and kills the client.
Nothing looked wrong because **the casualty is Vulkan clients specifically** — Steam's Big Picture UI is not one, so it painted normally and the session looked healthy right up until a game started.
Worth noting for anyone re-verifying this area: #144's original A/B ran `vkcube` from a shell, which inherits the *unit* environment where `=0` really is set. It never covered a Vulkan client that **Steam** launched, which is the path that fails.
## Fix
Send `DISABLE_GAMESCOPE_WSI=1` as well. The Vulkan loader resolves an implicit layer's two manifest knobs in a fixed order (`loader_implicit_layer_is_enabled`): `enable_environment` must equal exactly `"1"` to switch the layer on, and `disable_environment` is consulted last —
```c
// The disable_environment has priority over everything else. If it is defined, the layer is always disabled.
```
— where the mere **presence** of the variable, at any value, forces it off. The session script never mentions that one, so it is the only one of the two that survives it. (Corollary: `DISABLE_GAMESCOPE_WSI=0` also disables. It must be absent, not zeroed.)
Both spellings now go out through a single `WSI_OFF_ENV` + `wsi_off_setenv_args()` / `wsi_off_unit_lines()`, so the transient unit (`launch_session`) and the box's own session drop-in (`write_session_plus_dropin`) cannot drift apart — the same shape `SessionBind` already uses for the bind. `ENABLE_GAMESCOPE_WSI=0` stays alongside for a layer built without a `disable_environment`, and because it is what an operator reads the unit for.
## Verification
- `cargo fmt --all --check` clean; Linux clippy via `scripts/xcheck.sh linux clippy` clean.
- `pf-vdisplay` suite run for real in a Linux container: **150 passed, 0 failed**, with the new test confirmed executing by name (`gamescope::tests::the_wsi_opt_out_carries_the_variable_the_session_script_cannot_clobber`).
⚠ **Not yet reproduced on hardware.** The Nobara testbox `.136` and its node `.44` are both down. The causal chain is proven from the two sources — the session script's `export`, and the loader's precedence — and it matches the symptom exactly, but a field confirmation is still owed.
## Field workaround, no new build needed
The script sources `~/.config/gamescope-session-plus/sessions.d/$CLIENT` **after** the `export`, under `set -a`:
```sh
mkdir -p ~/.config/gamescope-session-plus/sessions.d
echo 'DISABLE_GAMESCOPE_WSI=1' > ~/.config/gamescope-session-plus/sessions.d/steam
```
User-owned, no root, survives package updates. If that fixes the box, it confirms the diagnosis and this PR does the same thing automatically.
## Considered and not done
Teaching our gamescope to speak the distro's variant of `gamescope_swapchain`. Rejected: the variant is unidentified (the obvious candidate — upstream `6a4d150dac` added a `vk_engine_name` string to `swapchain_feedback` in 2024-12 with no interface version bump — is ruled out, since both `3.16.23.2` and `3.16.25` carry that arg), one `wl_interface` version is one signature table validated by libwayland before dispatch, and the target is open-ended across distros.
The general fix is to build and ship **our own layer** beside our own compositor, so the two agree by construction. That composes with this PR — the script's own `ENABLE_GAMESCOPE_WSI=1` would enable ours while `DISABLE_GAMESCOPE_WSI=1` disables theirs — and is scoped separately, because it wants the Nobara box back for both identification and verification.
Two loose ends left for that follow-up, neither of which this PR needs to touch:
- `packaging/gamescope/build-punktfunk-gamescope.sh` turns the layer build off (`-Denable_gamescope_wsi_layer=false`) on a comment claiming the layer "is version-independent of the compositor binary". **That claim is what made this a shipped bug**, and it goes when the layer build comes on.
- The script's HDR gate reads `ENABLE_GAMESCOPE_WSI`, which the script itself set to `1`, so it still exports `DXVK_HDR=1` while no layer loads. DXVK should fall back to SDR cleanly.
Follow-up to #144.
A Nobara 44 field box on 0.27.0 got a working Game Mode takeover — right mode,
right refresh, perf overlay in the stream — and then every game it launched
played sound and took input over a BLACK PICTURE, with no error anywhere.
#144 disabled the distro's `VkLayer_FROG_gamescope_wsi` with
`--setenv=ENABLE_GAMESCOPE_WSI=0` on the transient unit, because that layer
speaks the distro gamescope's `gamescope_swapchain` protocol and ours rejects
its `swapchain_feedback`, killing the client. `gamescope-session-plus` then
runs an unconditional `export ENABLE_GAMESCOPE_WSI=1` near the top of the
script, before it launches anything — so the opt-out lived exactly as long as
it took the script to start, and gamescope, Steam and every game got the layer
back. The host logged that it had disabled it, which is what made this cost a
field round-trip: the claim was true of the unit and false of everything in it.
Nothing else looked wrong because the casualty is Vulkan clients specifically.
Steam's Big Picture UI is not one, so the session came up looking perfectly
healthy right up until a game started.
Send `DISABLE_GAMESCOPE_WSI=1` as well. The Vulkan loader resolves an implicit
layer's two manifest knobs in a fixed order (`loader_implicit_layer_is_enabled`):
`enable_environment` must equal `"1"` to switch the layer on, and
`disable_environment` is consulted last — "has priority over everything else" —
where the mere PRESENCE of the variable, at any value, forces it off. The
session script never mentions that one, so it is the only one that survives it.
Both spellings now go out through one place, so the transient unit
(`launch_session`) and the box's own session drop-in (`write_session_plus_dropin`)
cannot drift apart — the same shape `SessionBind` already uses for the bind.
`ENABLE_GAMESCOPE_WSI=0` stays alongside for a layer built without a
`disable_environment`, and because it is what an operator reads the unit for.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A Nobara 44 field box on 0.27.0 got a working Game Mode takeover — right mode, 1080p120, the perf overlay in the stream — and then every game it launched played sound and took input over a black picture, with no error on either side.
The host log is clean for the whole six minutes the game was up: gamescope came up at
07:55:47, capture attached, frames flowed continuously (NV12 zero-copy into the AV1 Vulkan encoder, ABR climbing 20 → 62 Mbps), and the next warning of any kind is the user switching back to KDE at08:02:12. The host was streaming faithfully; the black came from inside the session.Root cause
The log line at
07:55:47.731says we disabled the distro's Vulkan WSI layer. That was true of the systemd unit and false of everything running in it.gamescope-session-plusruns an unconditionalexport ENABLE_GAMESCOPE_WSI=1near the top of the script, before it launches anything. So #144's--setenv=ENABLE_GAMESCOPE_WSI=0survived exactly as long as it took the script to start, and gamescope, Steam and every game Steam launched got the layer back. The layer speaks the distro gamescope'sgamescope_swapchainprotocol; ours rejects itsswapchain_feedbackand kills the client.Nothing looked wrong because the casualty is Vulkan clients specifically — Steam's Big Picture UI is not one, so it painted normally and the session looked healthy right up until a game started.
Worth noting for anyone re-verifying this area: #144's original A/B ran
vkcubefrom a shell, which inherits the unit environment where=0really is set. It never covered a Vulkan client that Steam launched, which is the path that fails.Fix
Send
DISABLE_GAMESCOPE_WSI=1as well. The Vulkan loader resolves an implicit layer's two manifest knobs in a fixed order (loader_implicit_layer_is_enabled):enable_environmentmust equal exactly"1"to switch the layer on, anddisable_environmentis consulted last —— where the mere presence of the variable, at any value, forces it off. The session script never mentions that one, so it is the only one of the two that survives it. (Corollary:
DISABLE_GAMESCOPE_WSI=0also disables. It must be absent, not zeroed.)Both spellings now go out through a single
WSI_OFF_ENV+wsi_off_setenv_args()/wsi_off_unit_lines(), so the transient unit (launch_session) and the box's own session drop-in (write_session_plus_dropin) cannot drift apart — the same shapeSessionBindalready uses for the bind.ENABLE_GAMESCOPE_WSI=0stays alongside for a layer built without adisable_environment, and because it is what an operator reads the unit for.Verification
cargo fmt --all --checkclean; Linux clippy viascripts/xcheck.sh linux clippyclean.pf-vdisplaysuite run for real in a Linux container: 150 passed, 0 failed, with the new test confirmed executing by name (gamescope::tests::the_wsi_opt_out_carries_the_variable_the_session_script_cannot_clobber).⚠ Not yet reproduced on hardware. The Nobara testbox
.136and its node.44are both down. The causal chain is proven from the two sources — the session script'sexport, and the loader's precedence — and it matches the symptom exactly, but a field confirmation is still owed.Field workaround, no new build needed
The script sources
~/.config/gamescope-session-plus/sessions.d/$CLIENTafter theexport, underset -a:User-owned, no root, survives package updates. If that fixes the box, it confirms the diagnosis and this PR does the same thing automatically.
Considered and not done
Teaching our gamescope to speak the distro's variant of
gamescope_swapchain. Rejected: the variant is unidentified (the obvious candidate — upstream6a4d150dacadded avk_engine_namestring toswapchain_feedbackin 2024-12 with no interface version bump — is ruled out, since both3.16.23.2and3.16.25carry that arg), onewl_interfaceversion is one signature table validated by libwayland before dispatch, and the target is open-ended across distros.The general fix is to build and ship our own layer beside our own compositor, so the two agree by construction. That composes with this PR — the script's own
ENABLE_GAMESCOPE_WSI=1would enable ours whileDISABLE_GAMESCOPE_WSI=1disables theirs — and is scoped separately, because it wants the Nobara box back for both identification and verification.Two loose ends left for that follow-up, neither of which this PR needs to touch:
packaging/gamescope/build-punktfunk-gamescope.shturns the layer build off (-Denable_gamescope_wsi_layer=false) on a comment claiming the layer "is version-independent of the compositor binary". That claim is what made this a shipped bug, and it goes when the layer build comes on.ENABLE_GAMESCOPE_WSI, which the script itself set to1, so it still exportsDXVK_HDR=1while no layer loads. DXVK should fall back to SDR cleanly.Follow-up to #144.