Direct3D 10 からの変更点
このセクションは、Windows 7 以降、および Windows Server 2008 R2 以降のバージョンの Windows オペレーティング システムにのみ適用されます。
次のセクションでは、Direct3D 11 が Direct3D 10 からどのように変更されたかについて説明します。
カーネル モード サービスへのドライバー コールバック関数
Direct3D バージョン 11 ランタイムがユーザー モード ディスプレイ ドライバーの CreateDevice(D3D10) 関数を呼び出すときに、Direct3D バージョン 11 ランタイムが D3DDDI_DEVICECALLBACKS 構造で提供するデバイス固有のコールバック関数は、ドライバーをカーネル ハンドルとカーネル関数シグネチャから分離します。 Direct3D バージョン 11 ランタイムでは、コールバックのセマンティクスが変更されたため、コールバック関数の実装が変更され、フリースレッド モードの操作がサポートされるようになりました。一方、以前の Direct3D バージョンのランタイムではフリースレッド モードの操作はサポートされていませんでした。 フリー スレッド モード操作の規則は、ドライバーがフリー スレッド モード (D3D11DDICAPS_FR Enterprise Edition THREADED) をサポートしていることを示した後に適用されます。そうでない場合は、以前の制限の多い規則が適用されます。 ドライバーがフリー スレッド モードのサポートを示す方法については、「スレッド化とコマンド リスト」を参照してください。 Direct3D バージョン 11 には、次の制限が引き続き存在します。
HCONTEXT に対して一度に動作できるスレッドは 1 つだけです。 現在 HCONTEXT を使用している既存のコールバック関数は以下の通りです。pfnPresentCb、pfnRenderCb、pfnEscapeCb、pfnDestroyContextCb、pfnWaitForSynchronizationObjectCb、および pfnSignalSynchronizationObjectCb。 したがって、複数のスレッドがこれらのコールバック関数を呼び出し、同じ HCONTEXT を使用する場合、ドライバーはコールバック関数の呼び出しを同期する必要があります。 これらのコールバック関数は、即時コンテキストを操作するスレッドからのみ呼び出される可能性が高いため、この要件を満たすことは非常に自然なことです。
ドライバーは、以下のコールバック関数を、そのドライバー関数を呼び出したのと同じスレッドを使用して、以下のドライバー関数を呼び出している間にのみ呼び出す必要があります。
pfnAllocateCb: ドライバーは、共有リソースの作成時にドライバーの CreateResource(D3D11) 関数を呼び出したスレッドで pfnAllocateCb を呼び出す必要があります。 デバイスとの通常の非共有割り当ては、完全にフリー スレッドです。
pfnPresentCb: ドライバーは、ドライバーの PresentDXGI 関数の呼び出し中にのみ pfnPresentCb を呼び出す必要があります。
pfnSetDisplayModeCb: ドライバーは、ドライバーの SetDisplayModeDXGI 関数の呼び出し中にのみ pfnSetDisplayModeCb を呼び出す必要があります。
pfnRenderCb: ドライバーは、ドライバーの Flush(D3D10) 関数を呼び出したスレッドで pfnRenderCb を呼び出す必要があります。 この制限は、HCONTEXT の制限のため、非常に自然なことです。
pfnDeallocateCb コールバック関数は、ドライバーがほとんどのリソースの種類の DestroyResource(D3D10) 関数から戻る前に pfnDeallocateCb を呼び出す必要がないため、注目に値します。 DestroyResource(D3D10) はフリー スレッド関数であるため、ドライバーは、既存の即時コンテキスト参照が再メイン (つまり、pfnDeallocateCb の前に pfnRenderCb を呼び出す必要があります) をドライバーが効率的に確保できるようになるまで、オブジェクトの破棄を遅延する必要があります。 この制限は、共有リソース、または HRESOURCE を使用して pfnAllocateCb での HRESOURCE の使用を補完する他のコールバック関数にも適用されます。 ただし、この制限はプライマリには適用されません。 プライマリ例外の詳細については、「プライマリ例外」を参照してください。 一部のアプリケーションでは同期破棄の外観が必要になる場合があるため、ドライバーは Flush(D3D10) 関数の呼び出し中に、以前に破棄された共有リソースに対して pfnDeallocateCb を呼び出す必要があります。 また、ドライバーは、Flush(D3D10) 関数の呼び出し中に以前に破棄されたオブジェクト (パイプラインがストールしないオブジェクトのみ) をクリーンアップする必要があります。ドライバーは、このようなメカニズムを必要とする可能性があるいくつかのアプリケーションに対して遅延破棄オブジェクトをクリーンアップするための公式メカニズムとして Flush(D3D10) を呼び出すようにする必要があります。 このメカニズムの詳細については、「遅延破棄と Flush(D3D10)」を参照してください。 また、ドライバーは、ドライバーの DestroyDevice(D3D10) 関数がクリーンアップ中に戻る前に、破棄が遅延されたオブジェクトが完全に破棄されるようにする必要があります。
フリー スレッド DDI の変更を許可する機能の廃止
Direct3D バージョン 11 では、ディスプレイ デバイスの API レベルの概念と即時コンテキストは、ディスプレイ デバイスの従来の概念によって引き続き DDI レベルでバンドルされます。 このディスプレイ デバイスと即時コンテキストのバンドルにより、以前のバージョンの DDI (Direct3D バージョン 10 DDI など) との互換性が最大化され、複数のバージョンの DDI を介して複数のバージョンの API をサポートする場合のドライバーの変更頻度が削減されます。 ただし、このディスプレイ デバイスと即時コンテキストをバンドルすると、スレッドドメインが非常に明示的ではないため、より混乱した DDI になります。 代わりに、複数のインターフェイスのスレッド要件とそれらのインターフェイス内の関数を理解するには、ドライバー開発者がドキュメントを参照する必要があります。
Direct3D バージョン 11 API の主な機能は、複数のスレッドが作成関数と破棄関数を同時に入力できる点です。 このような機能は、D3D10DDI_DEVICEFUNCS および D3D10_1DDI_DEVICEFUNCS で指定された関数の Direct3D バージョン 10 DDI セマンティクスが許可されているため、ドライバーが作成および破棄のために関数テーブル ポインターを交換できるようにする機能との互換性はありません。 そのため、ドライバーは、作成 (CreateDevice(D3D10)) の関数ポインターを渡した後、ドライバーは、Direct3D バージョン 11 DDI の下で実行され、ドライバーが DDI スレッドをサポートしている間に、これらの特定の関数ポインターを変更して動作を変更しないようにする必要があります。 この制限は、pfnCreate、pfnOpen、pfnDestroy、pfnCalcPrivate、および pfnCheck で始まるすべてのデバイス関数に適用されます。 デバイス機能の残りの部分はすべて、即時コンテキストに強く関連付けられています。 1 つのスレッドが一度に即時コンテキストを操作するため、ドライバーが即時コンテキスト関数テーブル エントリをホット スワップできるように、引き続き有効に定義されています。
pfnRenderCb Versus pfnPerformAmortizedProcessingCb
Direct3D バージョン 10 API 関数は、Direct3D ランタイムの pfnRenderCb カーネル コールバック関数をフックして償却処理を実行しました (つまり、API 関数呼び出しごとに特定の操作を実行する代わりに、ドライバーは非常に多くの API 関数呼び出しごとに償却処理を実行しました)。 API は通常この機会を利用して、高基準値をトリミングしたり、遅延オブジェクト破棄キューを一掃したりします。
ドライバーのカーネル コールバック関数を可能な限りフリー スレッドにできるように、ドライバーが Direct3D バージョン 11 DDI をサポートしている場合、Direct3D API は pfnRenderCb を使用しなくなりました。 したがって、Direct3D バージョン 11 DDI をサポートするドライバーは、ドライバーが即時コンテキスト (または同様の頻度) でコマンド バッファーを送信した後、ドライバー DDI 関数に入ったのと同じスレッドから pfnPerformAmortizedProcessingCb カーネル コールバック関数を手動で呼び出す必要があります。 操作では高基準値をトリミングする必要があるため、状態更新 DDI コールバック関数を利用するときに、ドライバーがコマンド バッファー プリアンブルを生成する前に行うと便利です。
さらに、ドライバーは API の償却の問題を認識し、pfnPerformAmortizedProcessingCb カーネル コールバック関数を使用する頻度のバランスを取るようにする必要があります。 極端な場合、ドライバーが過剰処理を引き起こす可能性があります。 たとえば、ドライバーが常に pfnPerformAmortizedProcessingCb を 2 回 (バックツーバック) 呼び出した場合、場合によっては複数エンジンの使用により、pfnPerformAmortizedProcessingCb を 1 回だけ呼び出す方が効率的です。 もう一方の極端な例として、ドライバーが pfnPerformAmortizedProcessingCb を呼び出さなかった場合は、フレーム全体の処理を Direct3D API が許可しない可能性があります。これは、おそらく、交互フレーム レンダリング設計が原因です。 ドライバーは、通常よりも頻繁に pfnPerformAmortizedProcessingCb を呼び出す必要はありません (たとえば、ドライバーが pfnPerformAmortizedProcessingCb を 1 ミリ秒の時間枠で呼び出さなかった場合には、API をポンプする時間である必要があります)。 ドライバーは、pfnPerformAmortizedProcessingCb を伴う必要がある既存の pfnRenderCb 呼び出しのうち、当然ながら、操作のスレッド セマンティクスに準拠する必要があるかどうかを判断する必要があります。
コマンド リストをサポートするドライバーの場合、これらのドライバーは、これらのドライバーが不足するたびに遅延コンテキストから pfnPerformAmortizedProcessingCb を呼び出す必要があります (すべての即時コンテキスト フラッシュと同頻度)。 Direct3D バージョン 11 ランタイムでは、少なくとも、このような操作中に高基準値をトリミングすることが想定されています。 Direct3D バージョン 11 では pfnRenderCb に関連するスレッド セマンティックが緩和されているため、Direct3D バージョン 11 が制限なく pfnRenderCb を引き続きフックできるようにするには、コンカレンシーの問題を解決する必要があります。
新しい DDI エラー コード
D3DDDIERR_APPLICATIONERROR エラー コードは、ドライバーが、Direct3D バージョン 11 の API が行わなかった検証に参加できるようにするように作成されました。 以前は、ドライバーが E_INVALIDARG エラー コードを返した場合、API によって例外が発生していました。 デバッグ レイヤーが存在すると、デバッグ出力が発生し、ドライバーが内部エラーを返したことを示します。 デバッグ出力は、ドライバーにバグがあることを開発者に知らせます。 ドライバーが D3DDDIERR_APPLICATIONERROR を返す場合、デバッグ レイヤーは、代わりに、アプリケーションに問題があると判断します。
フリー スレッド CalcPrivate DDI が遡及的に必要
Direct3D バージョン 11 では、Direct3D バージョン 10 DDI 関数の pfnCalcPrivate で始まるドライバー関数をフリー スレッドにする必要があります。 このさかのぼった要件は、Direct3D バージョン 11 DDI の動作と一致し、ドライバーが DDI スレッド処理をサポートしていないことを示している場合でも、pfnCalcPrivate* 関数と pfnCalcDeferredContextHandleSize 関数をフリー スレッドにする必要があります。 このさかのぼった要件の詳細については、「フリー スレッド CalcPrivate DDI のさかのぼった要件」を参照してください。
遅延破棄と Flush D3D10
すべての破棄関数がフリー スレッドになったため、Direct3D ランタイムは破棄中にコマンド バッファーをフラッシュできません。 したがって、破棄関数は、ドライバーが即時コンテキストを操作するスレッドが存続するためにそのオブジェクトに依存しなくなったことを確認できるまで、オブジェクトの実際の破棄を遅延する必要があります。 個々の即時コンテキストメソッドは、同期を効率的に使用してこの破棄の問題を解決することはできません。そのため、ドライバーは、コマンド バッファーをフラッシュする場合にのみ同期を使用する必要があります。 Direct3D ランタイムは、同様の問題に対処する必要がある場合にも、この同じ設計を使用します。
遅延破棄の批准により、Direct3D ランタイムは、遅延破棄の回避策を許容できないアプリケーションは代わりに明示的なメカニズムを使用することを推奨しています。 そのため、ドライバーは、これらのメカニズムが実際に動作するように、その Flush(D3D10) 関数 (コマンド バッファーが空の場合でも) の呼び出し中に遅延破棄キューを処理する必要があります。
同期破棄の形式を必要とするアプリケーションでは、必要とする破棄の量に応じて、次のいずれかのパターンを使用する必要があります。
アプリケーションがそのオブジェクトに対するすべての依存関係 (コマンド リスト、ビュー、ミドル ウェアなど) を確実に解放した後、アプリケーションは次のパターンを使用します。
Object::Release(); // Final release ImmediateContext::ClearState(); // Remove all ImmediateContext references as well. ImmediateContext::Flush(); // Destroy all objects as quickly as possible.
以下は、より重量の大きい破棄のパターンです。
Object::Release(); // Final release ImmediateContext::ClearState(); // Remove all ImmediateContext references as well. ImmediateContext::Flush(); ImmediateContext::End( EventQuery ); while( S_FALSE == ImmediateContext::GetData( EventQuery ) ) ; ImmediateContext::Flush(); // Destroy all objects, completely.
プライマリの例外
プライマリは、ドライバーの CreateResource(D3D11) 関数の呼び出しでランタイムが作成するリソースです。 ランタイムは、D3D11DDIARG_CREATERESOURCE 構造の pPrimaryDesc メンバーを DXGI_DDI_PRIMARY_DESC 構造への有効なポインターに設定することで、プライマリを作成します。 プライマリには、Direct3D 10 から Direct3D 11 への上記の変更に関して、次の重要な例外があります。
ドライバーのプライマリの CreateResource(D3D11) 関数と DestroyResource (D3D10) 関数はどちらもフリー スレッド化されず、即時コンテキスト スレッド ドメインを共有します。 コンカレンシーは、createResource(D3D11) と DestroyResource(D3D10) を含む pfnCreate および pfnDestroy で始まる関数で引き続き存在できます。 ただし、プライマリには CreateResource(D3D11) と DestroyResource(D3D10) を使用してコンカレンシーを使用することはできません。 たとえば、ドライバーは、CreateResource(D3D11) または DestroyResource(D3D10) 関数の呼び出しがプライマリに対するものであることを検出し、これにより、関数呼び出しの間、即時コンテキスト メモリを安全に使用またはタッチできることを判断できます。
Direct3D ランタイムでプライマリ破棄を遅延することはできません。ドライバーは、ドライバーの DestroyResource(D3D10) 関数の呼び出し内で pfnDeallocateCb 関数を適切に呼び出す必要があります。