OpenGLの機能拡張が分かりにくいワケ

先日、久しぶりにOpenGL Extension Registryを見てたら、glcorearb.hなるファイルがありました。(気づくの遅い)

OpenGLの関数群について

現在のOpenGLの関数群は、大きく分けて、3種類に分かれます。
まずは、Vendor拡張とEXT拡張と呼ばれる関数群があります。これ等は、特定のGPUベンダーや、OS/PCメーカーなどが追加した機能拡張です。EXT拡張は、複数の会社が合意して提出された機能拡張です。大抵のOpenGLの最新機能は、これ等の拡張で定義された関数群で実装されます。
次は、ARB/KHR拡張と呼ばれる機能です。ARBは、OpenGL Architecture Review Boardのことで、OpenGLの機能を決める評議会と言えると思います。ここで承認された機能拡張は、ARB拡張となります。大抵は、まずはじめにVendor拡張や、EXT拡張として実装された機能が、一定の有意性が見出された場合、ARBの承認を得ることで、ARB拡張となることが多いです。KHRはKhronos Groupが主導して導入された機能拡張のようですが、ARBと同一の機能拡張番号を使用しているので、実質的にARB拡張とみなして良いと思います。
最後に、Coreと呼ばれる機能です。これは、OpenGLの標準機能に属します。これは、OpenGLのバージョンが上がるたびに、機能が変わりますが、ひとたび特定のバージョンをサポートしたDriver/GPUがあれば、それは、そのバージョンで定義されている機能を全てサポートしている必要があります。サポートする機能は、バージョンが上がるたびに、基本的には追加という形で増えていきます。大抵は、ARB拡張で定義されている機能拡張が、Coreの機能として取り込まれることが多いです。
しかし、OpenGL3.1/3.2では、機能の削減が行われました。(具体的には、一部のCore機能をARB拡張に移しました。)したがって、後方互換性を保障するものではありません。

OpenGLの機能拡張が分かりにくいワケ

上記のように、とあるひとつの機能が、Coreに取り込まれるまでの過程で、Vendor/EXT/ARB/Coreと複数の定義を持つことが多々あります。
そのため同一の機能が、複数の関数やdefineを持つこととなり、OpenGLの機能の分かりにくさを助長していると思います。
不要な機能拡張の使用を避けるため、OpenGLのプログラムを書く際は、OpenGL Extension Registryにアップされている、最新バージョンのCoreProfileSpecificationに沿って、プログラムを書くのが一番だと思います。そして、Coreに無い機能を使う場合には、まずARB拡張を探し、次にEXT拡張、最後にVendor拡張を探すと良いと思います。

ここでは、ひとつの分かりにくい例として、Occlusion Queryの機能について調べてみたいと思います。
Occlusion Query関連は、

  • GL_HP_occlusion_test
  • GL_NV_occlusion_query
  • GL_ARB_occlusion_query
  • GL_ARB_occlusion_query2
  • OpenGL Core 1.5
  • OpenGL Core 3.3
  • OpenGL Core 4.3

で機能が追加されたり、Vendor->ARB->Coreへの変更などが行われています。下記にその内容を示します。

GL_HP_occlusion_test機能拡張

Occlusionの結果をBool値のみで取得可能。QueryがObject化されていないので、複数のOcclusionTestの結果を得るためには、一旦結果をCPU側で取得しなくてはならない。昔に作られた仕様。

GL_HP_occlusion_test

#ifndef GL_HP_occlusion_test
#define GL_HP_occlusion_test 1
#define GL_OCCLUSION_TEST_HP              0x8165
#define GL_OCCLUSION_TEST_RESULT_HP       0x8166
#endif /* GL_HP_occlusion_test */

使い方
glEnable(GL_OCCLUSION_TEST_HP)
//gl rendering calls
glDisable(GL_OCCLUSION_TEST_HP)
 
glGetBooleanv(GL_OCCLUSION_TEST_RESULT_HP, &result)

GL_NV_occlusion_query機能拡張

Occlusionの結果を描画されたサンプル数で取得する。QueryがObject化されているので、複数のOcclusionQueryの結果を、非同期的に取得することができる。CPU側で、即時に結果が読めるかどうかのチェックも可能。DX9の機能とほぼ同等。

GL_NV_occlusion_query

#ifndef GL_NV_occlusion_query
#define GL_NV_occlusion_query 1
#define GL_PIXEL_COUNTER_BITS_NV          0x8864
#define GL_CURRENT_OCCLUSION_QUERY_ID_NV  0x8865
#define GL_PIXEL_COUNT_NV                 0x8866
#define GL_PIXEL_COUNT_AVAILABLE_NV       0x8867

#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids);
GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids);
GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id);
GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id);
GLAPI void APIENTRY glEndOcclusionQueryNV (void);
GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params);
#endif
#endif /* GL_NV_occlusion_query */

使い方
glBeginOcclusionQueryNV(occlusionQueries[i]);
// render 
glEndOcclusionQueryNV();

// you can check if the result is already available for reading on CPU side.
glGetOcclusionQueryuivNV(occlusionQueries[i], GL_PIXEL_COUNT_AVAILABLE_NV, &isAvailable);
// retrieve the pixel count
glGetOcclusionQueryuivNV(occlusionQueries[i], GL_PIXEL_COUNT_NV, &pixelCount);

GL_ARB_occlusion_query機能拡張

Vendor拡張がARB拡張になった典型的なケース。defineの名称や、関数名がARBに即した形になるが、同じ意味のdefineはNV拡張と同じ値。

GL_ARB_occlusion_query

#ifndef GL_ARB_occlusion_query
#define GL_ARB_occlusion_query 1
#define GL_QUERY_COUNTER_BITS_ARB         0x8864
#define GL_CURRENT_QUERY_ARB              0x8865
#define GL_QUERY_RESULT_ARB               0x8866
#define GL_QUERY_RESULT_AVAILABLE_ARB     0x8867
#define GL_SAMPLES_PASSED_ARB             0x8914

#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids);
GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids);
GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id);
GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id);
GLAPI void APIENTRY glEndQueryARB (GLenum target);
GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params);
#endif
#endif /* GL_ARB_occlusion_query */

GL_ARB_occlusion_query2機能拡張

ここで再びBool値でOcclusionの取得が可能になる。同時期にOpenGL Core3.3に取り込まれたため、本来ARBで定義されるべきdefineが無い。このように、Coreに取り込まれることが前提となるARB拡張では、ARBのpostfixがdefineや関数名についていないことがある。

GL_ARB_occlusion_query2

#ifndef GL_ARB_occlusion_query2
#define GL_ARB_occlusion_query2 1
#endif /* GL_ARB_occlusion_query2 */

GL_ANY_SAMPLES_PASSEDというdefineが新設されているが、Coreのdefineとして定義されている。

結果をBool値で欲しいとき(Bool値で十分なとき)は、GL_ANY_SAMPLES_PASSEDをQueryのBegin/Endで使用し、結果をBool値で受け取る。

OpenGL Core 1.5

GL_ARB_occlusion_queryに相当する機能がCore1.5で追加された。ARBのpostfixが取れている。

OpenGL Core 1.5
#ifndef GL_VERSION_1_5
#define GL_VERSION_1_5 1
#define GL_QUERY_COUNTER_BITS             0x8864
#define GL_CURRENT_QUERY                  0x8865
#define GL_QUERY_RESULT                   0x8866
#define GL_QUERY_RESULT_AVAILABLE         0x8867
#define GL_SAMPLES_PASSED                 0x8914

#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids);
GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids);
GLAPI GLboolean APIENTRY glIsQuery (GLuint id);
GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id);
GLAPI void APIENTRY glEndQuery (GLenum target);
GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params);
#endif
#endif /* GL_VERSION_1_5 */

OpenGL Core 3.3

GL_ARB_occlusion_query2に相当する機能が追加された。必要なdefineが定義されている。

OpenGL Core 3.3

#ifndef GL_VERSION_3_3
#define GL_VERSION_3_3 1
#define GL_ANY_SAMPLES_PASSED             0x8C2F
#endif /* GL_VERSION_3_3 */

OpenGL Core 4.3

MultiSample使用時のQueryのパフォーマンス向上のための機能追加が行われた。前提となる拡張が無く、いきなりCoreに取り込まれた。

OpenGL Core 4.3

#ifndef GL_VERSION_4_3
#define GL_VERSION_4_3 1
#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A
#endif /* GL_VERSION_4_3 */

追加されたdefineは、MutisampleのRenderTargetを使用している時の、Ccclusionテストの高速化を実現するためのもの。ただし、保守的な方向で、実際とは異なる結果を返す可能性がある。(全てのsampleがOccludeされているにも関わらず、されていないと返す可能性があるが、はやくなる(かもしれない))

gl_corearb.hについて

上記で示したとおり、OpenGLの機能拡張で定義された関数やdefineを全て含んだ glext.h というヘッダファイルは、いわば二重定義の嵐のような状況になっています。
OpenGLの機能拡張に精通していれば、それほど迷うことは無いのですが、初めてOpenGLを学んだ方には、まさにカオティックな内容になっています。
また、OpenGL3.2で追加されたCoreProfileとCompatibilityProfileでは、使用可能な機能に違いがあるのですが、同一のヘッダファイルとライブラリファイルを使用するため、コンパイルは通るけど、使ってはいけない関数というものが存在していました。
この状況下で、OpenGLのCoreとARB拡張のみを取り出した、gl_corearb.h を新設したのはすばらしい事だと思います。gl_corearb.hに定義されるARB拡張は、OpenGLのCoreProfileと互換性のあるもののみで、CompatibilityProfileでのみ使用可能なARB拡張は定義されないそうです。詳しくは、OpenGL 4.3 Core Profile SpecificationのAppendix G.2に記載されています。
ちなみに、OcclusionQuery関連について、gl_corearb.hをチェックすると、GL_ARB_occlusion_queryはgl_corearb.hに入っていませんでした。しかし、GL_ARB_occlusion_query2は残っていました。なんだか上記の説明と異なる気もしますが、CoreProfileが定義されるOpenGL3.2以前にCoreに統合されたARB拡張も、削除されていると考えると納得できます。

WindowsでGLを御使用の方へ

Windows上でのOpenGL Extensionを使用する場合は、Perl等を使って、gl_ext.hを書き換えれば簡単に使用できる旨を以前の[記事]で書きましたが、gl_corearb.hも同様に、Perl等を使って加工することで簡単に使えると思います。
ただし、gl_corearb.hには、OpenGL1.0 Coreについて定義があり、ここで定義されている関数は既に存在します。一方その他の関数はwglGetProcAddress()で取得する必要があるので、OpenGL1.0 Coreの定義の部分だけ、気をつけて処理する必要があります。
一応2013/10現在のgl_ext.h, wgl_ext.h, gl_corearb.hをPerlで加工したものをUploadしておきます。
glcorearb_win-zip.jpg(保存して拡張子変えてください)

使い方は、プログラム中で一箇所のみ、関数とポインタの実体を作る必要があるので、以下のように記述します。

#define WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR 1
#define WINDOWS_GL_EXT_CREATE_FUNCTIONPTR 1
#include "glcorearb_win.h"
#include "wglext_win.h"
#undef WINDOWS_GL_EXT_CREATE_FUNCTIONPTR
#undef WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR

それ以外の箇所で外部参照する際は、下記の様に記述します。

#define WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR 1
#include "glcorearb_win.h"
#include "wglext_win.h"
#undef WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR

注:上記ファイルは、余り(殆ど)チェックをしていないので、なにか不具合があるかもしれません。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中