PDD_SURFCB_LOCK
コールバックシグネチャ
DWORD PDD_SURFCB_LOCK(
DD_LOCKDATA* param0
);パラメーター
| フィールド | 型 | 説明 |
|---|---|---|
| param0 | DD_LOCKDATA* | ロックダウンの実行に必要な情報を格納する DD_LOCKDATA 構造体を指します。 |
公式ドキュメント
DdLock コールバック関数は、サーフェスメモリの指定された領域をロックし、サーフェスに関連付けられたメモリブロックへの有効なポインターを提供します。
戻り値
DdLock は、次のコールバックコードのいずれかを返します。
解説(Remarks)
ブリットまたはフリップが進行中の場合、DdLock は lpLock にある DD_LOCKDATA 構造体の ddRVal メンバーに DDERR_WASSTILLDRAWING を設定し、DDHAL_DRIVER_HANDLED を返す必要があります。
DD_LOCKDATA の dwFlags メンバーで特に指定されていない限り、ドライバーは DD_LOCKDATA の lpSurfData メンバーにサーフェスの先頭を指すメモリポインターを返すことができます。ドライバーがサーフェスのアドレスを独自に計算する必要がある場合、DD_LOCKDATA の fpProcess メンバーで渡されるポインターを、DirectDraw からアクセス可能なフレームバッファーのユーザーモードマッピングに対するプロセスごとのポインターとして利用できます。
ロックは、要求されたメモリブロックへの排他的なアクセスを提供するものではありません。つまり、複数のスレッドが同じサーフェスを同時にロックできます。ポインターを取得したメモリブロックへのアクセスを同期するのは、アプリケーションの責任です。
NT ベースのオペレーティングシステム上で動作するドライバーは、そのドライバーの DdCreateSurface 関数が PLEASE_ALLOC_USERMEM フラグを使用して事前にそのメモリを割り当てている場合を除き、DdLock 関数からシステムメモリへのポインターを返してはなりません。PLEASE_ALLOC_USERMEM が使用されていない場合、アプリケーションがそのようなメモリにアクセスしようとしたときにエラーを受け取る可能性があります。詳細については、NT カーネルによる DDLOCK_NOSYSLOCK の実装 を参照してください。
DdLock は、無効化された PDEV に対して呼び出されることがあります。PDEV は、ディスプレイドライバーの DrvAssertMode 関数を呼び出すことによって無効または有効になります。詳細については、PDEV の管理 を参照してください。
NT カーネルによる DDLOCK_NOSYSLOCK の実装
アプリケーションは、DirectDraw および Direct3D のアプリケーションプログラミングインターフェイス (API) を使用して、ビデオメモリリソースに対する長時間のロックを取得できます。このようなロックは「NOSYSLOCK」ロックと呼ばれます。これらのロックは、以降の段落で説明するとおり、一般的なビデオメモリのロックとは異なる動作をします。DirectDraw ランタイムが、DD_LOCKDATA の dwFlags メンバーに DDLOCK_NOSYSLOCK フラグを指定してドライバーの DdLock 関数を呼び出した後、ランタイムはドライバーが返したサーフェス内容へのポインターを調べます。ランタイムは、ドライバーが返したポインターをアプリケーションに直接渡す代わりに、ビデオメモリ (ローカルと非ローカルの両方) の 2 つ目のユーザーモードマッピングを作成し、そのマッピング内の等価な仮想アドレスを計算します。この仮想アドレスは、メモリロックに対するエイリアスポインターと呼ばれます。ランタイムはこのエイリアスロックポインターをアプリケーションに渡します。アプリケーションは、このエイリアスロックポインターを使用してビデオメモリに対して直接読み書きします。アプリケーションもドライバーも、異なるロック済みメモリポインターを使用していることを認識しません。
その後、モード切り替え時に、DirectDraw ランタイムは未解決のエイリアスロックポインターを把握します。ランタイムは、一般的なビデオメモリのロックのようにエイリアスロックポインターの完了を待つのではなく、ビデオメモリのユーザーモードマッピングを再マップし、モード切り替えを続行させます。ランタイムはユーザーモードマッピングを 1 つのダミーページに再マップし、アプリケーションは変更に気付かないまま、そのダミーページへの読み書きを続けます。次にランタイムは、ドライバーの DdUnlock 関数を呼び出してエイリアスロックポインターをクリーンアップする必要があります。アプリケーションはもはやビデオメモリに書き込んでいないため、ランタイムはエイリアスロックポインターをクリーンアップできます。このクリーンアップはモード切り替え時に行われるため、シーケンスの次の手順はサーフェスを失うこと、すなわちドライバーのサーフェスごとのオブジェクトを破棄することです。言い換えると、ランタイムは、アプリケーションがロック中と見なし続けているサーフェスも含め、すべてのサーフェスに対してドライバーの DdDestroySurface 関数を呼び出します。実際には、アプリケーションはシステムメモリのダミーページへの読み書きを続けています。
この一連の処理は、DdLock が返すメモリポインターがビデオメモリの何らかのマッピングである場合にのみ機能します。このビデオメモリのマッピングは、DirectDraw のカーネルモードランタイムが行う非ローカルビデオメモリのユーザーモードマッピングか、ドライバーの DdMapMemory 関数が提供するマッピングのいずれかです。メモリポインターがこれらのマッピングのいずれにも帰属しない場合、ランタイムはロックを再マップしません。モード切り替えは続行され、ドライバーのサーフェスオブジェクトは、ドライバーの DdUnlock 関数および DdDestroySurface 関数の呼び出しによって、それぞれロック解除され、破棄されます。その後、ドライバーは通常、ロック時に割り当てたシステムメモリを解放します。アプリケーションはこのメモリに書き込み続けているため、アクセス違反が発生します。
したがって、ドライバーは、そのドライバーの DdCreateSurface 関数が PLEASE_ALLOC_USERMEM フラグを使用して事前にそのメモリを割り当てている場合を除き、DdLock 関数からシステムメモリのポインターを返そうとすべきではありません。この方法で割り当てられたメモリは DirectDraw ランタイムが所有し、アプリケーションがメモリをロック解除するまで、その解放を遅延させることができます。そのため、ドライバーの DdLock 関数は、アプリケーションをクラッシュさせるリスクなしに、この方法で割り当てられたメモリへのポインターを返すことができます。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)