7

の次のOpenGL仕様をARB_map_buffer_range見つけました。

この拡張機能を使用して非ブロッキングマップ呼び出しを実行できるかどうか疑問に思っていますか?

現在、私のアプリケーションでは、FBOにレンダリングし、ホストPBOバッファーにマップしています。

glMapBuffer(target_, GL_READ_ONLY);  

ただし、これに伴う問題は、データの転送中にレンダリングスレッドがブロックされることです。

レンダリングをパイプライン化することでこの問題を減らすことができますが、私のアプリケーションではレイテンシーが大きな問題です。

私の質問は、map_buffer_rangeをMAP_UNSYNCHRONIZED_BITとともに使用して、別のスレッドでマップ操作が終了するのを待つか、レンダリングスレッドが次のフレームをレンダリングする間、同じスレッドでマップ操作を延期できるかどうかです。

例えば

thread 1:

map();
render_next_frame();

thread 2:

wait_for_map

また

thread 1:

map();
while(!is_map_ready())
   do_some_rendering_for_next_frame();

私が確信していないのは、マップ操作の準備ができたことをどのように知るかです。仕様には、「正しい操作を保証するための他の同期手法」のみが記載されています。

何か案は?

4

2 に答える 2

7

でバッファをマップする場合GL_MAP_UNSYNCHRONIZED_BIT、ドライバはOpenGLがそのメモリで完了するまで待機せずに、マップします。したがって、多かれ少なかれすぐにアクセスできるようになります。

問題は、これはあなたがそのメモリを気ままに読み書きできるという意味ではないということです。OpenGLがそのバッファからの読み取りまたはそのバッファへの書き込みを行っていて、それを変更した場合...未定義の動作へようこそ。これにはクラッシュが含まれる可能性があります。

したがって、非同期マッピングを実際に使用するには、動作をOpenGLによるそのバッファへのアクセスに同期させる必要があります。これには、ARB_syncオブジェクト(または、NVIDIAのみを使用していて、最近ドライバーを更新していない場合はNV_fence)の使用が含まれます。

そうは言っても、バッファへのアクセスを同期するためにフェンスオブジェクトを使用している場合は、実際にはまったく必要ありませんGL_MAP_UNSYNCHRONIZED_BIT。フェンスを終了するか、完了を検出すると、バッファーを正常にマップでき、すぐに完了するはずです(他の操作でも読み取り/書き込みが行われている場合を除く)。

一般に、非同期アクセスは、バッファへのきめ細かい書き込みアクセスが必要な場合に最適です。この場合、同期オブジェクトを適切に使用すると、本当に必要なもの(マップ操作がいつ終了したかを知る機能)が得られます。


補遺:上記は現在古くなっています(ハードウェアによって異なります)。OpenGL 4.4 / ARB_buffer_storageのおかげで、非同期でマップできるだけでなく、バ​​ッファーを無期限にマップしたままにすることができます。はい、使用中にバッファをマップすることができます。

これは、不変のストレージを作成し、そのストレージに(とりわけ)を提供することによって行われGL_MAP_PERSISTENT_BITます。次にglMapBufferRange、同じビットを提供します。

技術的には、それはほとんど何も変わりません。アクションをOpenGLと同期する必要があります。バッファの領域に何かを書き込む場合は、バリアを発行する、バッファのその領域を明示的にフラッシュする必要があります。また、読んでいる場合でも、フェンス同期オブジェクトを使用して、データを読み取る前にデータが実際に存在することを確認する必要があります(また、使用しない限りGL_MAP_COHERENT_BIT、読み取る前にバリアを発行する必要があります)。

于 2011-08-06T22:19:18.130 に答える
6

一般に、「ノンブロッキング マップ」を行うことはできませんが、ブロッキングなしでマッピングすることはできます。

「ノンブロッキング マップ」が存在しない理由は、関数呼び出しが返された瞬間にデータにアクセスできるため、ドライバーは確実にそこにあることを確認する必要があるためです。データが転送されていない場合、ドライバーはブロックする以外に何ができますか。
スレッドによってこれが改善されるわけではなく、悪化する可能性もあります (同期とコンテキスト共有の問題が追加されます)。スレッドは、魔法のようにデータ転送の必要性をなくすことはできません。

そして、これはマッピングをブロックしない方法につながります。転送が完了したことが確実な場合にのみマッピングします。glFinishこれを行う安全な方法の 1 つは、バッファーを反転した後、またはクエリ/フェンス オブジェクトを待機した後または後にバッファーをマップすることです。バッファーがスワップされるまで待てない場合は、フェンスを使用することをお勧めします。フェンスはパイプラインを停止しませんが、転送が完了したかどうかを通知します (停止glFinishする場合としない場合がありますが、おそらく停止する可能性があります)。バッファーを交換した後の読み取りも 100% 安全ですが、同じフレーム内のデータが必要な場合は受け入れられない場合があります (ただし、スクリーンショットやトーンマッピングのヒストグラムの計算には完全に機能します)。

安全性の低い方法は、「その他のもの」を挿入し、その間に転送が完了することを期待することです.


以下のコメントに関して:
この回答は正しくありません。データが利用可能になった後にデータにアクセスする以上のことはできません (これは明らかなはずです)。つまり、いずれかの方法で同期/ブロックする必要があり、選択の余地はありません。
非常にペダンティックな観点からは、もちろん を使用GL_MAP_UNSYNCHRONIZED_BITしてノンブロッキング マップ操作を取得できますが、上記のように暗黙的な同期を明示的に再現しない限り機能しないため、これはまったく関係ありません。安全にアクセスできないマッピングは何の役にも立ちません。

OpenGL が (暗黙的または明示的に) 同期/ブロックせずにデータを転送しているバッファーをマッピングおよびアクセスすることは、「未定義の動作」を意味します。
一方、明示的に同期する場合 (たとえば、上記のフェンスを使用) は、非同期フラグを使用するかどうかは関係ありません。これ以上暗黙の同期を行う必要がないためです。

于 2011-08-06T14:53:16.533 に答える