忍者ブログ

素人翻訳

適当に翻訳する。

X11 のフェンスを「trigger」する[Sync拡張]

Compositor, sync fences and redraw lag problems」のまとめ。

フェンスの「trigger」の意味・使い方を確認するための自分用メモである。

----------

再描画の際にラグ(遅れ)が発生する問題について。

X の Sync 拡張と GL_EXT_x11_sync_object を使えば解決できるか。

「コンポジット・マネージャが自身のレンダリング(画像の加工)の際に X サーバと適切に同期しない場合に発生する競合条件」が原因なのか。

----------

コンポジタが DamageNotify イベントを受け取った場合、「その時までにダメージ部分のドローアブルの内容の更新が完了している」と想定することはできない。

コンポジット・マネージャが direct-rendering OpenGL を使用している場合、同コンポジット・マネージャのレンダリングは X サーバのレンダリングとは非同期に行われる。

X サーバは、自身のレンダリングの命令を GPU の命令バッファに書き込んだらすぐに DamageNotify を送信する。
書き込んだ命令が実際に GPU で実行されるまで待ったりしない。

X サーバのレンダリングとコンポジット・マネージャの OpenGL レンダリングとが、メモリを共有している2つの並列 GPU スレッド(thread)で実行されている場合を考える。

X サーバの GPU スレッドがレンダリングの結果を共有メモリに書き込む前にコンポジタの GPU スレッドが共有メモリからの読み込みを開始した場合、コンポジタは古くなったデータを読み込んでしまう。

X Sync 拡張のフェンス(fence)は、上記のような場合に、サーバの書き込み操作が終わるまでコンポジタの読み込み操作が始まらないことを保証するための手段である。

----------

EXT_x11_sync_object は必要なのか。
なぜ NVIDIA のカードだけで問題が起きるのか。

----------

XSyncTriggerFence は、X サーバに対して、同者のレンダリングが完了した時にフェンス(fence)を「trigger」するよう頼むだけのもの。

XSyncAwaitFence は、X サーバに対して、1つあるいはそれ以上のフェンスが「trigger」されるまでクライアントを無視するよう頼むもの。

どちらも OpenGL のレンダリングには影響しない。
なぜかというと、OpenGL のレンダリングは、X サーバが行っている全てのことと非同期に行われているから。

EXT_x11_sync_object を使うと、OpenGL アプリケーションにおいて、 OpenGL に XSync のフェンス(fence)を導入することができる。

これによって、OpenGL アプリケーションは、X11 のフェンスが「trigger」されるまで OpenGL のレンダリングを止めるための手段を得る。(これが無ければ、2つのスレッドは非同期にレンダリングを行う。)

これはコンポジタにとって重要である。
なぜなら、X サーバとコンポジタはウィンドウ・ピクスマップの形でメモリを共有しており、コンポジタはこのウィンドウ・ピクスマップを EXT_texture_from_pixmap 経由で OpenGL に持ち込んでいるからである。

一般に、メモリを共有しながら非同期にレンダリングを行う状況では、何かしらの同期要素が必要である。
XSync のフェンスと GL_EXT_x11_sync_object 拡張とは、EXT_texture_from_pixmap 拡張に欠けている同期の手段を提供するものである。

NVIDIA のドライバで問題が起きやすいのは、同ドライバでは複数のレンダリング・スレッドが非同期に動くことを許しているからである。

他のドライバは、カーネル中のレンダリングを逐次化(serialize)し、全て CPU から受け取った通りの順番で GPU にて実行する。
これこそが、他のドライバで問題が起きない理由である。

----------
「Sync フェンスの輪」について。

フェンスという資源を作成して OpenGL と共有する動作は、高費用である。
そのため、予めフェンスを多めに作っておいて、それを使いまわす。
それがフェンスの輪である。

OpenGL クライアントは、GPU がフェンス・ウェイト(fence wait)命令を消化するのを待ってから、XSyncResetFence リクエストを X サーバへ送信する。
そして、同クライアントは、リセットが完了したこと告げる XSync アラーム(alarm)の受信を待ち、それを受信して初めて同一のフェンスを OpenGL 命令の流れの中に再び投入することができるようになる。

もしフェンスが1つしかなかったら、コンポジタの CPU スレッド、コンポジタの GPU スレッド、X サーバの CPU スレッド、X サーバの GPU スレッド(4つ存在するとすれば)を逐次化して実行しなければならなくなる。

Sync フェンスの輪のおかげで、フェンスを「trigger」すること、消化すること、リセットすることを同時に実行できるようになる。

----------
問題があるコードの例

fence = XSyncCreateFence(...)
XSyncTriggerFence(fence)

/*
* リクエストの処理が再開するまでブロックする
* コードのこの部分に来るまでに、
* フェンスは「trigger」されているはず。
*/
TrivialXRequestWithReply(...)

// GL のレンダリングをここから始める

----------
上記のコードの問題。

XSyncTriggerFence() は、フェンスが「trigger」されるまで待たないものであり、単に「trigger」コマンドを GPU の命令列に書き込むだけである。

TrivialXRequestWithReply() は、X サーバの CPU スレッドがこの関数を処理し終えたらすぐに戻ってしまい、GPU に溜まっている以前のリクエストのレンダリングの完了を待たない。

したがって、GL のレンダリングが始まるまでに GPU がフェンスを「trigger」している保証は無い。

glWaitSync() を使えば、コンポジタに CPU での処理を続けさせながら、GPU に XSync フェンスが「trigger」されるのを待たせることが可能。

----------
依然として問題のあるコードの例

fence = XSyncCreateFence(...)
XSyncTriggerFence(fence)

/*
* 追加の処理。
* このクライアントの X リクエストの処理が止まる
*/
XSyncAwaitFence(fence)

/*
* リクエストの処理が再開するまでブロックする
* コードのこの部分に来るまでに、
* フェンスは「trigger」されているはず。
*/
TrivialXRequestWithReply(...)

// GL のレンダリングをここから始める

----------
上記のコードの問題。

XSyncAwaitFence(fence) は、フェンスが「trigger」されるまでこのクライアントをブロックする。
しかし、X サーバは他のクライアントのリクエストは処理し続ける。
と同時に、コンポジタの CPU スレッドを本来より遅れた状態に留めてしまい、結果として画面の描画にチラつきや遅れが発生する。

XSyncAwaitFence() の代わりに glWaitSync() を用い、CPU スレッドではなく GPU スレッドをブロックするのが良い。

そうすれば、コンポジタは後続の Damage イベントの処理を続けられる。

----------
推測

EXT_x11_sync_object を使っているのは NVIDIA だけ。
最適化のためにコードの行数が多くなっている。

PR

X Nonrectangular Window Shape Extension Protocol(日本語訳)

X Nonrectangular Window Shape Extension Protocolの日本語訳が二色三術のここにある。

X Nonrectangular Window Shape Extension Libraryの日本語訳が二色三術のここにある。

SHAPE拡張のプロトコルでは、input region(入力領域)に関する記述がある。
SHAPE拡張のライブラリの仕様の方では、入力領域については何も書かれていない。

カレンダー

12 2025/01 02
S M T W T F S
1 2 4
5 6 7 8 9 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

最新コメント

[09/07 NONAME]
[08/18 NONAME]
[05/18 NONAME]
[04/09 NONAME]
[03/21 NONAME]

最新記事

(01/10)
(01/03)
(12/20)
(12/08)
(11/20)
(10/30)
(10/24)
(09/20)
(09/16)
(09/11)
(09/03)
(09/02)
(08/27)
(08/17)
(07/31)
(07/30)
(07/19)
(07/13)
(05/02)
(03/17)
(11/01)
(07/20)
(05/17)
(04/20)
(03/10)

ブログ内検索

広告

バーコード

広告