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

Technote 1182

NewGWorlds in VRAM and AGP Memory


CONTENTS

新しい NewGWorld ()

新しい NewGWorld () の使い方

インプリメントの実際

要約

のテクニカルノートでは、Mac OS 9 のリリースにともない NewGWorld に加えられた変更について説明します。

 

新しい NewGWorld ルーチンでは、オフスクリーン GWorld を AGP メモリおよび VRAM に割り当てることができるようになりました。これにより、オフスクリーンイメージの割り当て方法を決定する上で、アプリケーションプログラマにより大きな自由度が与えられることになります。しかし、その使い方を誤ると、状況はこれまで以上に複雑なものになり、アプリケーションのパフォーマンスを大幅に低下させる原因になってしまうこともあります。

 

このテクニカルノートでは、新しいセレクタを紹介し、それらの基本的な使い方を説明してから、これらのセレクタの使用に関連して発生する可能性のある基本的な問題をいくつか具体的に示します。また、最後に、簡単なインプリメントの例を示します。


新しい NewGWorld ()

NewGWorld

NewGWorld 関数を使用すると、オフスクリーングラフィックスワールドを作成することができます。


QDErr NewGWorld     (GWorldPtr   *   offscreenGWorld,
                              short           pixelDepth,
                              const Rect  *   boundsRect,
                              CTabHandle      cTable,    /* can be NULL */
                              GDHandle        aGDevice,  /* can be NULL */
                              GWorldFlags     flags)
         

offscreenGWorld

  • offscreenGWorld は、このルーチンによって作成されるオフスクリーングラフィックスワールドへのポインタです。

pixelDepth

  • pixelDepth は、オフスクリーンワールドの色数で、1 ピクセルあたり 1、2、4、8、16、または 32 ビットいずれかの値をとります。NewGWorld 関数は、boundsRect パラメータで指定した矩形と交差する境界矩形を持ったすべてのスクリーンの中で最も多くの色数を含むスクリーンの色数を使用します。このパラメータにゼロを指定すると、NewGWorld はオフスクリーンワールドに対する新しい GDevice レコードを作成する代わりに、このデバイスの GDevice レコードを使用するようになります。基本的な QuickDraw のみをサポートするコンピュータで NewGWorld を使用する場合は、このパラメータにゼロまたは 1 のみを指定します。

boundsRect

  • boundsRect は、オフスクリーンのピクセルマップに対する境界矩形およびポート矩形です。NewGWorld が GDevice レコードを作成する場合、これは GDevice レコードに対する境界矩形となります。pixelDepth パラメータにゼロを指定すると、NewGWorld は境界をグローバル座標で解釈し、それを使って、どのスクリーンが矩形と交差するかを判断します (この後、NewGWorld は、この矩形と交差する境界矩形を持ったすべてのスクリーンの中で最も多くの色数を含むスクリーンの色数、カラーテーブル、および GDevice レコードを使用します)。通常、アプリケーションはこのパラメータに、このオフスクリーンワールドからピクセルイメージをコピーするオンスクリーンウインドウに対応するポート矩形を指定します。

cTable

  • cTable は ColorTable レコードへのハンドルです。このパラメータに NULL を渡すと、NewGWorld は pixelDepth パラメータで指定した色数に対応するデフォルトのカラーテーブルを使用します。pixelDepth パラメータに 0 を設定すると、NewGWorld は cTable パラメータの設定を無視し、boundsRect パラメータで指定した矩形と交差する境界矩形を持ったすべてのグラフィックスデバイスの中で最も多くの色数を含むグラフィックスデバイスのカラーテーブルをコピーして使用します。基本的な QuickDraw のみをサポートするコンピュータで NewGWorld を使用する場合は、このパラメータに NULL のみを指定します。

aGDevice

  • aGDevice は、2 つのケースのみで使用される GDevice レコードへのハンドルです。まず第 1 に、flags パラメータで noNewDevice フラグをセットした場合、NewGWorld は新しいオフスクリーングラフィックスワールドにこの GDevice レコードをアタッチします。第 2 に、flags パラメータで useDistantHdwrMem または useLocalHdwrMem フラグをセットした場合、NewGWorld はこの GDevice の VRAM または AGP メモリを使って GWorld を格納します。pixelDepth パラメータにゼロを設定するか、noNewDevice、useDistantHdwrMem、または useLocalHdwrMem フラグをセットしなかった場合、NewGWorld は aGDevice パラメータを無視するため、このパラメータには NULL を設定しなければなりません。pixelDepth パラメータにゼロを設定すると、NewGWorld は、boundsRect パラメータで指定した矩形と交差する境界矩形を持ったすべてのグラフィックスデバイスの中で最も多くの色数を含むグラフィックスデバイスの GDevice レコードを使用します。コンピュータが基本的な QuickDraw のみをサポートしている場合は、このパラメータに NULL を渡してください。一般に、アプリケーションがオフスクリーングラフィックスワールドに対する GDevice レコードを作成することはありません。useDistantHdwrMem または useLocalHdwrMem フラグをセットした場合は、常に GDevice を指定してください。GDeviceを指定しないと、GWorld に関連する動作とデバイスが不定になってしまいます。

flags

  • flags はアプリケーションで使用可能なオプションを記述します。pixPurge、noNewDevice、useTempMem、keepLocal、useDistantHdwrMem、および useLocalHdwrMem の各フラグは、ほぼ自由に組み合わせて使用することができます。これらのフラグのいずれも使用したくない場合は、このパラメータに 0 を渡します。このとき NewGWorld はデフォルトの動作を行います。つまり、オフスクリーンピクセルイメージのベースアドレスがパージされない場所にオフスクリーングラフィックスを作成し、既存の GDevice レコードを使用するか (pixelDepth パラメータに 0 を渡した場合)、新しい GDevice レコードを作成して、アプリケーションヒープ内のメモリを使用します。また、グラフィックスアクセラレータがオフスクリーンピクセルイメージをキャッシュに格納することが許可されます。なお、keepLocal は useDistantHdwrMem または useLocalHdwrMem とともに使用しないでください。結果が不定になってしまいます。次に、使用可能なフラグの説明を示します。

enum 
{
     pixPurge           = 1L << pixPurgeBit,
     noNewDevice        = 1L << noNewDeviceBit,
     useTempMem         = 1L << useTempMemBit,
     keepLocal          = 1L << keepLocalBit,
     useDistantHdwrMem  = 1L << useDistantHdwrMemBit,
     useLocalHdwrMem    = 1L << useLocalHdwrMemBit,
};
typedef unsigned longGWorldFlags;
         

pixPurge

  • オフスクリーンピクセルイメージのベースアドレスをパージ可能にします。

noNewDevice

  • オフスクリーン GDevice レコードの作成を停止します。

useTempMem

  • テンポラリメモリ内にオフスクリーンピクセルイメージのベースアドレスを作成します。

keepLocal

  • オフスクリーンピクセルイメージをメインメモリ内に保持しますが、グラフィックスアクセラレータカードのキャッシュに格納できなくなります。

useDistantHdwrMem

  • VRAM 内でオフスクリーンピクセルイメージの作成を試みます。

useLocalHdwrMem

  • AGP メモリ内でオフスクリーンピクセルイメージの作成を試みます。

解説

NewGWorld 関数は、pixelDepth パラメータで指定した色数、boundsRect パラメータで指定した境界矩形、cTable パラメータで指定したカラーテーブル、それに flags パラメータで指定したオプションを使って、オフスクリーングラフィックスワールドを作成します。NewGWorld 関数は、offscreenGWorld パラメータに新しいオフスクリーングラフィックスワールドへのポインタを返します。このポインタは、この章で説明する他のルーチンの中でこの新しいオフスクリーンワールドを参照するときに使用します。

通常、pixelDepth パラメータには 0 を渡し、boundsRect パラメータにはウインドウのポート矩形を渡し、cTable および aGDevice パラメータには NULL を渡します。また、Pascal のコードの場合は flags パラメータに空集合 ([ ]) を渡し、C のコードの場合は 0 を渡します。これにより、アプリケーションには NewGWorld のデフォルトの動作が提供され、アプリケーションは基本的な QuickDraw を実行するコンピュータをサポートするようになります。また、アプリケーションがオフスクリーングラフィックスワールド内のイメージをオンスクリーングラフィックスポートにコピーするとき、QuickDraw は CopyBits、CopyMask、および CopyDeepMask プロシージャを最適化できるようになります。

NewGWorld 関数は、オフスクリーングラフィックスポートとそのピクセルマップに使用するメモリを割り当てます。基本的な QuickDraw のみをサポートするコンピュータでは、NewGWorld は、この章で説明する他の重要なルーチンを使って、アプリケーションが操作できる 1 ビットのピクセルマップを作成します。アプリケーションでは、この 1 ビットのピクセルマップを基本的なグラフィックスポートの中にコピーできます。

pixelDepth パラメータにゼロを指定しないかぎり、または flags パラメータに noNewDevice フラグを渡して、aGDevice パラメータに GDevice レコードを渡さないかぎり、NewGWorld は新しいオフスクリーン GDevice レコードも割り当てます。

イメージを作成するとき、アプリケーションは NewGWorld 関数を使って、色数などのイメージの特性を最適化したオフスクリーングラフィックスワールドを作成します。イメージ作成後、アプリケーションはさらに CopyBits、CopyMask、または CopyDeepMask プロシージャを使って、そのイメージをオンスクリーングラフィックスポートにコピーします。Color QuickDraw は、スクリーンで使用可能な最大の色数を使って、自動的にイメージのレンダリングを行います。このようにオフスクリーングラフィックスポート内でイメージを作成して、それをスクリーンにコピーすることで、アプリケーションが複雑なイメージをオンスクリーンで直接構築する場合に発生する可能性のある視覚的なぎこちなさを抑えることができます。

NewGWorld 関数は、OpenCPort 関数を呼び出して、オフスクリーングラフィックスポートを初期化します。NewGWorld 関数はオフスクリーングラフィックスポートの可視領域に、その境界矩形と一致する矩形領域を設定します。スクリーンの GDevice レコードのいずれかがオフスクリーンワールドの GDevice レコードと同じカラーテーブルを共有していないかぎり、NewGWorld 関数は Color Manager プロシージャの MakeITable を使って反転テーブルを生成します。また、スクリーンとオフスクリーンでカラーテーブルが共有されている場合は、その GDevice レコードの反転テーブルが使用されます。

オフスクリーンピクセルイメージのアドレスは、オフスクリーングラフィックスワールドの PixMap レコードからは直接的にアクセスできません。しかし、GetPixBaseAddr 関数 (『Inside Macintosh』の 6-38 ページで説明されています) を使って、オフスクリーンピクセルイメージの先頭へのポインタを取得することは可能です。

メモリの使用状況を見積もるため、次の式を使って、オフスクリーンピクセルイメージのサイズを計算することができます。


rowBytes * (boundsRect.bottom - boundsRect.top)

flags パラメータでは、GWorldFlags データ型によって定義されているいくつかのオプションを指定することができます。これらのオプションのいずれも使用したくない場合は、そのオプションにゼロを渡します。

pixPurge フラグを指定すると、NewGWorld はオフスクリーンピクセルイメージをパージ可能なメモリブロックに格納します。この場合は、オフスクリーンピクセルイメージとの間で描画のやり取りを開始する前に、LockPixels 関数 (『Inside Macintosh: Imaging With QuickDraw』で説明されています) を呼び出して、この関数が TRUE を返すことを確認してください。 LockPixels が FALSE を返す場合、ピクセルイメージのメモリはすでにパージされています。このためアプリケーションでは、UpdateGWorld を呼び出してメモリの再割り当てを行い、ピクセルイメージを再構築するか、オフスクリーングラフィックスワールド内でイメージを準備する代わりにウインドウ内で直接描画を行う必要があります。なお、メモリの再割り当てを行わないでピクセルイメージを再構築して、パージされているオフスクリーンピクセルイメージに描画を行ったり、パージされているピクセルイメージからデータをコピーすることは絶対に避けてください。noNewDevice フラグを指定すると、NewGWorld は新しいオフスクリーン GDevice レコードを作成しなくなります。その代わりに aGDevice パラメータで指定した GDevice レコード (およびそれに関連する色数とカラーテーブル) を使って、オフスクリーングラフィックスワールドが作成されます (pixelDepth パラメータに 0 を設定すると、たとえ noNewDevice フラグを指定していても、NewGWorld は boundsRect パラメータで指定した矩形と交差する境界矩形を持ったすべてのスクリーンの中で最も多くの色数を含むスクリーンの GDevice レコードを使用します)。NewGWorld 関数はオフスクリーングラフィックスワールドの GDevice レコードへの参照を保持し、SetGWorld プロシージャ(『Inside Macintosh: Imaging With QuickDraw』で説明されています) はこのレコードを使って現在のグラフィックスデバイスを設定します。useTempMem フラグをセットすると、NewGWorld はオフスクリーンピクセルイメージのベースアドレスをテンポラリメモリ内に作成します。テンポラリメモリは暫定的な目的にのみ使用し、AllowPurgePixels プロシージャ (『Inside Macintosh: Imaging With QuickDraw』で説明されています)とのみ組み合わせて使用する必要があるため、通常、このフラグを使用することはありません。keepLocal フラグを指定すると、オフスクリーンピクセルイメージは Macintosh のメインメモリに保持され、グラフィックスアクセラレータカードのキャッシュには格納されなくなります。組み込まれているグラフィックスアクセラレータカードによってもたらされる利点を帳消しにしてしまうこともあるので、このフラグを使用するときには十分な注意が必要です。useDistantHdwrMem または useLocalHdwrMem を指定すると、NewGWorld は VRAM または AGP メモリ内にオフスクリーンピクセルイメージを割り当てようとします。両方のフラグが指定されていると、NewGWorld はまず AGP メモリ内で割り当てを試み、これに失敗すると、VRAM 内で割り当てを試みます。useDistantHdwrMem、useLocalHdwrMem、またはこの両方を使用していて、NewGWorld が要求されたメモリ領域内にオフスクリーンピクセルイメージを割り当てることができないと、NewGWorld は異常終了し、memFullErr エラーコードを返します。

実行結果として、NewGWorld は後述する 4 つの結果コードのうちのいずれかを返します。NewGWorld の呼び出しが正常に終了すると、常に noErr が返されます。その他の結果コードが返されたときは、NewGWorld が要求されたオフスクリーンピクセルイメージの割り当てに失敗したと見なしてください。

特に注意を要する問題

cTable パラメータに ColorTable レコードへのハンドルを指定すると、NewGWorld はそのレコードのコピーを作成し、そのハンドルをオフセットスクリーンの PixMap レコードに格納します。cTable パラメータで指定した ColorTable レコードがオフスクリーングラフィックスポートの色数に対して有効であるかどうかを確認することはアプリケーションの責任です。

NewGWorld を使用するときに、色数、カラーテーブル、または GDevice レコードを指定し、これがオフスクリーンイメージのコピー先となるウインドウで使用されている色数、カラーテーブル、または GDevice レコードと異なっている場合は、CopyBits、CopyMask、および CopyDeepMask プロシージャはその実行にかなりの時間を要するようになります。これにより、AGP メモリまたは VRAM 内に割り当てられているバッファがハードウェアブリッティングを利用できなくなり、結果的にコピーのパフォーマンスが極端に低下してしまう可能性があります。

VRAM 内に割り当てられた GWorld に関して、特に注意すべき 2 つの重要な問題があります。まず第 1 に、VRAM 内でメモリが割り当てられるたびに、GetPixBaseAddr を介して取得されたベースアドレスや PixMap 構造体から直接読み込まれたベースアドレスが無効になってしまうことがあります。この問題は、NewGWorld の呼び出しのような明示的割り当てによっても発生しますが、OpenGL の内部テクスチャ割り当てに関連する呼び出しのような暗黙的割り当てによっても発生します。格納されているピクセルイメージそのものは常に有効ですが、それはすでに VRAM 内に移動されているため、格納されているベースアドレスのレンダリングは無効になってしまいます。必要以上に長い時間イメージのベースアドレスを格納しないようにしてください。特に、NewGWorld や テクスチャ作成ルーチンの呼び出しをはさんでベースアドレスを格納し続けることは絶対に避けてください。

第 2 に、VRAM 内に割り当てられたオフスクリーンピクセルイメージは、ディスプレイドライバによるシステムタスク時にパージされることがあります。つまり、アプリケーションが WaitNextEvent または SystemTask を呼び出して時間を計算するたびに、VRAM GWorld の内容が失われてしまう可能性があるということです。この問題は、通常、表示解像度や色数の変更に関連していて、それほど頻繁に発生するわけではありませんが、万一の場合に備えてコードを書く必要があります。このようなパージは、GWorld がロックされているかどうかによって、発生したり発生しなかったりすることもあります。

LockPixels から返される FALSE、GetPixBaseAddr から返される NULL、または PixMap の baseAddr フィールドに含まれる NULL はいずれも、ピクセルイメージがパージされたことを示しています。ピクセルイメージを再割り当てするには、UpdateGWorld または Dispose を呼び出して、DisposeGWorld から得られた現在の GWorld を処理し、NewGWorld によって GWorld を再構築します。いずれかの方法でピクセルイメージを再構築する必要があります。

オフスクリーングラフィックスワールドでカスタムカラーテーブルを使用するには、関連づけられたオフスクリーン GDevice レコードを作成する必要があります。これは、Color QuickDraw がその反転テーブルを必要とするためです。

現在のところ、NewGWorld では、指定されたパラメータの組み合わせに対する徹底的なエラーチェックを行っていません。NewGWorld はこれらのパラメータが意味を持つものと想定しています。特に、新しいフラグパラメータを使った作業を行うときに、このような無条件の前提が適用されます。たとえば、flags に keepLocal、useDistantHdwrMem、および useLocalHdwrMem を渡し、aGDevice に NULL を渡しても特に不都合はありませんが、このパラメータの組み合わせにはまったく意味がなく、このときの関数の動作も定義されていません。NewGWorld に指定したフラグやその他のパラメータの組み合わせが実際に協調して動作していることを確認する必要があります。また、OS によるこの種のエラーのチェックを期待しないでください。

NewGWorld 関数は、アプリケーションヒープ内のメモリブロックを移動したりパージしたりすることがあります。アプリケーションが割り込み時にこの関数を呼び出さないようにしてください。


リザルトコード

noErr

0

正常終了

paramErr

-50

不正なパラメータ

memFullErr

-108

メモリ不足によるエラー

cDepthErr

-157

無効な色数


『Inside Macintosh: Imaging With QuickDraw』も参照してください。

『Inside Macintosh: Imaging With QuickDraw』の 6-5 ページに記載されている Listing 6-1 と、6-10 ページに記載されている Listing 6-2 には、NewGWorld を使ってオフスクリーングラフィックスワールドを作成する方法が具体的に示されています。

オフスクリーングラフィックスワールドに対する色数、境界矩形、またはカラーテーブルを変更する必要がある場合は、『Inside Macintosh』の 6-23 ページに記載されている UpdateGWorld 関数を使用します。

ページの先頭に戻る

新しい NewGWorld () の使い方

新しい NewGWorld の使い方は基本的にこれまでと同様です。新しく追加された 2 つのフラグである useDistantHdwrMem および useLocalHdwrMem により、ユーザはオフスクリーンピクセルイメージが割り当てられる領域を指定できるようになりました。useDistantHdwrMem または useLocalHdwrMem を単独で使用すると、NewGWorld は、aGDevice で指定されているデバイス上の VRAM または AGP メモリ内のみにイメージを割り当てようとします。この割り当てに失敗すると、NewGWorld は異常終了し、memFullErr エラーを返します。useDistantHdwrMem と useLocalHdwrMem の両方を指定すると、NewGWorld はまず VRAM 内で割り当てを試み、この後、aGDevice で指定されているデバイスの AGP メモリ内で割り当てを試みます。両方の割り当てに失敗すると、NewGWorld は異常終了し、memFullErr エラーを返します。AGP メモリまたは VRAM 内で割り当てを行うときに、aGDevice には絶対に NULL を指定しないでください。NULL を指定すると、割り当てに使用するデバイスが不定になってしまいます (これはデベロッパによって意図されたことでないのは明らかです)。

useDistantHdwrMem は、一度 (または数回) 設定し、その後で繰り返し使用するピクセルイメージを割り当てるために役立ちます。システムメモリから VRAM ピクセルイメージへの書き込みには比較的時間がかかりますが、VRAM から VRAM、または VRAM からスクリーンへのハードウェアコピーは非常に高速に実行されます。現在のところ、コピーオペレーションがハードウェアアクセラレーションを使用するかどうかを判断するメカニズムは用意されていないため、VRAM 内に割り当てるピクセルイメージはすべて同じ色数を使用し、スクリーンと同じカラーテーブルを共有することが望ましいといえます。また、マスクやサイズ変更を使用しない単純なコピーオペレーションを使用し、できるかぎりコピーのパフォーマンスを向上できるように配慮してください。

useLocalHdwrMem は、AGP メモリ内でピクセルイメージの割り当てを試みます。システムに AGP グラフィックスシステムが搭載されていなかったり、NewGWorld がピクセルイメージを割り当てることができないと、割り当ては異常終了し、memFullErr エラーが返されます。AGP 割り当てはシステムメモリ内で実行されるため、VRAM 内に割り当てるピクセルイメージに関連するような問題の影響を受けることはありません。しかし、AGP メモリには、キャッシュに格納できない、他のシステムメモリとの間でコピーを行うと通常のシステムメモリよりも処理が若干遅くなるなど、いくつかの制限があります。それほど問題にはなりませんが、パフォーマンスが若干低下するのは事実です。

割り当てが行われる領域や、標準的なシステムメモリ以外の領域で割り当てを行うことに伴う一般的な注意事項 (前の段落で説明したような) を正しく理解することは非常に重要です。useDistantHdwrMem または useLocalHdwrMem フラグによるオフスクリーン GWorld を使用するときに、アプリケーションのパフォーマンスを向上させるポイントは、どのピクセルイメージが頻繁に使用されていて、どのピクセルイメージがハードウェアアクセラレーションによるコピーに移行できるかをきちんと認識することです。非常に頻繁に使用され、ほとんど変更されないイメージは、VRAM 内に置いてコピーのパフォーマンスを最適化することができます。一般的な用途には AGP メモリを使用できます。AGP メモリの容量は制限されていて、しかも OpenGL などの他のグラフィックサービスもこれを使用するため、より頻繁に使用されるイメージをまず割り当てるように注意を払います。また、AGP メモリは仮想メモリシステムによってディスクにスワップされず、使用可能な AGP メモリ容量はシステムにインストールされている物理メモリの容量によって変わる点にも注意する必要があります。

コピーにハードウェアアクセラレーションが使用されない場合、AGP メモリからスクリーンへのコピーのパフォーマンスは、最大でシステムメモリと同等になることが期待できます。同様にアクセラレーションが使用されないケースで、VRAM からスクリーンへのコピーのパフォーマンスはシステムメモリと比較すると若干遅くなります。このようにイメージを注意深く割り当て、AGP メモリと VRAM を賢く使い分けてください。

NewGWorld がアプリケーションヒープの外部 (つまり、AGP メモリ空間または VRAM) にメモリを割り当てるときは、アプリケーションを終了する前に DisposeGWorld を使って、そのメモリを適切に処理することが非常に重要です。これに失敗すると、メモリリークが発生し、それ以降 VRAM または AGP メモリを使用できなくなってしまいます。多くの場合、このようにしてリークしたメモリはシステムを再起動しないかぎり回復されません。

実際にインプリメントするときは、すでに述べた VRAM に割り当てる GWorld の注意事項を確実に守ってください。GWorld のピクセルイメージを VRAM 内で正しく移動またはパージすることが決め手です。これにより、VRAM GWorld を使用しようとするときに画面にゴミが表示されたり、無効なメモリにアクセスすることが確実になくなります。

ページの先頭に戻る

インプリメントのサンプル

NewGWorld のインプリメントは実際には非常に簡単です。以下に、実際の作業で役に立つ単純なサンプルコードを示します。まず、新しいフラグが使用できるかどうかをテストします。このとき必要になるのは、システムのバージョンを確認することだけです (NewGWorld の新バージョンに特殊なセレクタは含まれていないので)。使用中の Mac OS が 8.6 より大きいものであれば、useDistantHdwrMem および useLocalHdwrMem が使用可能であると見なせます。


Boolean gNewNewGWorld = false;
long versionSystem;
// これは、8.6 以降の Mac OS で動作する
Gestalt ('sysv', &versionSystem);
 if (0x00000860 < (versionSystem & 0x00000FFFF))
      gNewNewGWorld = true; // システムはバージョン 8.6 以降である

次に、GWorld の割り当てと再割り当てに必要となる関数をカプセル化する必要があります。両方の処理には同じロジックを使用します。つまり、まず GWorldPtr をチェックし、さらにピクセルイメージの baseAddr をチェックして、最後にウインドウの GDevice がまだオフスクリーンの GDevice と同じであるかどうかをチェックします。GWorld を割り当てるときは、位置入力パラメータを指定し、このパラメータを使って、割り当てを行うメモリ空間 (VRAM、AGP メモリ、またはアプリケーションヒープ) を決定します。割り当てが正常に終了しなかった場合は、次のタイプを対象に処理を繰り返します。もちろん、この動作は必要に応じて修正することができます。GWorld の割り当てまたは再割り当てが正常に終了したときは True が返され、既存の GWorld が有効なときは False が返されます。以下のサンプルコードはこの一連の処理を示しています。


Boolean BuildOffscreen (GWorldPtr * ppGWorld, WindowPtr pWindow,
                        short * plocation)
{ 
     GDHandle hgdWindow = NULL;
     Boolean fMustRebuild = false;
         
if (NULL == *ppGWorld) // 渡された GWorld が割り当てられていない場合
     fMustRebuild = true;
else
{
     PixMapHandle hPixmap = GetGWorldPixMap (*ppGWorld);
     // pixmap ハンドルが NULL であるか、pixmap のベースアドレスが NULL の場合
     if ((NULL == hPixmap) || (!GetPixBaseAddr (hPixmap)))
         fMustRebuild = true;
     // GWorld がウインドウと同じデバイス上にない場合
         else if (GetGWorldDevice(*ppGWorld) != GetWindowDevice (pWindow))
         fMustRebuild = true;
}
         
if (fMustRebuild) // 再構築する必要がある
{
     // ウインドウの色数
     short wPixDepth = (**((CGrafPtr)pWindow)->portPixMap).pixelSize; 
     GDHandle hgdWindow = GetWindowDevice (pWindow);// ウインドウの GDevice
     if (NULL != *ppGWorld) // 割り当てられた GWorld がある場合
     {
         DisposeGWorld (*ppGWorld);// 現在の GWorld をダンプする
         *ppGWorld = NULL;
     }
     switch (*plocation) // 配置したい場所
     {
         case kInVRAM:
             if (noErr == NewGWorld (ppGWorld, wPixDepth, &pWindow->portRect,
                                     NULL, hgdWindow,
                                     noNewDevice | useDistantHdwrMem))
              break;
         // VRAM で失敗したら、そのシグナルを送り、AGP に移る
         SysBeep (30);
         *plocation = kInAGP;
     case kInAGP:
         if (noErr == NewGWorld (ppGWorld, wPixDepth, &pWindow->portRect,
                                 NULL, hgdWindow,
                                 noNewDevice | useLocalHdwrMem))
              break;
         // AGP で失敗したら、そのシグナルを送り、システムメモリに移る
         SysBeep (30);
         *plocation = kInSystem;
     case kInSystem:
     default:
         if (noErr != NewGWorld (ppGWorld, wPixDepth,
                                 &pWindow->portRect, NULL, hgdWindow,
                                 keepLocal | noNewDevice))
         
          {
              // システムでも失敗したら、GWorld を割り当てることはできない
              // 適切なロケーションがないことを示すシグナルを送り、デバッガに移る
              SysBeep (30);
              *plocation = kNoWhere;
              DebugStr ("\pUnable to allocate off screen image");
              return false; // 何も割り当てられなかった
         }
         *plocation = kInSystem;
    } 
     return true; // GWorld を再構築する
   }
   return false; // すべて OK
}

この前の関数は、大多数のウインドウが常駐する GDevice を決定する GetWindowDevice を呼び出す点を除けば、標準的な Macintosh Toolbox 関数を使用しています。なお、ウインドウが複数のデバイスにわたるケースを処理するかどうかはそれぞれのアプリケーションデベロッパの判断に委ねられているということに注意してください。次に、GetWindowDevice のコードを示します。

GDHandle GetWindowDevice (WindowPtr pWindow)
{
     Rect rectWind, rectSect;
     short wFrameHeight, wTitleHeight;
     long greatestArea, sectArea;
     GDHandle hgdNthDevice, hgdZoomOnThisDevice;
         
     rectWind = pWindow->portRect;
     LocalToGlobal ((Point*)& rectWind.top); // グローバル座標に変換する
     LocalToGlobal ((Point*)& rectWind.bottom);
     // ウインドウのタイトルバーの高さを計算する
     wFrameHeight = rectWind.left - 1 ?
                   (**(((WindowPeek)pWindow)->strucRgn)).rgnBBox.left;
     wTitleHeight = rectWind.top - 1 ?
                   (**(((WindowPeek)pWindow)->strucRgn)).rgnBBox.top;
     rectWind.top = rectWind.top - wTitleHeight;
     hgdNthDevice = GetDeviceList ();
     greatestArea = 0; // 0 に初期化する
     // GDevice リストに含まれるすべての gdRects に対するウインドウをチェックし、
     //  どの gdRect にウインドウの最大領域が含まれているかを記憶する
     while (hgdNthDevice)
     {
         if (TestDeviceAttribute (hgdNthDevice, screenDevice))
              if (TestDeviceAttribute (hgdNthDevice, screenActive))
              {
                   // SectRect ルーチンはウインドウ矩形と
                   //  この GDevice 矩形との交差を計算し、
                   //  矩形同士が交わる場合は TRUE を返し、
                   //  そうでない場合は FALSE を返す
                   SectRect(&rectWind, &(**hgdNthDevice).gdRect, &rectSect);
                   // まず、どのスクリーンが最大のウインドウ領域を保持しているかを
                   //  判断し、現在のデバイス上での矩形の領域を計算する
                   sectArea = (long)(rectSect.right - rectSect.left) *
  (rectSect.bottom - rectSect.top);
                   if ( sectArea > greatestArea )
                   {
                        greatestArea = sectArea;// いままでの最大の領域を設定する
                        hgdZoomOnThisDevice = hgdNthDevice;// ズームデバイスを設定する
                   }
                   hgdNthDevice = GetNextDevice(hgdNthDevice);
              }
         } // while ループ
     return hgdZoomOnThisDevice;
}
         

バッファの割り当てが完了した後は、それにデータを書き込み、それをウインドウにブリットするだけです。この処理を行うプロセスはこれまで同様です。次に、この処理の具体例を示します。なお、FillOffscreen は渡された GWorldPtr が有効であることを前提にしていますが、BlitToWindow はより一般的な用途に使用され、GWorld を対象とするチェックを実行します。


// オフスクリーンバッファをランダムな明色で塗りつぶす
         
void FillOffscreen (GWorldPtr pGWorld)
{
     GDHandle hGDSave;
     CGrafPtr pCGrafSave;
     Rect rectSource = (pGWorld->portRect);
     RGBColor rgbColor;
         
     rgbColor.red   = (Random () + 32767) / 2 + 32767;
     rgbColor.green = (Random () + 32767) / 2 + 32767;
     rgbColor.blue  = (Random () + 32767) / 2 + 32767;
         
     GetGWorld (&pCGrafSave, &hGDSave);
     SetGWorld (pGWorld, NULL);
     if (LockPixels (GetGWorldPixMap (pGWorld)))
     {
         // バックグランドを描画する
         EraseRect (&rectSource);
         RGBForeColor (&rgbColor);
         PaintRect (&rectSource);
         UnlockPixels (GetGWorldPixMap (pGWorld));
     }
     SetGWorld (pCGrafSave, hGDSave);
}
         
// オフスクリーンをチェックし、それを前面にブリットする
         
void BlitToWindow (GWorldPtr pGWorld, WindowPtr pWindow, short * pLocation)
{
    Rect rectDest = ((GrafPtr)pWindow)->portRect;
     Rect rectSource = ((GrafPtr)pWindow)->portRect;
     GrafPtr pCGrafSave;
         
     // オフスクリーンが有効であることを確認し、必要な場合は再構築する
     if (BuildOffscreen (&pGWorld, pWindow, pLocation))
     FillOffscreen (pGWorld);
         
     // ブリット
     GetPort (&pCGrafSave);
     SetPort ((GrafPtr) pWindow);
     if (LockPixels (GetGWorldPixMap (pGWorld)))
     {
         CopyBits (&((GrafPtr)pGWorld)->portBits,
                   &pWindow->portBits, &rectSource, &rectDest, srcCopy, NULL);
         UnlockPixels (GetGWorldPixMap (pGWorld));
     }
     SetPort (pCGrafSave);
}

最後に、NewGWorld によって割り当てられたメモリが適切に処理されていることを確認する必要があります。次のコードはこの処理を具体的に示しています。

// GWorld がアプリケーションヒープ内に存在しない可能性があるため、この処理は非常に重要
if (pGWorld)
     DisposeGWorld (pGWorld);
pGWorld = NULL;

ページの先頭に戻る

要約

新しい NewGWorld を使用すると、パフォーマンスオリエンティッドなアプリケーションを作成するときに使用できる選択肢の数が大幅に増加します。たとえば、ピクセルイメージを VRAM または AGP メモリ空間に割り当てることで、以前とは比べものにならないレベルのグラフィックスパフォーマンスを達成できます。ただし、これらの新機能を使用することでアプリケーションデベロッパにはいくつかの条件が課されることになり、コードがすべての条件下で適切に機能することを確認しなければならなくなります。以下に、新しい NewGWorld を使用するときに検討すべきチェックリストを示します。

  • システムのバージョンをチェックし、useDistantHdwrMem および useLocalHdwrMem フラグが使用可能であることを確認する。
  • seDistantHdwrMem または useLocalHdwrMem を使用するときは、GDevice を指定する。
  • エラーに対する戻り値をチェックする。
  • ピクセルイメージのベースアドレスを取得するときは、NULL であるかどうかをチェックする。
  • パージされたピクセルイメージを処理するための回復の仕組みをインプリメントする。
  • システムに時間を渡す関数や VRAM の割り当てと割り当て解除を行うことのできる関数の間でベースアドレスをキャッシュに格納しない。
  • GWorlds が適切に処理されていることを確認し、VRAM または AGP メモリでメモリリークが発生するのを防ぐ。

useDistantHdwrMem および useLocalHdwrMem フラグは、デベロッパにオフスクリーングラフィックスを処理するためのさまざまなオプションを提供しますが、アプリケーションに課される追加条件を十分に理解した上で使用する必要があります。

参考文献


ページの先頭に戻る


更新日: 1999 年 10 月 5 日