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 日
|