feat(gamestream): advertise HDR + surface the game library (with covers) to Moonlight
apple / swift (push) Successful in 1m6s
ci / rust (push) Failing after 1m11s
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 1m2s
android / android (push) Successful in 4m52s
apple / screenshots (push) Successful in 5m20s
windows-host / package (push) Successful in 6m30s
ci / bench (push) Successful in 4m42s
deb / build-publish (push) Successful in 3m19s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m32s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m34s
docker / deploy-docs (push) Successful in 18s
apple / swift (push) Successful in 1m6s
ci / rust (push) Failing after 1m11s
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 1m2s
android / android (push) Successful in 4m52s
apple / screenshots (push) Successful in 5m20s
windows-host / package (push) Successful in 6m30s
ci / bench (push) Successful in 4m42s
deb / build-publish (push) Successful in 3m19s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m32s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m34s
docker / deploy-docs (push) Successful in 18s
Bring the GameStream/Moonlight plane up to the native plane's capability parity. HDR (Windows only): - New host_hdr_capable() gate (Windows + PUNKTFUNK_10BIT, matching the native policy). serverinfo layers SCM_HEVC_MAIN10 onto the probed/static codec mask, so Moonlight finally offers its HDR toggle (live: mask 0x10101 -> 0x10301). - Parse the client's dynamicRangeMode into StreamConfig.hdr and pass it through to OutputFormat::resolve, so a client HDR request proactively enables advanced color on the per-session virtual display (PQ flows even from an SDR desktop). The encoder bit depth now derives from the captured frame format (gs_bit_depth) rather than a hard-coded 8 that mislabeled the already-Main10 HDR stream. Game library in /applist: - The catalog now layers library::all_games() (Steam/Epic/GOG/Xbox/custom) on top of Desktop/apps.json, each with a STABLE GameStream id (FNV-1a, dedup-probed) and the store-qualified library id. Launch routes through the existing security-reviewed launch_title/launch_command via library::launch_gamestream_library — a client can only pick an existing title, never inject a command. - /appasset cover proxy: Moonlight fetches per-app covers from the host, so resolve appid -> library cover URL and proxy the bytes (portrait -> header -> hero -> logo; data: + bounded http(s) fetch), on a blocking thread. IsHdrSupported reflects the host HDR capability. 4:4:4 stays off on GameStream by design: stock Moonlight is 4:2:0 and the Windows IDD-push capturer can't deliver full chroma yet (capturer_supports_444() == false); the gate is documented so it lights up once IDD-push full-chroma capture lands. Validated live (Moonlight -> Windows NVENC host): HDR advertised, the Epic library shows with covers, launch works. clippy clean; apps/serverinfo/library unit tests cover the HDR mask, stable-id, dedup, and data-URL paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,11 +43,33 @@ pub fn serverinfo_xml(host: &Host, https: bool, paired: bool) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// The `<ServerCodecModeSupport>` mask to advertise. On the VAAPI (AMD/Intel) backend it reflects
|
||||
/// what the GPU can ACTUALLY encode (probed — AV1 is narrow, and an old iGPU might lack HEVC), so a
|
||||
/// Moonlight client never negotiates a codec the encoder can't open. NVENC and Windows keep the
|
||||
/// Moonlight-validated static superset.
|
||||
/// The `<ServerCodecModeSupport>` mask to advertise: the SDR baseline ([`base_codec_mode_support`]) plus
|
||||
/// the HEVC Main10 (HDR) bit when the host can actually deliver HDR ([`apply_hdr`] /
|
||||
/// [`crate::gamestream::host_hdr_capable`]). Without the Main10 bit Moonlight never offers its HDR
|
||||
/// toggle; with it, enabling HDR client-side negotiates Main10 and the IDD-push path streams BT.2020 PQ.
|
||||
fn codec_mode_support() -> u32 {
|
||||
apply_hdr(
|
||||
base_codec_mode_support(),
|
||||
crate::gamestream::host_hdr_capable(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Add the HEVC Main10 (HDR) bit to `base` when `hdr` and HEVC is advertised — pure so the
|
||||
/// HDR-layering is unit-testable without a GPU. (HDR streaming uses HEVC Main10; AV1 Main10 is left
|
||||
/// off until the GameStream AV1 path is live-confirmed.)
|
||||
fn apply_hdr(base: u32, hdr: bool) -> u32 {
|
||||
if hdr && base & super::SCM_HEVC != 0 {
|
||||
base | super::SCM_HEVC_MAIN10
|
||||
} else {
|
||||
base
|
||||
}
|
||||
}
|
||||
|
||||
/// The **SDR baseline** mask. On the VAAPI (AMD/Intel) backend it reflects what the GPU can ACTUALLY
|
||||
/// encode (probed — AV1 is narrow, and an old iGPU might lack HEVC), so a Moonlight client never
|
||||
/// negotiates a codec the encoder can't open. NVENC and the GPU-less software path keep the
|
||||
/// Moonlight-validated static superset. HDR (Main10) is layered on by [`codec_mode_support`].
|
||||
fn base_codec_mode_support() -> u32 {
|
||||
#[cfg(target_os = "linux")]
|
||||
if crate::encode::linux_zero_copy_is_vaapi() {
|
||||
if let Some(m) = probed_mask(crate::encode::vaapi_codec_support()) {
|
||||
@@ -108,6 +130,22 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_hdr_adds_main10_only_when_capable_and_hevc() {
|
||||
// HDR-capable + HEVC advertised → Main10 added.
|
||||
assert_eq!(
|
||||
apply_hdr(SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8, true),
|
||||
SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8 | SCM_HEVC_MAIN10
|
||||
);
|
||||
// Not HDR-capable → baseline unchanged (no HDR claim).
|
||||
assert_eq!(
|
||||
apply_hdr(SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8, false),
|
||||
SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8
|
||||
);
|
||||
// HDR-capable but a GPU with no HEVC at all → no Main10 (you can't do Main10 without HEVC).
|
||||
assert_eq!(apply_hdr(SCM_H264, true), SCM_H264);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serverinfo_xml_carries_codec_mask() {
|
||||
let host = Host {
|
||||
|
||||
Reference in New Issue
Block a user