頂点バッファスチーミングの場合、複数のglBufferSubData VS Orphaning?


8

私は最近OpenGLを学んでいました。ゲームでは、ゲームオブジェクトの位置を頻繁に更新する必要があり、それらは常に画面に出入りします。そのため、レンダリングでは、頂点バッファーも頻繁に更新する必要があります。

OpenGLのコンテキストでは、1つの直感的な方法は、glBufferSubDataを使用して変更されたものを更新することです。

しかし、私はまた、新しいバッファデータを作成して頂点データ全体をそれにアップロードするOrphaningと呼ばれるトリックをオンラインで読んでいます。

また、州の費用とアップロードの費用により、複数のglBufferSubDataの費用も高くなる可能性があります。

これが私の質問です、

  1. どちらの方法が良いですか?
  2. この場合、屋台は本当に重要ですか?
  3. この場合、状態の変更とアップロードのコストは本当に重要ですか?

ありがとう!


実際にマトリックスのみを更新できるのに、なぜジオメトリを再アップロードしたいのですか?つまり、変換のみを変更する必要がある場合は、頂点を再アップロードする必要はありません。
concept3d 2014年

@ concept3dええ、あなたは正しいです!問題の説明を書き間違えました。思い出させてくれてありがとう!私はすでに説明を更新しました。
YiFeng 2014年

回答:


9

これは複雑な問題であり、実際に重要な細部が多数あります。パフォーマンスはプラットフォームとアプリケーションによって異なります。したがって、最適化に投資する前に、ボトルネックの可能性をプロファイルする必要があります。

とはいえ、最初に、たとえばインスタンス化を使用するなど、アップロードと更新をできるだけ減らす必要があると思います。

次に、GPUはバッファの転送とレンダリングを同時に実行できないため、コマンドキュー内のすべてのOpenGLコマンドがデバイスによって順次処理されることに注意してください。データをコピーしたり、GPUで使用できるようにする方法はいくつかあります。

データをGPUにストリーミングするにはさまざまな方法があります

1- glBufferDataまたはglBufferSubDataメソッド

glBufferDataorの使用glBufferSubDataはmemcpyに似ています。ポインタを渡すとDMA操作実行される可能性があります。使用フラグ(GL_STREAM)によっては、メモリがCPUメモリに固定され、GPUへのメモリ転送が実際に発生せずにGPUによって直接使用される可能性があるためです。実装が簡単なので、最初はこれを試してみてください。

2-を使用して内部メモリへのポインタを取得する glMapBuffer

上記が十分に使用できない場合は、glMapBuffer内部メモリへのポインタを取得し、このポインタを使用して直接バッファをいっぱいにすることができます。これは、ファイルデータを直接マップできるため、ファイルの読み取りおよび書き込み操作に適しています。最初に一時バッファにコピーするのではなく、GPUメモリに。バッファ全体をマップしたくない場合glMapBufferRangeは、バッファの一部をマップするために使用できるバッファを使用できます。

1つのトリックは、大きなバッファーを作成し、前半をレンダリングに使用し、後半を更新に使用することです。

3-バッファの孤立化

バッファの孤立化については、nullとglBufferDataおよびそれと同じパラメータを使用してこれを実行できます。ドライバーは、使用されなくなるとメモリブロックを返します。そして、次のglBufferData呼び出しで使用されます(新しいメモリは割り当てられません)。

言及されたすべてのメソッドは、多くの高価な同期を引き起こします。ここでも、GPUはバッファを転送して同時にレンダリングすることができません。

4- Unsynchronized Buffers

GL_MAP_UNSYNCHRONIZED_BITフラグを使用して同期を行わずにバッファーを使用するのが最も高速な(そして最も正確な方法ではない)方法です。glMapBufferRange問題は、同期が行われないことです。そのため、バッファーにデータをアップロードし始めて、すべてを台無しにしてしまう可能性があります。非同期ビットで複数のバッファを使用すると、少し簡単になります。


0

私はそれを別の方法で行います。何か問題があるかもしれません。いくつかの隠された危険なもの。

(私はC#+ OpenTK.GameWindowを使用しています)

オブジェクトをインスタンス化するために、すべてのインスタンスのモデルマトリックスのセットに対して、個別の配列バッファーオブジェクトを使用します。共有された頂点で:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

私のC#コードでは、行列はfloat配列に格納されています float[] mat4_array

次に、配列を配列バッファオブジェクトにバインドします。

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

フレームごとにモデルマトリックスが更新されます。更新するmat4_arrayには、次のように呼び出します。

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

シーンをレンダリングします。を使用してGL.DrawElementsInstanced

追加のOpenGL呼び出しはなく、完全に機能します。


頂点属性としてモデルマトリックスを使用する目的は何ですか?
アントンドゥゼンコ2017

これはインスタンスオブジェクトの描画用です。また、均一変数として渡される配列よりも高速です。
Dennis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.