コンテキスト ローカル DDI ハンドルの使用
このセクションは、Windows 7 以降、および Windows Server 2008 R2 以降のバージョンの Windows オペレーティング システムのみを対象としています。
各オブジェクト (リソース、シェーダーなど) には、コンテキストローカル DDI ハンドルがあります。
オブジェクトが 3 つの遅延コンテキストで使用されるとします。 この状況では、4 つのハンドルが同じオブジェクトを参照します (遅延コンテキストごとに 1 つのハンドル、即時コンテキスト用にもう 1 つのハンドル)。 各コンテキストはスレッドによって同時に操作できるため、コンテキスト ローカル ハンドルにより、複数の CPU スレッドが (意図的にまたは意図せずに) 類似のメモリを競合しないようにします。 コンテキスト ローカル ハンドルも直感的です。ドライバーは、コンテキストごとに論理的に関連付けられているこのデータの多くを変更する必要があるためです (たとえば、オブジェクトはコンテキストによってバインドされる可能性があります)。
即時コンテキスト ハンドルと遅延コンテキスト ハンドルの違いはまだあります。 特に、即時コンテキスト ハンドルは、割り当てられる最初のハンドルと破棄される最後のハンドルであることが保証されます。 対応する即時コンテキスト ハンドルは、各遅延コンテキスト ハンドルを「開く」間に提供され、それらをリンクします。 現在、デバイスごとの DDI ハンドルを持つオブジェクトの概念はありません (つまり、直前のコンテキスト ハンドルの前に作成され、破棄され、コンテキスト ハンドルの作成によってのみ参照されるハンドル)。
一部のハンドルには、他のハンドルとの依存関係があります (たとえば、ビューは対応するリソースに依存しています)。 即時コンテキストに対して存在する作成と破棄の順序付け保証は、遅延コンテキスト ハンドルにも拡張されます (つまり、ランタイムはそのリソースに対してコンテキスト ローカル ビュー ハンドルを作成する前にコンテキスト ローカル リソース ハンドルを作成し、ランタイムはそのリソースに対するすべてのコンテキスト ローカル ビュー ハンドルを破棄した後にコンテキスト ローカル リソース ハンドルを破棄します)。 ランタイムがコンテキスト ローカル ハンドルを作成すると、ランタイムは対応するコンテキスト ローカル依存関係ハンドルも提供します。
ドライバー データ編成
注意が必要なドライバー データの編成には多少の懸念があります。 Direct3D バージョン 10 と同様に、データの適切なローカリティにより、API とドライバーの間のキャッシュ ミスを減らすことができます。 また、データの適切なローカリティによってキャッシュスラッシングが回避される可能性があります。これは、頻繁にアクセスされる複数のデータがすべて同じキャッシュ インデックスに解決され、キャッシュの連想的な部分が使い果たされたときに発生します。 DDI は Direct3D バージョン 10 以降で設計されており、ドライバーがハンドルを満たすために必要なメモリ量とハンドルの値を割り当てる API を API に通知することで、このような問題が発生するのを防ぐことができます。 ただし、スレッド関連の新しい懸念は、Direct3D バージョン 11 の時間枠の DDI 設計に影響します。
当然ながら、コンテキスト ローカル ハンドルは、スレッド間の競合の問題を回避するコンテキストごとにオブジェクト データを関連付ける方法を提供します。 ただし、このようなデータは遅延コンテキストごとにレプリケートされるため、このようなデータのサイズは大きな懸念事項です。 これは、即時コンテキスト ハンドルと遅延コンテキスト ハンドルの間で読み取り専用データを共有する自然な合理化を提供します。 遅延コンテキスト ハンドルの作成時に、ハンドル間の接続を確立するために即時コンテキスト ハンドルが提供されます。 ただし、遅延コンテキスト ハンドルから離れたデータは API データの局所性のメリットを得ることができ、読み取り専用データに対する間接参照レベルが追加されるので、局所性のメリットが読み取り専用データに拡張されなくなります。 局所性のメリットによってデータの重複が正当化される場合、一部の読み取り専用データを各コンテキスト ハンドル リージョンにレプリケートできます。 ただし、各遅延コンテキスト ハンドルをバックアップするメモリについては、そのデータが比較的大きく、他のデータほど頻繁にアクセスされない場合は、ハンドルから隣接していないデータを再配置する価値があるかどうかを考慮する必要があります。 理想的には、各遅延コンテキスト ハンドルに関連付けられているデータの型は、とにかくすべての頻度の高いデータになります。したがって、データは再配置が必要と考えられるほど大きくはありません。 当然ながら、ドライバーはこれらの競合する動機のバランスを取る必要があります。
ドライバー のデータ設計を Direct3D バージョン 10 と効率的に互換性を持たせるために、実装では違いはありませんが、読み取り専用データは、即時コンテキスト ハンドル データと連続して配置する必要があります (ただし、その後も分離されます)。 ドライバーがこの設計を使用する場合、ドライバーは、即時コンテキスト ハンドル データと読み取り専用データの間にキャッシュ ラインパディングが必要であることを認識する必要があります。 スレッドは各コンテキストハンドルのデータを頻繁に操作する可能性があるため (同時に処理しない場合)、即時コンテキスト ハンドル データと遅延コンテキスト ハンドル データの間で誤共有のペナルティが発生します (キャッシュ ラインパディングが使用されていない場合)。 ドライバーの設計は、ポインターが確立され、コンテキスト ハンドルメモリ領域間で定期的に走査される場合に現れる誤共有ペナルティを認識する必要があります。
Direct3D ランタイムは、遅延コンテキスト ローカル ハンドルに次の Direct3D 11 DDI を使用します。
CheckDeferredContextHandleSizes 関数は、遅延コンテキスト ハンドルのハンドル データを保持するドライバープライベート メモリ空間のサイズを検証します。
CalcDeferredContextHandleSize 関数は、遅延コンテキストのメモリ領域のサイズを決定します。
Direct3D ランタイムがドライバーに必要な遅延コンテキスト ハンドル サイズを取得するには、上記の DDI 関数を使用する必要があります。 即時コンテキストのオブジェクトの作成直後に、ランタイムは CalcDeferredContextHandleSize を呼び出して、ドライバーがこのオブジェクトへの遅延コンテキスト ハンドルを満たすために必要な記憶域空間の量をドライバーにクエリします。 ただし、Direct3D API は、アクセスされる一意のハンドル サイズとその値の数を決定することによって、CLS メモリ アロケーターを調整する必要があります。ランタイムは、ドライバーの CheckDeferredContextHandleSizes 関数を呼び出して、この情報を取得します。 そのため、デバイスのインスタンス化中に、API は二重ポーリングによって遅延コンテキスト ハンドル サイズの配列を要求します。 1 回目のポーリングでは、返されるサイズの数を要求し、2 回目のポーリングは各サイズの値を取得するために配列を渡します。 ドライバーは、ハンドルの種類とともにハンドルを満たすために必要なメモリ量を示す必要があります。 ドライバーは、特定のハンドルの種類に関連付けられている複数のサイズを返すことができます。 ただし、ドライバーが、CalcDeferredContextHandleSize 配列で対応して返されなかった値を CheckDeferredContextHandleSizes から返すことはありません。
DDI ハンドルの作成については、遅延コンテキストの create メソッドが使用されます。 たとえば、CreateBlendState(D3D10_1) 関数と DestroyBlendState 関数を調べます。 HDEVICE は、適切な遅延コンテキスト (または即時コンテキスト) を自然に指し示します。他の CONST 構造体ポインターは NULL (オブジェクトに依存関係がないという想定) であり、D3D10DDI_HRT* ハンドルは、対応する即時コンテキスト オブジェクトへの D3D10DDI_H* ハンドルです。
依存関係を持つオブジェクト (たとえば、ビューに対応するリソースに依存関係がある) の場合、依存関係ハンドルを提供する構造体ポインターは NULL ではありません。 ただし、構造体の唯一の有効なメンバーは依存関係ハンドルであり、残りのメンバーは 0 で満たされます。 たとえば、ランタイムが遅延コンテキストでこの関数を呼び出した場合、D3D11DDIARG_CREATESHADERRESOURCEVIEW ポインターはドライバーの CreateShaderResourceView(D3D11 関数の呼び出しで NULL になりません。 この CreateShaderResourceView(D3D11) 呼び出しでは、ランタイムはリソースの適切なコンテキスト ローカル ハンドルを D3D11DDIARG_CREATESHADERRESOURCEVIEW の hDrvResource メンバーに割り当てます。 ただし、D3D11DDIARG_CREATESHADERRESOURCEVIEW の残りのメンバーは 0 で埋められます。
次のコード例は、Direct3D ランタイムがアプリケーションの作成要求を変換する方法と、遅延コンテキストをユーザー モードディスプレイ ドライバーの呼び出しに最初に使用して、即時または遅延コンテキストを作成する方法を示しています。 アプリケーションの ID3D11Device::CreateTexture2D への呼び出しは、次の 「リソース作成」セクションでランタイム コードを開始します。 アプリケーションのID3D11Device::CopyResource への呼び出しは、次の 「遅延コンテキスト リソース使用」セクションでランタイム コードを開始します。
// Device Create
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, NULL );
pArray = malloc( u * ... );
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, pArray );
// Resource Create
s = IC::pfnCalcPrivateResourceSize( hIC, &Args );
pICRHandle = malloc( s );
IC::pfnCreateResource( hIC, &Args, pICRHandle, hRTResource );
s2 = IC::pfnCalcDeferredContextHandleSize( hIC, D3D10DDI_HT_RESOURCE, pICRHandle );
// Deferred Context Resource Usage
pDCRHandle = malloc( s2 );
DC::pfnCreateResource( hDC, NULL, pDCRHandle, pICRHandle );
pfnSetErrorCb に関する問題
どの create 関数もエラー コードを返さないという仕様であり、これは Direct3D バージョン 11 スレッド モデルに最適でした。 すべての create 関数は、pfnSetErrorCb を使用してドライバーからエラー コードを取得します。 Direct3D バージョン 10 ドライバー モデルとの互換性を最大限に高めるために、エラー コードを返す新しい DDI create 関数は導入されませんでした。 代わりに、ドライバーは create 関数の実行中に pfnSetErrorCb で統合デバイス/即時コンテキスト D3D10DDI_HRTCORELAYER ハンドルを引き続き使用する必要があります。 ドライバーがコマンド リストをサポートしている場合、ドライバーは、対応するコンテキストに関連付けられている適切な pfnSetErrorCb を使用する必要があります。 つまり、遅延コンテキスト エラーは、対応するハンドルを使用するなどして pfnSetErrorCb への特定の遅延コンテキスト呼び出しに移動する必要があります。
遅延コンテキストは、DDI 関数の呼び出しごとに永続的に拡張されるので、DDI 関数から pfnSetErrorCb の呼び出しを介して E_OUTOFMEMORY を返すことができ、以前はこうした DDI 関数では D3DDDIERR_DEVICEREMOVED (Draw、SetBlendState など) のみが許可されました。 Direct3D API は、ローカル コンテキストの削除をトリガーして、ドライバーがこのようなエラーケースを支援し、部分的に構築されたコマンド リストを効果的に取り出します。 アプリケーションは、コマンド リストを記録していることを引き続き判断します。ただし、アプリケーションが最終的に FinishCommandList 関数を呼び出すと、FinishCommandList は E_OUTOFMEMORY のエラー コードを返します。