LinuxでDisplay HDRを有効にしてゲームをプレイする方法

PS5やWindows PCの様にLinuxHDR対応コンテンツを楽しめるのかですが、結論から言えば、一応可能です。 ただ、そんなにお手軽かというとそうではないし、それなりに制約があります。また、自分の目ではちゃんとなっている様に見えますが本当に想定された色が出てるのかというと良く分からんという感じではあります。

という訳で、多少問題はあるかもしれませんが可能は可能なので、その実現方法について紹介します。

LinuxHDRサポート状況

参考: HDR monitor support - ArchWiki

現状、デスクトップ環境レベルでHDRをサポートしているのはKDE Plasmaがwaylandセッションで実験的にサポートしているだけです。 X.orgにおいてはサポートされる見込みは恐らくありません。

GNOMEデスクトップはサポートに向けて頑張っている最中の様です。

DRMレベルでHDR metadataを扱うことは可能です。一般的なuserspaceのクライアントで利用可能なものはありませんが、特定のソフトウェアで対応しているものがあります。

自分が利用しているHyprlandというwaylandのWMは実装が進められている様です。今後に期待したい。(https://github.com/hyprwm/aquamarine/pull/112)

という訳で、現時点でまともにHDRコンテンツを扱う方法は自分が知ってる限りではgamescopeを利用する方法だけです。

gamescopeについて

gamescopeはValveSoftwareがSteamOS向けに開発したWindow Managerです。 gamescopeは単体のGUIアプリを起動するのに特化したWindow Managerでゲームフレームの描画に最適化されています。そのため、通常のデスクトップ環境で起動するよりもゲームフレームの描画がスムーズになります。

gamescopeにはembeddedモードとnestedモードという二種類のモードがあり、通常のデスクトップ環境で起動するとnestedモードになります。Xwaylandのサンドボックスデスクトップを起動して既存のデスクトップ環境と干渉することを避け、任意の解像度とリフレッシュレートを偽装することができます。

embeddedモードではDRM/KMSを利用してXwaylandを起動し、この上でゲームを起動することで余計なフレームコピーを回避して描画を高速化しています。

このembeddedモードでDRM/KMSを利用する場合において、HDRを有効化できます。

Steamをプレイする場合の実行手順

最初にgamescopeのhelpを紹介しておきます。

 gamescope --help                                                                 
[gamescope] [Info]  console: gamescope version  (gcc 14.2.1)
usage: gamescope [options...] -- [command...]

Options:
  --help                         show help message
  -W, --output-width             output width
  -H, --output-height            output height
  -w, --nested-width             game width
  -h, --nested-height            game height
  -r, --nested-refresh           game refresh rate (frames per second)
  -m, --max-scale                maximum scale factor
  -S, --scaler                   upscaler type (auto, integer, fit, fill, stretch)
  -F, --filter                   upscaler filter (linear, nearest, fsr, nis, pixel)
                                     fsr => AMD FidelityFX™ Super Resolution 1.0
                                     nis => NVIDIA Image Scaling v1.0.3
  --sharpness, --fsr-sharpness   upscaler sharpness from 0 (max) to 20 (min)
  --expose-wayland               support wayland clients using xdg-shell
  -s, --mouse-sensitivity        multiply mouse movement by given decimal number
  --backend                      select rendering backend
                                     auto => autodetect (default)
                                     drm => use DRM backend (standalone display session)
                                     sdl => use SDL backend
                                     headless => use headless backend (no window, no DRM output)
                                     wayland => use Wayland backend
  --cursor                       path to default cursor image
  -R, --ready-fd                 notify FD when ready
  --rt                           Use realtime scheduling
  -T, --stats-path               write statistics to path
  -C, --hide-cursor-delay        hide cursor image after delay
  -e, --steam                    enable Steam integration
  --xwayland-count               create N xwayland servers
  --prefer-vk-device             prefer Vulkan device for compositing (ex: 1002:7300)
  --force-orientation            rotate the internal display (left, right, normal, upsidedown)
  --force-windows-fullscreen     force windows inside of gamescope to be the size of the nested display (fullscreen)
  --cursor-scale-height          if specified, sets a base output height to linearly scale the cursor against.
  --hdr-enabled                  enable HDR output (needs Gamescope WSI layer enabled for support from clients)
                                 If this is not set, and there is a HDR client, it will be tonemapped SDR.
  --sdr-gamut-wideness           Set the 'wideness' of the gamut for SDR comment. 0 - 1.
  --hdr-sdr-content-nits         set the luminance of SDR content in nits. Default: 400 nits.
  --hdr-itm-enable               enable SDR->HDR inverse tone mapping. only works for SDR input.
  --hdr-itm-sdr-nits             set the luminance of SDR content in nits used as the input for the inverse tone mapping process.
                                 Default: 100 nits, Max: 1000 nits
  --hdr-itm-target-nits          set the target luminace of the inverse tone mapping process.
                                 Default: 1000 nits, Max: 10000 nits
  --framerate-limit              Set a simple framerate limit. Used as a divisor of the refresh rate, rounds down eg 60 / 59 -> 60fps, 60 / 25 -> 30fps. Default: 0, disabled.
  --mangoapp                     Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.

Nested mode options:
  -o, --nested-unfocused-refresh game refresh rate when unfocused
  -b, --borderless               make the window borderless
  -f, --fullscreen               make the window fullscreen
  -g, --grab                     grab the keyboard
  --force-grab-cursor            always use relative mouse mode instead of flipping dependent on cursor visibility.
  --display-index                forces gamescope to use a specific display in nested mode.
Embedded mode options:
  -O, --prefer-output            list of connectors in order of preference
  --default-touch-mode           0: hover, 1: left, 2: right, 3: middle, 4: passthrough
  --generate-drm-mode            DRM mode generation algorithm (cvt, fixed)
  --immediate-flips              Enable immediate flips, may result in tearing
  --adaptive-sync                Enable adaptive sync if available (variable rate refresh)

Debug options:
  --disable-layers               disable libliftoff (hardware planes)
  --debug-layers                 debug libliftoff
  --debug-focus                  debug XWM focus
  --synchronous-x11              force X11 connection synchronization
  --debug-hud                    paint HUD with debug info
  --debug-events                 debug X11 events
  --force-composition            disable direct scan-out
  --composite-debug              draw frame markers on alternating corners of the screen when compositing
  --disable-color-management     disable color management
  --disable-xres                 disable XRes for PID lookup
  --hdr-debug-force-support      forces support for HDR, etc even if the display doesn't support it. HDR clients will be outputted as SDR still in that case.
  --hdr-debug-force-output       forces support and output to HDR10 PQ even if the output does not support it (will look very wrong if it doesn't)
  --hdr-debug-heatmap            displays a heatmap-style debug view of HDR luminence across the scene in nits.
Reshade shader options:
  --reshade-effect               sets the name of a reshade shader to use in either /usr/share/gamescope/reshade/Shaders or ~/.local/share/gamescope/reshade/Shaders
  --reshade-technique-idx        sets technique idx to use from the reshade effect

Steam Deck options:
  --mura-map                     Set the mura compensation map to use for the display. Takes in a path to the mura map.

Keyboard shortcuts:
  Super + F                      toggle fullscreen
  Super + N                      toggle nearest neighbour filtering
  Super + U                      toggle FSR upscaling
  Super + Y                      toggle NIS upscaling
  Super + I                      increase FSR sharpness by 1
  Super + O                      decrease FSR sharpness by 1
  Super + S                      take a screenshot
  Super + G                      toggle keyboard grab

オプションの中に--hdr-enabledなどがあることが分かりますが、この辺を利用します。

embeddedモードで起動しなければならないので、まずCTRL+ALT+F2(〜F6)ホットキーを使って、デスクトップ環境が起動していないセッションに切り替えます。どの番号が空いてるかは環境に依りますが、大抵F1かF7でデスクトップが起動していると思うのでF2は空いてるでしょう。

コンソール上で以下のコマンドを使ってsteamを起動します。

ENABLE_GAMESCOPE_WSI=1 ENABLE_HDR_WSI=1 DXVK_HDR=1 gamescope -e --xwayland-count 2 -o "DP-1,*" --hdr-enabled --hdr-itm-enable -- steam -gamepadui

DP-1の部分は自身が接続しているディスプレイのポートに合わせて読み替えてください。

WSI関連の環境変数はgamescopeのHDR対応の有効化とvulkan APIHDR対応の有効化に必要な様です。 DXVK_HDR=1は、ProtonやWineで起動するゲームアプリケーションのHDR対応を有効化するために必要で、VulkanでエミュレートしているDirectXAPIに対してHDR機能の有効化を指示する環境変数です。この環境変数を付けて起動すれば、対応しているゲームならゲーム内の設定でHDRの有効・無効が切り替えられる様になるはずです。

上記のコマンドでsteamが起動すると、この時点でディスプレイのHDRモードが有効になるはずです。

この状態で、steamのゲームを起動し、DXVK_HDR環境変数が正しく渡っていれば、HDR対応のゲームをHDRモードでプレイできるはずです。もしゲーム内まで環境変数が伝播していなければ、steamtinkerlaunchなどの起動ラッパーを使うか、steam上の設定で実行コマンドに環境変数を付与すれば対応できるでしょう。

自分の環境ではサイバーパンク2077とDMC5において、HDRモードでプレイすることができました。

DRMで直接steamを起動していることによる制約

この状態だとディスプレイが1枚しか使えません。ゲームをプレイしつつサブディスプレイでWebブラウザを表示するなどは不可能です。

CTRL+ALT+F1でデスクトップ環境が起動しているセッションに戻ることで通常のデスクトップアプリを操作することはできます。この時、ディスプレイのHDRモードが自動でオフになったりはしないので、やたらビビッドな色で表示されることになりますが、攻略サイトを見たりすることは出来ると思います。

steamを落としてgamescopeプロセスを終了したらディスプレイのHDRモードが解除されるはずです。

HDR動画を試聴する

世の中にそんなにHDR対応の動画コンテンツは無いんですが、mpvとgamescopeを組み合わせることでHDRモードで動画を再生することが出来ます。

mpvのconfigに以下の内容を追加します。

[hdr]
target-colorspace-hint=yes
inverse-tone-mapping=yes
target-prim=bt.2020
target-trc=pq
target-peak=600

大事なのはtarget-colorspace-hintとinverse-tone-mappingです。

設定を追加したら、以下の様にmpvを起動します。

ENABLE_GAMESCOPE_WSI=1 ENABLE_HDR_WSI=1 gamescope -e --xwayland-count 2 -O "DP-1,*" --hdr-enabled -- mpv --profile=hdr <movie file>

これでディスプレイがHDRモードに切り替わった状態でmpvが起動し、tone-mappingによるHDR->SDR変換ではなくHDRの色情報のまま動画が表示できるはずです。

多分、色的にも正しいと思うのですが、そもそも理想的な色状態に確信が無いので、ちょっと何とも言えないところはありますが……。

という訳で、gamescopeを使って頑張れば現状のLinuxでもHDR対応コンテンツを楽しむことは出来ると思います。

この記事が誰かの参考になれば幸いです。