gamescope's built-in PipeWire node has always been SDR-only: it offers BGRx and NV12, and `paint_pipewire()` hardcodes a Gamma-2.2 composite with the SDR screenshot LUT set. So an HDR game reaches punktfunk already tone-mapped down, and the whole gamescope backend streams 8-bit no matter what the client can decode. Games CAN render HDR on a headless gamescope today — only the capture half is missing. Carry the two patches that close it (packaging/gamescope/patches): the node additionally offers `xRGB_210LE`/`xBGR_210LE` with MANDATORY SMPTE ST.2084 + BT.2020 props, mapped to the same 10-bit capture texture the HDR AVIF screenshot path already allocates, and `paint_pipewire()` composites into them with the HDR screenshot LUTs + `EOTF_PQ` — which is exactly the in-tree `bHDRScreenshot` branch. The new formats are listed LAST, so every existing consumer keeps negotiating the 8-bit stream bit-for-bit. Offered upstream against ValveSoftware/gamescope#2126. Ship it as `punktfunk-gamescope`, beside the distro's own binary rather than replacing it: a Bazzite sysext payload (`build-sysext.sh --gamescope`, which refuses a binary without the marker), an Arch package, a nix derivation + `services.punktfunk.host.gamescopeHdr`, and one distro-agnostic build script the rest call. The second patch stamps `+pfhdr1` into the `--version` banner. That is not cosmetic: punktfunk fixes a session's bit depth in the Welcome, before the display exists and with no way to take it back, so its capability answer has to be a static property of the binary it will spawn.
73 lines
3.4 KiB
Nix
73 lines
3.4 KiB
Nix
# `punktfunk-gamescope` — nixpkgs' gamescope carrying punktfunk's `pipewire-hdr` patches, exposed
|
|
# under its own name so it sits BESIDE the system gamescope instead of replacing it.
|
|
#
|
|
# An override rather than a from-scratch derivation on purpose: gamescope vendors wlroots,
|
|
# vkroots, libliftoff, libdisplay-info, SPIRV-Headers and reshade as git submodules plus two meson
|
|
# wraps, and nixpkgs already solves all of that. What we add is two patches and a rename.
|
|
#
|
|
# `gamescope` in nixpkgs is a wrapper (it wires the WSI layer + capabilities); the buildable
|
|
# derivation is `gamescope.unwrapped` — patching the wrapper would be a no-op, so this asserts on
|
|
# it rather than silently shipping an unpatched binary.
|
|
#
|
|
# Version drift: the patches are applied to whatever gamescope your nixpkgs pins, NOT to the
|
|
# commit `packaging/gamescope/build-punktfunk-gamescope.sh` names. Both hunks sit in code that has
|
|
# been stable across the 3.16 series (`src/pipewire.cpp`'s format builders, `paint_pipewire()` in
|
|
# `src/steamcompmgr.cpp`), so this normally just works — and when it does not, the build fails
|
|
# loudly at `patchPhase` rather than producing a gamescope that quietly cannot do HDR.
|
|
{
|
|
lib,
|
|
gamescope,
|
|
patchDir,
|
|
}:
|
|
let
|
|
unwrapped =
|
|
gamescope.unwrapped or (throw ''
|
|
punktfunk-gamescope needs `gamescope.unwrapped` (the buildable derivation behind nixpkgs'
|
|
gamescope wrapper) and this nixpkgs does not expose it. Update nixpkgs, or build the
|
|
compositor with packaging/gamescope/build-punktfunk-gamescope.sh instead.
|
|
'');
|
|
in
|
|
unwrapped.overrideAttrs (old: {
|
|
pname = "punktfunk-gamescope";
|
|
|
|
patches = (old.patches or [ ]) ++ [
|
|
"${patchDir}/0001-pipewire-offer-10-bit-BT.2020-PQ-capture-formats-HDR.patch"
|
|
"${patchDir}/0002-punktfunk-stamp-the-version-banner-with-pfhdr1.patch"
|
|
];
|
|
|
|
# nixpkgs builds from a `fetchFromGitHub` src, so there is no `.git` for `git describe` and the
|
|
# banner would read `+pfhdr1 (gcc …)` with no version at all — which the host's diagnostic
|
|
# version gate then misreads (it takes the first X.Y.Z triple it finds, i.e. the compiler's).
|
|
# Substituting the real version in keeps `--version` honest AND keeps our marker.
|
|
postPatch = (old.postPatch or "") + ''
|
|
substituteInPlace src/meson.build \
|
|
--replace-fail \
|
|
"vcs_tag = run_command(vcs_tag_cmd, check: false).stdout().strip()" \
|
|
"vcs_tag = '${old.version}'"
|
|
'';
|
|
|
|
# Ship ONLY the compositor, renamed. Everything else nixpkgs installs (gamescopectl,
|
|
# gamescopereaper, gamescopestream, the WSI layer, .desktop files) belongs to the real gamescope
|
|
# package — duplicating it here would put two of each on PATH. The host only execs the
|
|
# compositor.
|
|
postInstall = (old.postInstall or "") + ''
|
|
find $out -mindepth 1 -maxdepth 1 ! -name bin -exec rm -rf {} +
|
|
find $out/bin -mindepth 1 ! -name gamescope -delete
|
|
mv $out/bin/gamescope $out/bin/punktfunk-gamescope
|
|
'';
|
|
|
|
# `gamescope --version` exits non-zero on some builds; the grep is the real assertion.
|
|
doInstallCheck = true;
|
|
installCheckPhase = ''
|
|
runHook preInstallCheck
|
|
$out/bin/punktfunk-gamescope --version 2>&1 | grep -q '+pfhdr' \
|
|
|| { echo "punktfunk-gamescope: the +pfhdr marker is missing — the patches did not take"; exit 1; }
|
|
runHook postInstallCheck
|
|
'';
|
|
|
|
meta = (old.meta or { }) // {
|
|
description = "gamescope with 10-bit BT.2020/PQ PipeWire capture, for punktfunk HDR streaming";
|
|
mainProgram = "punktfunk-gamescope";
|
|
};
|
|
})
|