このページの内容

コンテキストガイド

このガイドでは、OpenGL および OpenGL ES コンテキストに関連する GLFW の関数を紹介します。この分類の特定の関数について詳しくは、contextを参照してください。GLFW API のほかの領域についてもガイドがあります。

コンテキストオブジェクト

ウィンドウオブジェクトは、トップレベルウィンドウと OpenGL または OpenGL ES コンテキストの両方を内包します。glfwCreateWindow で作成し、glfwDestroyWindow または glfwTerminate で破棄します。詳しくは window_creationを参照してください。

ウィンドウとコンテキストは分離できない形で結び付いているため、ウィンドウオブジェクトはコンテキストのハンドルとしても機能します。

さまざまな種類のコンテキストの作成を試し、そのプロパティを確認するには、glfwinfo テストプログラムを実行してください。

注: Vulkan にはコンテキストがなく、Vulkan インスタンスは Vulkan API 自体を介して作成します。Vulkan を使ってウィンドウへ描画する場合は、GLFW_CLIENT_API ヒントを GLFW_NO_API に設定し、コンテキストの作成を無効にしてください。詳しくは vulkan_guideを参照してください。

コンテキスト作成ヒント

glfwWindowHint で指定するヒントには、作成するコンテキストの種類に関係するものがいくつかあります。ウィンドウガイドのコンテキスト関連ヒントを参照してください。

コンテキストオブジェクトの共有

glfwCreateWindow でウィンドウとその OpenGL または OpenGL ES コンテキストを作成するとき、新しいコンテキストがオブジェクト(テクスチャ、頂点バッファ、要素バッファなど)を共有する相手として、別のウィンドウを指定できます。

GLFWwindow* second_window = glfwCreateWindow(640, 480, "Second Window", NULL, first_window);

オブジェクト共有はオペレーティングシステムとグラフィックスドライバーが実装します。共有するオブジェクトの種類を選べるプラットフォームでは、GLFW はすべての種類の共有を要求します。

詳しくは OpenGL または OpenGL ES のリファレンス文書の該当する章を参照してください。残念ながら、この章の名前と番号はバージョンや API によって異なりますが、Shared Objects and Multiple Contexts という名前だったことがあります。

GLFW には、sharing という最小限のオブジェクト共有サンプルプログラムが付属します。

オフスクリーンコンテキスト

GLFW は、関連付けられたウィンドウを持たないコンテキストの作成をサポートしません。ただし、GLFW_VISIBLE ウィンドウヒントを使うと、非表示ウィンドウを持つコンテキストを作成できます。

glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);

GLFWwindow* offscreen_context = glfwCreateWindow(640, 480, "", NULL, NULL);

ウィンドウを表示する必要はなく、そのコンテキストを通常のオフスクリーンコンテキストとして使えます。ウィンドウマネージャーによっては、非表示ウィンドウのフレームバッファのサイズが使用不能または変更不能なことがあります。そのため、このようなコンテキストでの描画にはフレームバッファオブジェクトを推奨します。

表示されているものがなくても、ウィンドウが少なくとも1つある間はイベントを処理するべきです。

コンテキストのないウィンドウ

GLFW_CLIENT_API ヒントを GLFW_NO_API に設定すると、コンテキストの作成を無効にできます。

コンテキストのないウィンドウを glfwMakeContextCurrent または glfwSwapBuffers に渡してはいけません。渡すと GLFW_NO_WINDOW_CONTEXT エラーが発生します。

カレントコンテキスト

OpenGL または OpenGL ES を呼び出す前に、正しい種類のカレントコンテキストが必要です。1つのコンテキストを同時にカレントにできるのは1つのスレッドだけであり、1つのスレッドで同時にカレントにできるコンテキストも1つだけです。

コンテキストをスレッド間で移動するときは、新しいスレッドでカレントにする前に、元のスレッドで非カレントにしなければなりません。

ウィンドウのコンテキストは glfwMakeContextCurrent でカレントにします。

glfwMakeContextCurrent(window);

カレントコンテキストのウィンドウは glfwGetCurrentContext が返します。

GLFWwindow* window = glfwGetCurrentContext();

次の GLFW 関数は、コンテキストがカレントであることを必要とします。カレントコンテキストなしでこれらの関数を呼び出すと、GLFW_NO_CURRENT_CONTEXT エラーが発生します。

バッファ交換

ウィンドウガイドの buffer_swapを参照してください。

OpenGLとOpenGL ESの拡張

OpenGL と OpenGL ES の利点の1つは拡張性です。ハードウェアベンダーは、ある機能が OpenGL または OpenGL ES 仕様の新しいバージョンへ取り込まれる前に、その API を拡張する機能を実装へ含められます。また、一部の拡張は仕様へ取り込まれず、廃れるまで拡張のままです。

拡張は次の要素で定義されます。

  • 拡張名(例: GL_ARB_gl_spirv
  • 新しい OpenGL トークン(例: GL_SPIR_V_BINARY_ARB
  • 新しい OpenGL 関数(例: glSpecializeShaderARB

公式拡張に使われる Architecture Review Board の略である ARB 接辞に注目してください。上記の拡張は ARB が作成しましたが、Nvidia の NV、AMD の AMD など、多くの異なる接辞があります。どの団体も汎用の EXT 接辞を使えます。拡張の一覧とその仕様は、OpenGL RegistryOpenGL ES Registry にあります。

ローダーライブラリで拡張を読み込む

拡張ローダーライブラリは、OpenGL と OpenGL ES の拡張、および新しいバージョンの OpenGL または OpenGL ES コア API の両方へアクセスするための、最も簡単で優れた方法です。必要なものすべての宣言と読み込みに関する詳細を処理します。そのようなライブラリの1つが glad で、ほかにもいくつかあります。

以下の例では glad を使いますが、どの拡張ローダーライブラリも同様に動作します。

最初に glad の Python スクリプトを使ってソースファイルを生成する必要があります。この例では、GLFW と glad の両方でデフォルトとなる、すべてのバージョンの OpenGL に対応したローダーを生成します。OpenGL ES 用のローダーや、特定の API バージョンと拡張集合用のローダーも生成できます。生成ファイルは output ディレクトリへ書き込まれます。

python main.py --generator c --no-loader --out-path output

--no-loader オプションを追加するのは、GLFW が OpenGL および OpenGL ES の関数ポインターを読み込む関数をすでに提供しているためです。この関数は、選択されたコンテキスト作成 API を自動的に使います。glad は独自の実装を持つ代わりに、この関数を呼び出せます。ほかにもいくつかのコマンドラインオプションがあります。詳しくは glad のドキュメントを参照してください。

生成された output/src/glad.coutput/include/glad/glad.houtput/include/KHR/khrplatform.h ファイルをビルドへ追加します。次に、開発環境の OpenGL ヘッダーを置き換える glad ヘッダーファイルをインクルードします。glad ヘッダーを GLFW ヘッダーより先にインクルードすると、開発環境の OpenGL または OpenGL ES ヘッダーが抑制されます。

#include <glad/glad.h>
#include <GLFW/glfw3.h>

最後に、適切なカレントコンテキストを用意してから glad を初期化します。

window = glfwCreateWindow(640, 480, "My Window", NULL, NULL);
if (!window)
{
    ...
}

glfwMakeContextCurrent(window);

gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);

glad を読み込むと、作成したコンテキストと生成した glad ローダーの両方がサポートする、OpenGL コア関数と拡張関数すべてにアクセスできます。その後、描画を開始できます。

コンテキストヒントで、必要な OpenGL または OpenGL ES の最低バージョンを指定できます。要件がより複雑な場合は、コンテキスト属性で実際の OpenGL または OpenGL ES バージョンを確認するか、GLAD_GL_VERSION_x_x 真偽値で特定のバージョンがカレントコンテキストにサポートされるか確認できます。

if (GLAD_GL_VERSION_3_2)
{
    // Call OpenGL 3.2+ specific code
}

特定の拡張がサポートされるか確認するには、GLAD_GL_xxx 真偽値を使います。

if (GLAD_GL_ARB_gl_spirv)
{
    // Use GL_ARB_gl_spirv
}

拡張を手動で読み込む

絶対に必要な場合を除き、この方法を使わないでください。 拡張ローダーライブラリを使えば、単調で反復的かつエラーを起こしやすい大量の作業を省けます。

特定の拡張を使うには、まずコンテキストがその拡張をサポートするか確認し、拡張が新しい関数を導入する場合は、その関数へのポインターを取得しなければなりません。GLFW は、拡張と新しい API 関数を手動で読み込むために glfwExtensionSupportedglfwGetProcAddress を提供します。

この節では、OpenGL 拡張を手動で読み込む方法を示します。OpenGL ES 拡張の読み込みも、拡張ヘッダーの名前を除けば同じです。

glext.hヘッダー

glext.h 拡張ヘッダーは、すべての OpenGL 拡張のインターフェースを定義する、継続的に更新されているファイルです。最新版は常に OpenGL Registry にあります。OpenGL ES Registry には、OpenGL ES の各バージョン向け拡張ヘッダーもあります。開発環境に含まれるものは数年前のままで、使いたい拡張が含まれていない可能性があるため、独自に用意した拡張ヘッダーを使うことを強く推奨します。

このヘッダーは、サポートするすべての拡張の全関数について、関数ポインター型を定義します。これらには PFNGLSPECIALIZESHADERARBPROCglSpecializeShaderARB 用)のような名前が付きます。つまり、関数名を大文字にし、両端へ PFN(pointer to function)と PROC(procedure)を追加します。

拡張ヘッダーをインクルードするには、GLFW ヘッダーをインクルードする前に GLFW_INCLUDE_GLEXT を定義します。

#define GLFW_INCLUDE_GLEXT
#include <GLFW/glfw3.h>

拡張の有無を確認する

使用するマシンが実際に拡張をサポートするとは限りません。ドライバーが古い場合や、グラフィックスカードに必要なハードウェア機能がない場合があるためです。そのため、実行時にコンテキストが拡張をサポートするか確認する必要があります。これは glfwExtensionSupported で行います。

if (glfwExtensionSupported("GL_ARB_gl_spirv"))
{
    // The extension is supported by the current context
}

引数は拡張名を格納したヌル終端 ASCII 文字列です。拡張がサポートされていれば glfwExtensionSupportedGLFW_TRUE、それ以外の場合は GLFW_FALSE を返します。

関数ポインターを取得する

すべてではありませんが、多くの拡張では新しい OpenGL 関数を使う必要があります。これらの関数には、オペレーティングシステムのクライアント API ライブラリ内にエントリーポイントがないことが多いため、実行時に取得する必要があります。glfwGetProcAddress でこれらの関数へのポインターを取得できます。

PFNGLSPECIALIZESHADERARBPROC pfnSpecializeShaderARB = glfwGetProcAddress("glSpecializeShaderARB");

一般に、関数ポインター変数へ関数と(完全に)同じ名前を付けることは避けるべきです。リンカーが混乱する可能性があります。代わりに、上記のように異なる接頭辞を使うか、別の命名方式を使えます。

これまでに説明した要素を組み合わせると、次のようになります。

#define GLFW_INCLUDE_GLEXT
#include <GLFW/glfw3.h>

#define glSpecializeShaderARB pfnSpecializeShaderARB
PFNGLSPECIALIZESHADERARBPROC pfnSpecializeShaderARB;

// Flag indicating whether the extension is supported
int has_ARB_gl_spirv = 0;

void load_extensions(void)
{
    if (glfwExtensionSupported("GL_ARB_gl_spirv"))
    {
        pfnSpecializeShaderARB = (PFNGLSPECIALIZESHADERARBPROC)
            glfwGetProcAddress("glSpecializeShaderARB");
        has_ARB_gl_spirv = 1;
    }
}

void some_function(void)
{
    if (has_ARB_gl_spirv)
    {
        // Now the extension function can be called as usual
        glSpecializeShaderARB(...);
    }
}