何かを描画するたびにglEnableとglDisableを呼び出す必要がありますか?
-
03-07-2019 - |
質問
glEnable()
や glEnableClientState()
などのOpenGL関数と、対応する glDisable
を呼び出す頻度はどれくらいですか?アプリケーションの開始時に一度呼び出されることを意図していますか、それとも無効にして、何かを描画するためにすぐに必要な機能のみを有効にする必要がありますか?パフォーマンスの違いはありますか?
解決
"それは依存します"。
アプリ全体で有効化/無効化状態の組み合わせを1つだけ使用する場合は、必ず最初に設定してから行ってください。
ほとんどの実世界のアプリをミックスする必要があり、特定の状態を有効にするために glEnable()
を呼び出し、描画呼び出しを行い、次に glDisable( )
「ステージをクリア」するために完了したら、再びそれらをコードします。
状態の切り替え、状態の追跡、および多くの最適化スキームは、状態の切り替えに時間がかかることがあるため、これに由来します。
他のヒント
状態変数の値を頻繁にチェックし、その後glEnable / glDisableを呼び出すことに気付いた場合は、属性スタック(glPushAttrib / glPopAttrib)を使用して少しクリーンアップできる場合があります。
属性スタックを使用すると、コードの領域を分離し、あるセクションの属性の変更が他のセクションの属性の状態に影響しないようにすることができます。
void drawObject1(){
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
/* Isolated Region 1 */
glPopAttrib();
}
void drawObject2(){
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_FOG);
glEnable(GL_GL_POINT_SMOOTH);
/* Isolated Region 2 */
glPopAttrib();
}
void drawScene(){
drawObject1();
drawObject2();
}
GL_LIGHTING
および GL_DEPTH_TEST
はdrawObject1に設定されますが、それらの状態はdrawObject2に保存されません。 glPushAttribがない場合、これは当てはまりません。また、関数呼び出しの最後にglDisableを呼び出す必要がないことに注意してください。glPopAttribは仕事をします。
パフォーマンスに関しては、glEnable / glDisableへの個々の関数呼び出しによるオーバーヘッドは最小限です。大量の状態を処理する必要がある場合は、おそらく独自の状態マネージャーを作成するか、glGetInteger ...を何度も呼び出して、それに応じて行動する必要があります。追加された機械と制御フローにより、コードの透明性が低下し、デバッグが困難になり、保守が難しくなります。これらの問題により、他のより実り多い最適化が困難になる場合があります。
属性スタックは、抽象化レイヤーを維持し、分離領域を作成するのに役立ちます。
まず、どのOpenGLバージョンを使用しますか?ターゲットグループにはどの世代のグラフィックハードウェアがありますか?これを知っていると、より正確な答えを出すことが容易になります。私の答えはOpenGL 2.1を想定しています。
OpenGLは状態マシンです。つまり、状態が変更されるたびに、その状態が「現在」になります。新しいOpenGL API呼び出しでプログラマーによって明示的に再び変更されるまで。現在の頂点の色を未定義にするクライアント状態配列呼び出しのように、この規則には例外があります。ただし、これらはルールを定義する例外です。
" アプリケーションの開始時に1回"アプリケーションの実行中にOpenGLコンテキストを破棄する必要がある場合があるため、あまり意味がありません。私はあなたがすべてのウィンドウ作成の直後を意味すると仮定します。これは、後で変更する必要のない状態で機能します。例:すべての描画呼び出しが同じ頂点配列データを使用する場合、後でglDisableClientStateでそれらを無効にする必要はありません。
古い固定機能パイプラインには、多くの有効/無効状態が関連付けられています。これの簡単な引き換えは、シェーダーを使用することです!せいぜい5歳までの世代のカードをターゲットにした場合、それはおそらくシェーダーを使用した固定機能パイプラインを模倣しています。シェーダーを使用することにより、変換およびラスタライズの段階で発生することを多かれ少なかれ完全に制御でき、独自の「状態」を作成できます。ユニフォームを使用すると、変更や更新が非常に安くなります。
OpenGLが上で述べたような状態マシンであることを知っていれば、可能な限り状態の変化を最小限に抑えるよう努力すべきであることを明確にすべきです。ただし、ステートコールの有効化/無効化よりもパフォーマンスに大きく影響するものは他にもあります。それらについて知りたい場合は、読んでください。
古い固定機能状態呼び出しに関連付けられた状態の費用は、単純な有効/無効状態ではなく、費用が大幅に異なる場合があります。特に、シェーダーのリンク、および名前のバインド(テクスチャ、プログラム、バッファオブジェクトの「名前」)は、通常かなり高価です。これが、多くのゲームとアプリケーションが、テクスチャに従ってメッシュの描画順序をソートするために使用した理由です。そのようにして、同じテクスチャを2回バインドする必要がなくなりました。しかし、今日では、シェーダープログラムにも同じことが当てはまります。同じシェーダープログラムを2回バインドする必要はありません。また、特定のOpenGLバージョンのすべての機能がすべてのカードでハードウェアアクセラレーションされるわけではありません。それらのカードのベンダーがOpenGLに準拠していると主張している場合でも。準拠しているということは、仕様に従っていることを意味し、必ずしもすべての機能を効率的に実行するということではありません。 GL__ ARB __imagingのglHistogramやglMinMaxのような関数のいくつかは、この点に関して覚えておく必要があります。
結論:明確な理由がない限り、シェーダーを使用してください!代わりにユニフォームを使用できるため、多くの不必要な状態呼び出しからあなたを救います。 OpenGLシェーダーは、ご存知の約6年間使用されています。また、状態変更の有効化/無効化のオーバーヘッドは問題になる可能性がありますが、通常、glUseProgram、glCompileShader、glLinkprogram、glBindBuffer、glBindTextureなどの他のより高価な状態変更を最適化することにより、さらに多くのメリットが得られます。
PS:OpenGL 3.0は、クライアント状態の有効化/無効化呼び出しを削除しました。このバージョンでは、配列の描画が唯一の描画方法であるため、暗黙的に有効になっています。即時モードが削除されました。 gl..Pointer呼び出しも削除されました。glVertexAttribPointerが本当に必要なだけだからです。
私が教わった経験則では、現在の状態を確認して必要な場合にのみ変更するよりも、自由に有効/無効にする方がほとんど常に安価であると言われました。
とはいえ、マークの答えは間違いなく機能するはずです。