opengl:glFlush()とglFinish()


105

呼び出しの実際的な違いを区別するのに苦労しています glFlush()としglFinish()

ドキュメントはと言うglFlush()glFinish()1の違いは、され、それらはすべて実行されます保証することができますようにOpenGLにバッファリングされたすべての操作をプッシュしますglFlush()リターンがすぐ場合などglFinish()ブロックすべての操作が完了するまで。

定義を読んだ後、それを使用glFlush()すると、OpenGLに実行できるよりも多くの操作をOpenGLに送信するという問題が発生する可能性があると考えました。だから、試してみるために、glFinish()ために、はa glFlush()とloました。見たところ、私のプログラムは(私の知る限り)実行されましたが、まったく同じです。フレームレート、リソース使用量、すべてが同じでした。

したがって、2つの呼び出しの間に大きな違いがあるのか​​、それとも私のコードで2つの呼び出しに違いがないのかと思います。または、一方を他方に対して使用する必要がある場合。また、OpenGLが次のような呼び出しを行うこともわかりましたglIsDone() aのすべてのバッファリングされたコマンドglFlush()が完了したかどうかを確認する(そのため、実行可能な速度よりも速くOpenGLに操作を送信しないでください)が、そのような関数を見つけることができませんでした。

私のコードは典型的なゲームループです:

while (running) {
    process_stuff();
    render_stuff();
}

回答:


93

これらのコマンドはOpenGLの初期の頃から存在していることに注意してください。glFlushは、以前のOpenGLコマンドが有限時間内に完了する必要があることを保証します(OpenGL 2.1仕様、245ページ)。フロントバッファーに直接描画する場合、これにより、OpenGLドライバーが過度の遅延なしに描画を開始することが保証されます。各オブジェクトの後にglFlushを呼び出すと、画面上のオブジェクトの後にオブジェクトが表示される複雑なシーンを考えることができます。ただし、ダブルバッファリングを使用する場合、バッファを交換するまで変更は表示されないため、glFlushは実質的にまったく効果がありません。

glFinish は、以前に発行されたコマンドからのすべての効果が完全に実現されるまで戻りません[...]。つまり、プログラムの実行は、最後のすべてのピクセルが描画され、OpenGLがそれ以上何もするまで、ここで待機します。フロントバッファーに直接レンダリングする場合、glFinishは、オペレーティングシステムの呼び出しを使用してスクリーンショットを撮る前に行う呼び出しです。完了を強制した変更が表示されないため、ダブルバッファリングにはあまり役に立ちません。

したがって、ダブルバッファリングを使用する場合は、おそらくglFlushもglFinishも必要ありません。SwapBuffersは、OpenGL呼び出しを正しいバッファーに暗黙的に送信します。最初にglFlushを呼び出す必要はありません。そして、OpenGLドライバーにストレスをかけることを気にしないでください。glFlushは、あまりにも多くのコマンドを抑制しません。この呼び出しがすぐに戻ることは保証されていません(それが何であれ)。そのため、コマンドの処理に必要な時間がかかる場合があります。


1
glFlushが「FINITE時間で完了する必要がある」とはどういう意味ですか。その後、「コマンドを処理するために必要な時間はかかるため」glFlushはチョークされません。(Caps mine)それらは相互に排他的ではないですか?
Praxiteles、2014年

1
ある時点で終了する限り、FINITE時間基準を満たします。一部のドライバーは、この呼び出しをまったく行いません。
chrisvarnz 2014年

SwapBuffersはコンテキスト固有であり、呼び出しスレッドコンテキストをフラッシュするだけで十分です。複数のコンテキストをレンダリングする場合、別のコンテキストを介してバッファをスワップするときに発生することが保証されていないため、それぞれを手動でフラッシュする必要があります。
Andreas

22

他の回答が示唆しているように、仕様どおりの良い回答は実際にはありません。の一般的な目的glFlush()は、それを呼び出した後、ホストCPUがOpenGL関連の作業を行う必要がないことです。コマンドはグラフィックハードウェアにプッシュされます。の一般的な目的glFinish()は、戻った後、残りの作業が残っていないことであり、結果はすべての適切なOpenGL以外のAPI(たとえば、フレームバッファー、スクリーンショットなどからの読み取り)でも利用できるはずです。それが本当に起こるかどうかは、ドライバーに依存します。仕様では、何が合法かについて寛大な寛容さを認めています。


18

これら2つのコマンドについても私は常に混乱していましたが、この画像はそれを私にすべて明らかにしました: フラッシュと仕上げ いくつかのGPUドライバーは、特定の数のコマンドが蓄積されない限り、発行されたコマンドをハードウェアに送信しないようです。この例では、その数は5です。
この画像は、発行されたさまざまなOpenGLコマンド(A、B、C、D、E ...)を示しています。上の図からわかるように、キューがまだいっぱいでないため、コマンドはまだ発行されていません。

中央にはglFlush()、キューに入れられたコマンドにどのように影響するかが示されています。キューに入れられたすべてのコマンドをハードウェアに送信するようにドライバーに指示します(キューがまだ満杯になっていなくても)。これは呼び出しスレッドをブロックしません。追加のコマンドを送信しない可能性があることをドライバーに通知するだけです。したがって、キューがいっぱいになるのを待つのは時間の無駄です。

下部には、を使用した例がありglFinish()ます。glFlush()すべてのコマンドがハードウェアによって処理されるまで呼び出しスレッドを待機させることを除いて、とほぼ同じです。

本「OpenGLを使用した高度なグラフィックプログラミング」から取得した画像。


私は実際に何glFlushglFinishして何をしているのかを知っていますが、その画像が何を言っているのかわかりません。左側と右側には何がありますか?また、その画像はパブリックドメインでリリースされましたか、それともインターネットに投稿できるいくつかのライセンスの下でリリースされましたか?
Nicol Bolas

もう少し詳しく説明する必要があります。ライセンスについて:私は実際にはよくわかりません。調査中に、GoogleでPDFを見つけました。
タラ

7

パフォーマンスの違いが見られない場合は、何か問題があることを意味します。他の人が述べたように、どちらも呼び出す必要はありませんが、glFinishを呼び出すと、GPUとCPUが実現できる並列処理が自動的に失われます。さらに詳しく説明します。

実際には、ドライバーに送信するすべての作業はバッチ処理され、後で(たとえば、SwapBuffer時に)ハードウェアに送信される可能性があります。

したがって、glFinishを呼び出す場合、本質的にはドライバーにコマンドをGPUにプッシュするよう強制し(それまではバッチ処理され、GPUに処理を要求することはありません)、プッシュされたコマンドが完全に完了するまでCPUをストールします。実行されました。したがって、GPUが動作している間は、CPUは動作しません(少なくともこのスレッドでは)。そして、CPUがその作業(ほとんどの場合、バッチコマンド)を実行している間、GPUは何も実行しません。だから、ええ、glFinishはあなたのパフォーマンスを損なうはずです。(これは概算です。多くのコマンドが既にバッチ処理されている場合、ドライバーはいくつかのコマンドでGPUを動作させる可能性があるためです。ただし、コマンドバッファーは非常に多くのコマンドを保持できるほど大きくなる傾向があるため、一般的ではありません)。

では、なぜglFinishを呼び出すのでしょうか。私が使用したのは、ドライバーのバグがあったときだけでした。実際、ハードウェアに送信したコマンドの1つがGPUをクラッシュさせる場合、どのコマンドが原因であるかを特定するための最も簡単なオプションは、各描画の後にglFinishを呼び出すことです。そうすることで、クラッシュを正確にトリガーするものを絞り込むことができます

補足として、Direct3DのようなAPIは、Finishコンセプトをまったくサポートしていません。


5
「では、なぜそんなにglFinishを呼び出すのだろう」について。あるスレッドでリソース(例:テクスチャ)をロードし、それらを使用して別のスレッドでレンダリングする場合、wglShareListsまたは類似のものを介して共有し、非同期にロードされたものを自由にレンダリングできることをレンダリングスレッドに通知する前にglFinishを呼び出す必要があります。
Marco Mp 2013年

以下で述べたように、これはドライバーのバグの回避策のように見えます。この要件に関する仕様の記述は見つかりません。Windowsではドライバーはロックを取得する必要があり、MacではCGLLockContextを実行する必要があります。しかし、glFinishの使用は非常に間違っているようです。テクスチャが有効であることを知るには、いずれにしても独自のロックまたはイベントを実行する必要があります。
starmole 2013年

1
opencl opengl interop docsは、同期のためにglFinishを推奨します "clEnqueueAcquireGLObjectsを呼び出す前に、アプリケーションはmem_objectsで指定されたオブジェクトにアクセスする保留中のGL操作が完了していることを確認する必要があります。これらのオブジェクトへの保留中の参照があるGLコンテキスト」
マークエッセル

1
glFinishを呼び出す必要がないので、同期オブジェクトを使用できます。
chrisvarnz 2014年

元の回答以来、追加するだけです:GL実装によっては、flush / finishを使用して、異なるスレッドの共有コンテキスト間で同期を開始しました:developer.apple.com/library/ios/documentation/3DDrawing/…-特にApple IOS。基本的には、APIのもう1つの誤用だと思います。しかし、これは私の元の答えに対する警告です。一部の実装では、終了/フラッシュが必要です。しかし、OpenGLの仕様ではなく、実装の方法と理由が定義されています。
starmole 2015

7

glFlushは本当にクライアントサーバーモデルにさかのぼります。すべてのglコマンドをパイプ経由でglサーバーに送信します。そのパイプがバッファリングする可能性があります。他のファイルやネットワークと同様に、I / Oがバッファリングする可能性があります。glFlushは、「まだバッファーがいっぱいになっていなくても、今すぐバッファーを送信してください!」とのみ表示します。ローカルシステムでは、ローカルOpenGL APIがそれ自体をバッファリングすることはほとんどなく、コマンドを直接発行するだけなので、これはほとんど必要ありません。また、実際のレンダリングを引き起こすすべてのコマンドは、暗黙的なフラッシュを行います。

一方、glFinishはパフォーマンス測定用に作成されました。GLサーバーへのPINGの一種。コマンドをラウンドトリップし、サーバーが「アイドルです」と応答するまで待機します。

今日、現代の地元のドライバーは、アイドルであることが何を意味するのかという非常に創造的なアイデアを持っています。それは「すべてのピクセルが描画される」ですか、それとも「コマンドキューにスペースがある」ですか?また、多くの古いプログラムが理由なくコード全体にglFlushとglFinishを散りばめたため、多くの最近のドライバーはブードゥーコーディングを "最適化"として無視しています。本当にそのせいにはできません。

要約すると、古代のリモートSGI OpenGLサーバー用にコーディングしているのでない限り、glFinishとglFlushの両方を実際には操作として扱いません。


4
違う。上記の回答で残したコメントのように、あるスレッドでリソース(例:テクスチャ)をロードして別のスレッドでレンダリングする場合、wglShareListsなどで共有して、レンダリングスレッドに通知する前にglFinishを呼び出す必要があります。非同期でロードされたものを自由にレンダリングできること。そして、それはあなたが言ったようなノーオペレーションではありません。
Marco Mp 2013年

本当に?これを、仕様リンクを持つ共有オブジェクトを使用する前にglFinishを呼び出す必要性をバックアップできますか?これを必要とするバグのあるドライバーを完全に見ることができます。そして、そのようなことは私が言ったことに戻ります。バグのあるドライバーを回避するためにglFinishを使用しています。そのため、適切に機能するドライバーは、パフォーマンスのために無視します。プロファイリング(およびシングルバッファレンダリング)の元の仕様のユースケースは機能しなくなりました。
starmole 2013年

2
破損したテクスチャに対処し なければならない個人的な経験に加えて、higherorderfun.com / blog / 2011/05/26 / …考えてみれば、ミューテックスやその他の同期とそれほど変わらないので、それは理にかなっています。スレッド間のAPI-コンテキストが共有リソースを使用できるようになる前に、他のユーザーがそのリソースのロードを完了する必要があるため、バッファーが空になっていることを確認する必要があります。仕様よりもバグのほうがいいと思いますが、私はこのステートメントの証拠はありません:)
Marco Mp

素晴らしい答え、ありがとう。特に、「多くの古いプログラムは、ブードゥーのように理由もなく、コード全体にglFlushとglFinishをまき散らしました」。
akauppi 2014年

それは本当にopsではありませんか?高輝度画像処理やGPUベースの数学コプロセッシングにとって、glFinishやglFlushは非常に価値のあるものではありませんか?例えば、我々はまでCPUにピクセルバッファからGPU計算の結果を読んでいない後の計算が書かれているすべてのピクセルを確保するためにglFinishを呼び出します。何かが足りませんか?
Praxiteles 2014年

5

見ていこちらを。要するに、それは言う:

glFinish()は、glFlush()と同じ効果がありますが、送信されたすべてのコマンドが実行されるまでglFinish()がブロックする点が異なります。

別の記事では、他の違いについて説明しています。

  • スワップ関数(ダブルバッファアプリケーションで使用)は自動的にコマンドをフラッシュするため、呼び出す必要はありません glFlush
  • glFinish OpenGLが未解決のコマンドを実行するように強制しますが、これは悪い考えです(例:VSyncを使用)

要約すると、これは、ダブルバッファリングを使用するときにこれらの関数を必要としないことを意味します。ただし、swap-buffers実装がコマンドを自動的にフラッシュしない場合を除きます。


はい、そうですが、ドキュメントには、たとえばウィンドウシステムに関する違いがほとんどなく、どちらも同じ効果がある記載されています。しかし、とにかく私は私の答えを編集しました;)
AndiDog

ニュアンスを強調してください。glFlush()およびTHEN glFinish()を呼び出す必要があるかどうかを明確にします-(上記のすべてを読んだ後の質問)
Praxiteles

4

バッファのステータスを問い合わせる方法がないようです。同じ目的を果たすことができるこのApple拡張機能がありますが、クロスプラットフォームではないようです(まだ試していません)。一見するとflush、fenceコマンドをプッシュする前のようです。その後、バッファを移動するときにフェンスのステータスを照会できます。

flushコマンドをバッファリングする前に、呼び出した次のフレームのレンダリングを開始する前にを使用できるかどうか疑問に思いますfinish。これにより、GPUが機能するときに次のフレームの処理を開始できますが、戻ってくるまでに完了しない場合finishは、すべてが新しい状態であることを確認するためにブロックされます。

私はこれを試していませんが、まもなくやってみます。

CPUとGPUをかなり使用している古いアプリケーションで試してみました。(元々使用されていましたfinish。)

flush最後に変更するとfinish最初、差し迫った問題はありませんでした。(すべてが正常に見えました!)プログラムの応答性が向上しました。おそらく、CPUがGPUで待機してストールしていなかったためです。間違いなくより良い方法です。

比較のため、finishedフレームの先頭から削除し、flushたが、同じように動作しました。

したがって、と呼び出したときにバッファが空の場合、パフォーマンスに影響はないため、flushとと言います。とにかくバッファがいっぱいだったら、どうしてもしたいはずです。finishfinishfinish


0

問題は、OpenGLコマンドの実行中にコードの実行を継続するか、OpenGLコマンドの実行後にのみ実行するかです。

これは、ネットワーク遅延などの場合、画像が描画されたにのみ特定のコンソール出力が出力されるなどの問題になることがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.