Gamescopeでゲームを1920×1080で動かし、3840×2160に拡大してWayland compositorへ表示している。この構成でGamescopeのfilterを明示すると、linearnearestのような軽い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の結果は同じだった。

modifierDCC構成plane数maxExtent
0x02000000104abb04DCC、pipe-align、最大block 256B22560×2560
0x020000001046bb04DCC、pipe-align、最大block 128B22560×2560
0x0200000010467b04DCC retile、最大block 128B32560×2560
0x0200000010437b04DCC retile、最大block 64B316384×16384
0x0200000010401b04DCCなし、64K tiled116384×16384
0x0200000010401604DCCなし、4K tiled116384×16384
0x0200000000000a04DCCなし、64K tiled116384×16384
0x0000000000000000linear116384×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: XB30DRM_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、レイテンシ影響もまだ測定していない。