0

編集:レンダラーの分離問題全体に対する優れた解決策を以下に投稿しました。

私は最近、マルチスレッド X11 環境で OpenGL をいじっています。次のチュートリアルを見つけました。これは、コンパイル、リンク、および正常に実行されます。

その後、自分のニーズに合わせてコードを調整しようとした後、奇妙な問題に遭遇しました。

チュートリアルでは、XCreateWindow、glXCreateContext、XSelectInput、および XSetWMProtocols の呼び出し順序は次のとおりです。

param[i].win = XCreateWindow(param[i].d_, root, 200,200, 
                   300,200, 0, visInfo->depth, InputOutput, visInfo->visual,
                   CWColormap,
                   &windowAttr);
param[i].ctx = glXCreateContext(param[i].d_, visInfo,  NULL, True);
XSelectInput(d, param[i].win, StructureNotifyMask);
XSetWMProtocols(d, param[i].win, &(delMsg), 1);

XCreateWindow と XSelectInput/XSetWMProtocols は異なるディスプレイ接続を使用することに注意してください。

ただし、呼び出しの順序を変更すると、

param[i].win = XCreateWindow(param[i].d_, root, 200,200, 
                   300,200, 0, visInfo->depth, InputOutput, visInfo->visual,
                   CWColormap,
                   &windowAttr);
XSelectInput(d, param[i].win, StructureNotifyMask);
XSetWMProtocols(d, param[i].win, &(delMsg), 1);
param[i].ctx = glXCreateContext(param[i].d_, visInfo,  NULL, True);

プログラムは失敗します

失敗した要求の X エラー: BadWindow (無効なウィンドウ パラメータ)
失敗した要求のメジャー オペコード: 2 (X_ChangeWindowAttributes)
失敗した要求のリソース ID: 0x5000002 失敗した要求のシリアル番号: 17 出力ストリームの現在のシリアル番号: 18

XSetWMProtocols が原因のようです。

異なるディスプレイ接続が使用されていたので、そもそも全体が機能しなくても驚かないでしょう。しかし、どういうわけか、glXCreateContext を呼び出した後は、すべてが魔法のようにうまくいっているように見えます。

私は X11/GLX プログラミングに比較的慣れていません。glXCreateContext はどのような魔法を実行しますか? それとも何か他のことが起こったのですか?または、OpenGL とマルチスレッドは常に問題を引き起こすように見えるため、単に先に進む必要があるかもしれません。

私の解決策:

私は怠け者で、チュートリアルのアプローチを使用していました。私のプロジェクトにfreetypeを追加するまではうまくいきましたが、突然BadWindowが再びクラッシュしました。そのため、たとえすべてがうまくいっているように見えても、別のスレッドで作業している場合、X11 は、あなたがいない間にメモリをいじくり回しています。(私ではありませんでした。valgrind で確認しました)

私の現在の解決策は nm がコメントしたとおりです。私はすべてを GUI スレッド (X11 および GL/GLX 呼び出し) に入れました。そのリソースは他のスレッドでは決して利用できません。ただし、レンダリング ループが遅くなる可能性があるため、次の 2 つの点に注意する必要があります。

  • メッセージ処理が遅いとレンダリングが遅れます(以下のilmaleが述べているように)
  • 遅いレンダリングはメッセージ処理を遅らせます (私の懸念事項)

最初の問題は簡単に修正できます。アプリ ロジックに関連する XEvents をキューに入れ、別のスレッドからそれらを取得する stl デキューまたはリスト、または任意のコンテナーを作成します。STL がスレッドセーフであることを確認し、疑わしい場合は独自のキューを実装してください。コンテナーのサイズに待機条件を設定すると、XNextEvent などのブロッキング呼び出しをシミュレートすることもできます。

2 つ目の問題は厄介です。レンダラーが 1 fps 以下の場合、ゲームやアプリケーションは役に立たないと主張するかもしれません。それは本当です。しかし、たとえ 0.1 fps であっても、いくつかの kill シグナル (ウィンドウの破棄アトムなど) を処理できれば素晴らしいことです。私が考えることができる唯一の解決策は、約1000個のスプライトごとにレンダリングした後に新しいメッセージをチェックすることです. それらをコンテナに送信し、レンダリングを続行します。もちろん、その場合、レンダリング スレッドにユーザー スクリプトやその他の未知のコードを実行させることはできません。しかし、それでは、レンダリングを他のスレッドから分離するという考えは無意味になると思います。

お役に立てれば。

:

4

2 に答える 2

1

私は基本的に、クロスプラットフォーム プロジェクトでマルチスレッド化された X11 と Win32 の同じ試行を経験しました。

私が気づいたことの 1 つは、X11 が上記の投稿が示すほどメモリを変更していないことです。確かに、さまざまなコマンドの奇妙な順序がいくつかありますが、正しく理解すると、かなり安定しているように見えます。

特に私がタオルを投げそうになった項目の 1 つは、バックグラウンド GPU 処理でした。この非常に奇妙で、実行時の競合状態をキャッチするのが困難だったので、OS のせいだと思いました。

表示リスト内のカードにテクスチャを送信した後 (freetype を実装する場合も同様)、すぐにリソースを描画すると、後で描画する場合でも、フォント表示リストがわずかに破損することがありました。表示リスト自体が壊れていたので、スレッドが原因ではないことを証明するためだけに、グローバルな OpenGL ロックを実装することにしました。しかし、なぜ破損していたのでしょうか? OS?いいえ、GPU。

GLX コンテキストを共有すると、一部のカード、特にシステムの nvidia で異なる動作が強制されると思います。私の問題を引き起こしたのは他のスレッドではなく、リソースを使用する前の glFinish() の欠如と組み合わされた createContext 呼び出しの共有フラグでした。それと、以下で説明するいくつかのベスト プラクティスについて説明します。

実行の 99% で、マルチスレッドであっても glFinish() がなくても問題なく動作します。ロード時にのみ条件が発生するため、アプリを頻繁に停止/再起動すると、最終的には公開されます。問題なくすべてがロードされた場合、アプリはそれ以降正常に動作します。問題があった場合、画像をリロードするまで画像が破損したままになります。

これらの問題はすべて、これらの単純なルールに従うことで修正されました。

  1. 非 main() スレッドで 2 番目の GLContext を作成します。(同じスレッドで両方のコンテキストを作成して、2 番目のスレッドにポインターを渡さないでください。その方法では安定しません)
  2. 2 番目のスレッドでリソースをロードするときは、使用するキューに結果を配置するにglFinish() を追加します。(簡単に言えば、リソースを使用する前に glFinish() )
  3. 2 番目のスレッド内の 2 番目のコンテキストで makeCurrent() を呼び出したら、getCurrentContext() 関数を呼び出して、それが非 NULL になるのを待ってから、いずれかのスレッドが他の OpenGL リソースのロードまたは呼び出しを実行するようにします。2 番目のスレッドで it(makeCurrent) が返されることがありますが、一部のビデオ カードでは getCurrentContext() がしばらくの間 NULL のままになることがあります。ドライバーがそれを可能にする理由や方法はわかりませんが、チェックによりアプリがクラッシュするのを防ぎます。

これらのプラクティスを 6-Thread+ アプリに実装したところ、奇妙な 1 回限りの破損の問題がなくなり、二度と戻ってこなくなりました。

私の経験からすると、X11 はそれほど意地悪ではないことがわかりました... ビデオ カードはそうですが、何よりもうるさいだけです。私の場合、typedef を使用して、非固有の関数を使用して Linux/Windows コードを記述しているため、さらに複雑になりますが、適切な予防措置を講じれば、これは扱いやすい獣です:)。

風変わりですが、私に言わせれば、「すべてのコストを回避する」問題ではありません。この投稿がお役に立てば幸いです。頑張ってください!

于 2012-09-19T04:51:11.400 に答える
0

私はnmに同意し、チュートリアルを書いたのは私です。:D私が解決しようとした問題は、イベントループをレンダリングループから切り離して、レンダリングに影響を与えずにイベントを再生できるようにすることです。その逆も同様です。LUAフレームワークを作成していましたが、「processMessage(event)」関数がユーザー定義のLua関数を呼び出す可能性がありました。

私がイベントループを書いている間、私はあなたが持っていたような多くの問題を抱えていました、私はFedoraで動作するがUbuntuでクラッシュしたXCBも試しました、多くの頭痛の後に私は異なるディスプレイで解決策を見つけました(Xサーバーの場合共有GlContextとロード用の別のスレッド(テクスチャとメッシュ)を使用して、さまざまなプロセスを提供するようなものです。

問題に戻る:

XSetWMProtocols(...)

ウィンドウが作成された場所と同じ表示が必要ですが、Xの一部のバージョンのみが必要です。そのため、現在Qtを使用しています。

于 2012-07-02T21:58:07.823 に答える