Gamescopeでゲームを1920×1080で動かし、3840×2160に拡大してWayland compositorへ表示している。この構成でGamescopeのfilterを明示すると、linearやnearestのような軽いfilterでも画面が完全に黒くなった。
原因はfilterの処理負荷ではなく、Gamescopeが合成後の出力bufferに選ぶAMD DCC modifierと、外側のScroll/wlrootsがそのDMA-BUFをimportできる最大sizeの不一致だった。私の環境では、GamescopeだけにRADV_DEBUG=nodccを適用するとnearestを有効にしたまま表示できた。INFINITASと最小再現用のvkcubeの両方で確認した。
適用条件と回避策
確認した環境は、Radeon 890MのRADV、Mesa 26.2.1、Gamescope 3.16.19系、Vulkan rendererのScroll/wlrootsだ。GamescopeはWayland backendで、内部解像度が1920×1080、出力解像度が3840×2160だった。
回避策の重要な点は、RADV_DEBUG=nodccをGamescopeのprocessだけに適用することだ。Gamescopeから起動するゲーム側では環境変数をunsetする。
RADV_DEBUG=nodcc gamescope ... -- env -u RADV_DEBUG umu-run ...これにより、Gamescopeの最終合成bufferではDCC modifierを使わず、INFINITASを描画するDXVK/RADVでは通常どおりDCCを利用できる。DMA-BUF、DRM modifier、explicit syncは無効化していない。
MesaのRADV_DEBUG=nodccは、RADVがimageに使うDelta Color Compressionを無効にするdebug optionである。子processでunsetしない場合はDXVKにも継承され、ゲーム側のcolor render targetやswapchain imageのDCCまで無効になる。iGPUでは共有メモリ帯域への影響があり得るため、常用設定としてゲームへ継承させる理由はない。
どこで黒くなっていたか
filterを指定しない場合は表示できるが、-F linearや-F nearestを指定すると黒画面になった。filterなしで起動した後にhotkeyでnearestをONにしても黒くなり、OFFに戻すと再び表示された。
INFINITASやWineを実行せずvkcubeをXCB WSIでGamescopeに表示しても同じ現象が再現した。そのため、INFINITAS、Proton、DXVKは再現の必要条件ではない。nearestは計算量の小さいfilterなので、iGPUの性能不足で画面全体が黒くなるという説明も合わなかった。
Gamescope内部のBASE、FULL、SCREEN_BUFFERをscreenshotすると、nearestのときでもvkcubeは正常に描画されていた。一方、Scrollのjournalにはフレームごとに次の失敗が記録されていた。
DMA-BUF is too large to import (3822x2052 > 2560x2560)
Failed to create texture
Failed to upload bufferつまりfilter shaderが黒を出力していたのではない。Gamescope内部では正常なframeが完成していたが、そのbufferをScrollがtextureとしてimportできなかった。
modifierごとの最大extent
Scrollが使っているwlrootsのVulkan rendererは、formatとmodifierごとにvkGetPhysicalDeviceImageFormatProperties2を呼び、返されたmaxExtentより大きいDMA-BUFをimport前に拒否する。そのqueryを再現する小さなVulkan probeを同じRADV環境で実行した。
8-bitのVK_FORMAT_B8G8R8A8_UNORMと10-bitのVK_FORMAT_A2B10G10R10_UNORM_PACK32の結果は同じだった。
| modifier | DCC構成 | plane数 | maxExtent |
|---|---|---|---|
0x02000000104abb04 | DCC、pipe-align、最大block 256B | 2 | 2560×2560 |
0x020000001046bb04 | DCC、pipe-align、最大block 128B | 2 | 2560×2560 |
0x0200000010467b04 | DCC retile、最大block 128B | 3 | 2560×2560 |
0x0200000010437b04 | DCC retile、最大block 64B | 3 | 16384×16384 |
0x0200000010401b04 | DCCなし、64K tiled | 1 | 16384×16384 |
0x0200000010401604 | DCCなし、4K tiled | 1 | 16384×16384 |
0x0200000000000a04 | DCCなし、64K tiled | 1 | 16384×16384 |
0x0000000000000000 | linear | 1 | 16384×16384 |
この結果から、問題は8-bitと10-bitの違いではない。また、DCCが有効なmodifierのすべてが2560に制限されているわけでもない。実際に0x0200000010437b04はDCC有効のまま16384×16384までimportできる。問題は先頭の3種類のDCC構成に限られる。
nodccで変わったこと
同じprobeをRADV_DEBUG=nodccで実行すると、DCC modifierが消え、次の4種類だけが残った。どちらのpixel formatでもすべてmaxExtent=16384x16384だった。
0x0200000010401b04
0x0200000010401604
0x0200000000000a04
LINEARこの状態で最小再現をやり直すと、3840×2160出力と-F nearestを維持したままvkcubeが見えた。Scroll journalにもDMA-BUFのsizeやtexture importのerrorは記録されなかった。さらに、Gamescopeだけをnodccにした同じ構成でINFINITASも表示できた。
これはDMA-BUFを無効化した結果ではない。非DCCのtiled modifierを使い、DMA-BUFとexplicit syncを維持したまま成功している。
効果がなかった設定
次の設定は原因の切り分けには役立ったが、黒画面は改善しなかった。
gamescope_wayland_use_modifiers=false:XB30とDRM_FORMAT_MOD_INVALIDの組み合わせをScrollが拒否した。WLR_RENDER_NO_EXPLICIT_SYNC=1: 変化なし。gamescope_upscale_preemptive=false: 変化なし。- 8-bit出力への変更: modifierの最大extentが10-bitと同じなので根拠にならない。
- OSの再導入とcustom Gamescopeの除去: 新規環境でも再現した。
根本的な不一致
linux-dmabuf feedbackでconsumerからproducerへ広告されるのは、基本的にformatとmodifierの組み合わせである。そのmodifierが特定のusageでimportできる最大extentは含まれない。
そのため、GamescopeはScrollが広告したDCC modifierを出力buffer候補に含められるが、Scroll側のVulkan sampled imageとして3840級のbufferをimportできないことをfeedbackからは知れない。Gamescope側の作成とexportは成功しても、Scroll側の別usageでのimportに失敗する。
現行GamescopeのWayland backendにはmodifier全体のON/OFFはあるが、特定modifierだけを除外する設定は見つからなかった。RADV_DEBUG=nodccはソースを修正せずに使える実用的な回避策だが、16384まで使えるDCC modifierまで除外するため、根本修正より広い。
理想的には、2560制限の3種類だけを出力候補から除外し、0x0200000010437b04のような大きなextentに対応するDCC modifierは残したい。ただし、この精密な制御は現在の設定だけではできていない。Gamescopeの合成出力だけを非DCCにしたときの帯域、frame time、レイテンシ影響もまだ測定していない。