web-dev-qa-db-ja.com

セカンダリスレッドでのOpenGLレンダリング

私は3Dモデルビューアアプリケーションを趣味のプロジェクトとして、またさまざまなレンダリング手法を試すためのテストプラットフォームとして作成しています。ウィンドウ管理とイベントの処理にはSDLを使用し、3DレンダリングにはOpenGLを使用しています。私のプログラムの最初の反復はシングルスレッドであり、十分に実行されました。しかし、シングルスレッドプログラムが原因でシステムが非常に遅く/遅くなっていることに気づきました。私の解決策は、すべてのレンダリングコードを別のスレッドに移動することで、メインスレッドを解放してイベントを処理し、アプリが応答しなくなるのを防ぎました。

このソリューションは断続的に機能し、主にXウィンドウシステムから発生する一連のエラーの変化(そして私の考えでは奇妙な)が原因でプログラムが頻繁にクラッシュしました。これにより、すべてのOpenGL呼び出しがコンテキストが作成されたスレッドで行われている限り、すべてがうまくいくはずであるという私の最初の仮定に疑問を投げかけました。一日の大部分をインターネットで答えを探して過ごした後、私は完全に困惑しています。

より簡潔に:メインスレッド以外のスレッドでOpenGLを使用して3Dレンダリングを実行することは可能ですか?この構成でSDLやGLFWなどのクロスプラットフォームウィンドウライブラリを引き続き使用できますか?私がやろうとしていることをするためのより良い方法はありますか?

これまで、C++を使用してLinux(Ubuntu 11.04)で開発してきましたが、動作するソリューションがある場合はJavaおよびPythonそれらの言語でより良い。

PDATE:要求に応じて、いくつかの説明:

  • 「システムが遅くなる」と言うときは、デスクトップとの対話(ウィンドウのドラッグ、パネルとの対話など)が通常よりもはるかに遅くなることを意味します。アプリケーションのウィンドウの移動には数秒程度の時間がかかり、他の操作は煩わしいほど遅いだけです。
  • コンポジットウィンドウマネージャーとの干渉については... Ubuntu 11.04に同梱されているGNOMEシェルを使用しています(今のところUnityから離れています...)。デスクトップエフェクトを無効にするオプションが見つかりませんでした。以前のディストリビューション。これは、コンポジットウィンドウマネージャーを使用していないことを意味していると思います...非常に間違っている可能性がありますが。
  • 「Xエラー」は、端末で表示されるエラーメッセージによるサーバーエラーだと思います。詳細は以下をご覧ください。

アプリのマルチスレッドバージョンで発生するエラー:

XIO:致命的IOエラー11(リソースは一時的に利用できません)Xサーバー ":0.0"で73リクエスト(73既知の処理済み)後、残り0イベント。

X失敗した要求のエラー:BadColor(無効なカラーマップパラメーター)失敗した要求のメジャーオペコード:79(X_FreeColormap)失敗した要求のリソースID:0x4600001失敗した要求のシリアル番号:72出力ストリームの現在のシリアル番号:73

ゲーム:../../ src/xcb_io.c:140:dequeue_pending_request:アサーション `req == dpy-> xcb-> pending_requests 'が失敗しました。中止

私は常に上記の3つのエラーのいずれかを受け取ります。エラーは明らかにランダムに変化します。これは(私の目には)私の問題が実際にスレッドの使用に起因していることを確認するように見えます。私は進むにつれて学んでいることを覚えておいてください、それで私の無知の中で私が途中でかなり愚かな何かを持っているという非常に良いチャンスがあります。

SOLUTION:同様の問題を抱えている人のために、SDL_Init(SDL_INIT_VIDEO)の呼び出しをレンダリングスレッドに移動し、ミューテックスを使用してコンテキストの初期化をロックすることで問題を解決しました。これにより、コンテキストを使用するスレッドでコンテキストが作成され、初期化タスクが完了する前にメインループが開始されなくなります。起動手順の簡略化された概要:

1)メインスレッドはstructを初期化します。これは、2つのスレッド間で共有され、ミューテックスを含みます。
2)メインスレッドはレンダースレッドを生成し、短時間(1〜5ミリ秒)スリープし、レンダースレッドにミューテックスをロックする時間を与えます。この一時停止の後、ミューテックスをロックしようとしている間、メインスレッドはブロックします。
3)レンダリングスレッドはミューテックスをロックし、SDLのビデオサブシステムを初期化し、OpenGLコンテキストを作成します。
4)レンダリングスレッドはミューテックスのロックを解除し、その「レンダリングループ」に入ります。
5)メインスレッドはブロックされなくなったため、初期化ステップを完了する前にミューテックスをロックおよびロック解除します。

必ず回答とコメントを読んでください。そこには役立つ情報がたくさんあります。

26
rjacks

OpenGLコンテキストが一度に1つのスレッドからのみアクセスされる限り、問題が発生することはありません。あなたは、シングルスレッドプログラムでさえあなたのシステムを遅くしたと言いました。それはシステム全体を意味しますか、それともあなた自身のアプリケーションだけを意味しますか?シングルスレッドのOpenGLプログラムで発生するはずの最悪の事態は、その1つのプログラムのユーザー入力の処理が遅くなるが、システムの他の部分は影響を受けないことです。

いくつかの合成ウィンドウマネージャー(Compiz、KDE4 kwin)を使用している場合は、すべての合成効果を無効にするとどうなるか試してみてください。

Xエラーと言うとき、それはクライアント側のエラー、またはXサーバーログで報告されたエラーを意味しますか? Xサーバーはあらゆる種類の不正な形式のXコマンドストリームに対処でき、多くても警告を発する必要があるため、後者の場合は発生しないはずです。それ(Xサーバー)がクラッシュした場合、これはバグであり、X.orgに報告する必要があります。

プログラムがクラッシュした場合は、Xとの相互作用に問題があります。その場合、そのバリエーションのエラー出力を提供してください。

7
datenwolf

念のため、X-Serverには独自の同期サブシステムがあります。描画中に次のことを試してください:man XInitThreads-初期化用
man XLockDisplay/XUnlockDisplay-描画用(イベント処理については不明)。

3
Vadim Lomovtsev

同様の状況で私がしたことは、OpenGL呼び出しをメインスレッドに保持し、頂点配列の準備を別のスレッド(または複数のスレッド)に移動することでした。

基本的に、CPUを集中的に使用するものをOpenGL呼び出しから分離することができれば、残念ながら疑わしいOpenGLマルチスレッドについて心配する必要はありません。

それは私にとって美しくうまくいきました。

3
Toni Ruža

私はあなたのエラーの1つを得ていました:

../../src/xcb_io.c:140: dequeue_pending_request: Assertion `req == 
    dpy->xcb->pending_requests' failed. Aborted

さまざまなホスト全体も同様です。 SDL_PollEventには、初期化されたメモリを持つポインタが必要であることがわかりました。したがって、これは失敗します。

SDL_Event *event;
SDL_PollEvent(event);

これが機能している間:

SDL_Event event;
SDL_PollEvent(&event);

他の誰かがグーグルからこれに出くわした場合に備えて。

1
Chris
  1. C++、SDL、OpenGl :::
    1. メインスレッド:SDL_CreateWindow();
    2. SDL_CreateSemaphore();
    3. SDL_SemWait();
    4. renderThreadの場合:SDL_CreateThread(run、 "rendererThread"、(void *)this)
    5. SDL_GL_CreateContext()
    6. 「残りのopenGlを初期化して輝きました」
    7. SDL_SemPost()//以前に作成されたセマフォのロックを解除する
    8. 追伸:SDL_CreateThread()は、メソッドではなく、最初のパラメーターとして関数のみを受け取ります。メソッドが必要な場合は、クラス内のメソッド/関数をフレンド関数にしてシミュレートします。このようにして、SDL_CreateThread()のファンクターとして使用できる一方で、メソッド特性があります。
    9. PSS:スレッド用に作成された「run(void * data)」内では、「(void *)」これは重要であり、関数内で「this」を再取得するには、この行が必要です「ClassName * me =(ClassName *)data; "
1

これは半分の答えと半分の質問です。

別のスレッドでSDLでレンダリングすることが可能です。通常、どのOSでも動作します。あなたがする必要があるのは、レンダリングスレッドが引き継ぐときにGLコンテキストを最新にすることを確認することです。同時に、そうする前に、それをから解放する必要があります。メインスレッド、例:

メインスレッドから呼び出されます:

void Renderer::Init()
{
#ifdef _WIN32
    m_CurrentContext = wglGetCurrentContext();
    m_CurrentDC      = wglGetCurrentDC();
    // release current context
    wglMakeCurrent( nullptr, nullptr );
#endif
#ifdef __linux__
    if (!XInitThreads())
    {
        THROW( "XLib is not thread safe." );
    }
    SDL_SysWMinfo wm_info;
    SDL_VERSION( &wm_info.version );
    if ( SDL_GetWMInfo( &wm_info ) ) {
        Display *display = wm_info.info.x11.gfxdisplay;
        m_CurrentContext = glXGetCurrentContext();
        ASSERT( m_CurrentContext, "Error! No current GL context!" );
        glXMakeCurrent( display, None, nullptr );
        XSync( display, false );
    }
#endif
}

レンダリングスレッドから呼び出されます:

void Renderer::InitGL()
{
    // This is important! Our renderer runs its own render thread
    // All
#ifdef _WIN32
    wglMakeCurrent(m_CurrentDC,m_CurrentContext);
#endif
#ifdef __linux__
    SDL_SysWMinfo wm_info;
    SDL_VERSION( &wm_info.version );
    if ( SDL_GetWMInfo( &wm_info ) ) {
        Display *display = wm_info.info.x11.gfxdisplay;
        Window   window  = wm_info.info.x11.window;
        glXMakeCurrent( display, window, m_CurrentContext );
        XSync( display, false );
    }
#endif
    // Init GLEW - we need this to use OGL extensions (e.g. for VBOs)
    GLenum err = glewInit();
    ASSERT( GLEW_OK == err, "Error: %s\n", glewGetErrorString(err) );

ここでのリスクは、残念ながら、SDLにネイティブのMakeCurrent()関数がないことです。そのため、SDLの内部を少し調べなければなりません(1.2、1.3はこれを解決した可能性があります)。

そして、1つの問題が残っています。それは、何らかの理由で、SDLがシャットダウンしているときに問題が発生することです。スレッドが終了したときにコンテキストを安全に解放する方法を誰かに教えてもらえるかもしれません。

1
jschober