高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technical Q&A QA1248
Context Sharing Tips

Q: OpenGL コンテキスト間でリソースを共有するにはどうしたらよいですか?

A: コンテキストの作成時に共有コンテキストを指定することにより、コンテキストはオブジェクトリソースとそれに関連するオブジェクト状態を共有できます。共有コンテキストは、共有を始める前後に作成されたテクスチャオブジェクト、頂点配列オブジェクト、ディスプレイリスト、頂点プログラム、フラグメントプログラムをすべて共有します。これらのオブジェクトの状態も共有されますが、他のコンテキスト状態は共有されません。すべてのコンテキストが、他のどのコンテキストとも共有できるわけではありません。同じレンダラのセットが両方のコンテキストをサポートしているかどうかに大きく依存します。全体的には、共有コンテキストを使うことによって、リソースの重複、セットアップのコスト、デベロッパのコーディングを軽減できます。

Q: 基礎

A: OpenGL コンテキストを作成する際に、オブジェクトリソースを共有する別のコンテキストを指定できます。共有はすべてピアツーピアであるため、デベロッパは、共有リソースに対する参照カウントが維持されており、したがってリソースが明示的に解放されるか、リソースを最後まで共有しているコンテキスト自体が解放されるまで、共有リソースが維持されるものと想定できます。これはできるだけ簡単に考えて、過度に複雑なものと想定しないほうがよいでしょう。

Q: 共有されるものとされないもの

A: コンテキストは「オブジェクトリソース」を共有します。具体的には、テクスチャ、頂点配列オブジェクト、ディスプレイリスト、頂点プログラム、フラグメントプログラムです。テクスチャパラメータやプログラム環境パラメータなどの、オブジェクト状態も共有されます。テクスチャターゲットの有効化の状態、現在のフラグメント/頂点プログラムなど、オブジェクト以外の状態は共有されません。クライアントは必要に応じて、コンテキスト状態の変更を複製する必要がありますが、個々のオブジェクトのセットアップは 1 回だけ行う必要があります

Q: 共有できるコンテキスト

A: コンテキストの共有は、同一の仮想スクリーンマッピングを持っているコンテキストに限られます。つまり、まったく同じレンダラのセットを使って設定されたピクセルフォーマットは、共有可能なコンテキストを作成できるということです。この目標を達成する単純な方法が 2 つあります。1 つは、クライアントが共有したいすべてのコンテキストに対して、まったく同じピクセルフォーマットを使うことです。これにより、仮想スクリーンマッピングが同じであり、したがってコンテキストを共有する能力があることが保証されます。いくつかのケースでは、要求されるピクセルフォーマットが異なるために、レンダラのセットを一致させることが容易でない場合があります。この場合の最も簡単な解決策は、すべてのピクセルフォーマットに対して単一のレンダラを選択することです。

Q: コンテキストを共有する正確な方法

A: コンテキストを共有するのは非常に簡単です。関数呼び出しの正確な形式は、使用している API によって異なります。CGL、AGL、および NSOpenGL の例をリスト 1、2、3 にそれぞれ示します。

リスト 1. CGL Context Setup for Sharing

#include <OpenGL/OpenGL.h>

// 注:説明の目的の都合上、通常は単一のフルスクリーンの
// CGL ピクセルフォーマットを作成するものとする。この例はリスト 2 を参照
CGLPixelFormatAttribute attrib[] = {kCGLPFADoubleBuffer, 0};
CGLPixelFormatObj pixelFormat = NULL;
long numPixelFormats = 0;
CGLContextObj cglContext1 = NULL;
CGLContextObj cglContext2 = NULL;
     
CGLChoosePixelFormat (attribs, &amp;pixelFormat, &amp;numPixelFormats);
CGLCreateContext(pixelFormat, NULL, &amp;cglContext1);

// 最初のコンテキストと共有する 2 番目のコンテキストを作成
CGLCreateContext(pixelFormat, cglContext1, &amp;cglContext2);

リスト 2: AGL でのコンテキストの共有設定

#include <AGL/agl.h>

GLint attrib[] = {AGL_RGBA, AGL_DOUBLEBUFFER, AGL_NONE};
     
AGLPixelFormat aglPixFmt = aglChoosePixelFormat (NULL, 0, attrib);
AGLContext aglContext1 = aglCreateContext (aglPixFmt, NULL);

// 最初のコンテキストと共有する 2 番目のコンテキストを作成
AGLContext aglContext2 = aglCreateContext (aglPixFmt, aglContext1);

リスト 3: NSOpenGL でのコンテキストの共有設定を変更

#import <Cocoa/Cocoa.h>

+ (NSOpenGLPixelFormat*)defaultPixelFormat
{
  NSOpenGLPixelFormatAttribute attributes [] = {
                               NSOpenGLPFADoubleBuffer,  // ダブルバッファ
                               (NSOpenGLPixelFormatAttribute)nil };
  
  return [[(NSOpenGLPixelFormat *)[NSOpenGLPixelFormat alloc]
                                  initWithAttributes:attribs] autorelease];
}

- (NSOpenGLContext*)openGLContextWithShareContext:(NSOpenGLContext*)context
{ // 初めて通るときはコンテキストを作成
   if (_openGLContext == NULL) {
    _openGLContext = [[NSOpenGLContext alloc]
                      initWithFormat:[[self class] defaultPixelFormat]
                      shareContext:context];
    [_openGLContext makeCurrentContext];
    [self prepareOpenGL]; // ここで OpenGL 状態を初期化する呼び出し
  }
  return _openGLContext;
}

- (void)prepareOpenGL
{
  // OpenGL 状態をオーバーライドして初期化するために、
  // コンテキスト作成後に発生
}

また、特定のディスプレイスクリーンに単一のレンダラを使用するようにコンテキストを設定することも比較的簡単です。OpenGL フレームワークは通常、ソフトウェアによるフォールバックレンダラを提供するため、まだフルスクリーンを指定していない場合は(フルスクリーンが「リカバリなし」を意味するため)、ピクセルフォーマット属性で「リカバリなし」を指定して、これを明示的に回避する必要があります。リスト 4、5、6 には、単一ディスプレイのみのピクセルフォーマットを設定する方法を例示します。

リスト 4: CGL での単一スクリーンのピクセルフォーマット

#include <OpenGL/OpenGL.h>

// フルスクリーンの単一ドローアブルのピクセル属性セット
CGLPixelFormatAttribute attribs[] = { kCGLPFADisplayMask, 0,
                                      kCGLPFAFullScreen,
                                      kCGLPFADoubleBuffer,
                                      0 };
CGLPixelFormatObj pixelFormat = NULL;
long numPixelFormats = 0;
CGLContextObj cglContext = NULL;

// 選択したディスプレイのディスプレイマスク属性を埋める
CGDirectDisplayID display = CGMainDisplayID ();
attribs[1] = CGDisplayIDToOpenGLDisplayMask (display);
CGLChoosePixelFormat (attribs, &amp;pixelFormat, &amp;numPixelFormats);

リスト 5: AGL での単一スクリーンのピクセルフォーマット

#include <AGL/agl.h>

GLint attrib[] = {AGL_RGBA, GL_DOUBLEBUFFER, AGL_NO_RECOVERY, AGL_NONE};
     
GDHandle display = GetMainDevice ();
AGLPixelFormat aglPixFmt = aglChoosePixelFormat (&amp;display, 1, attrib);

リスト 6: NSOpenGL での単一スクリーンのピクセルフォーマット

#import <Cocoa/Cocoa.h>

+ (NSOpenGLPixelFormat*)defaultPixelFormat
{
  NSOpenGLPixelFormatAttribute attributes [] = {
                               NSOpenGLPFAScreenMask,
                               0,
                               NSOpenGLPFANoRecovery,
                               NSOpenGLPFADoubleBuffer,  // ダブルバッファ
                               (NSOpenGLPixelFormatAttribute)nil };
  
// 選択したディスプレイのディスプレイマスク属性を埋める
CGDirectDisplayID display = CGMainDisplayID ();
attributes[1] = (NSOpenGLPixelFormatAttribute)
                                  CGDisplayIDToOpenGLDisplayMask (display);
return [[(NSOpenGLPixelFormat *)[NSOpenGLPixelFormat alloc]
                                  initWithAttributes:attribs] autorelease];
}

Q: 補足

A: 単一のディスプレイ、したがって単一のレンダラをサポートするピクセルフォーマットを指定した場合は、ウインドウ化されているドローアブルを別のスクリーンにドラッグしたり、フルスクリーンコンテキストを別のディスプレイにアタッチしたりすると、レンダリングに失敗するか、または指定したレンダラで、新しいディスプレイにコピーを使ってレンダリングが行われる可能性があります。どちらも望ましいことではありません。安全を考えて、レンダラの変更を検出して対処するようなコードを作成する必要があります。

Q: フルスクリーンのピクセルフォーマットを使ったウインドウ化されたドローアブルについて

A: ウインドウ化されたスクリーンと、フルスクリーンのピクセルフォーマットの共有をさらに簡素化するために、ウインドウ化されたドローアブルでフルスクリーンのピクセルフォーマットを使う機能が Mac OS X v10.3 (Panther) に追加されました。つまり、クライアントはフルスクリーンを指定して 1 つのピクセルフォーマットを作成し、それを使ってウインドウ化されたスクリーンとフルスクリーンのコンテキストをサポートできます。フルスクリーンのピクセルフォーマットが明示的に単一のディスプレイをサポートするための要件と、共有ピクセルフォーマットがまったく同じスクリーン設定をサポートするための要件は緩和されておらず、この要件は依然として満たしている必要があります

掲載日: 2005-02-07

ドキュメントの改訂履歴

日付 メモ
2005-02-07 テキストの更新と再編成。radr://problem/3968679 に従って最後のパラグラフの情報を変更。
2003-06-24 新規ドキュメント

掲載日: 2005-02-07