Direct3D DXGI とマルチ GPU (アダプタ)

PC を入れ替えて複数のビデオカードを同時に使えるようになりました。
対応マザーとそれなりの電源が必要です。
いろいろ試しています。

マルチ GPU といっても SLI や CrossFire のことではなく、
それぞれ独立した GPU として使います。

例えば GeForce と RADEON の 2枚差しで、それぞれにモニタをつないで
マルチモニタのように使用することができます。

・GeForce GTX260 → モニタ1
・RADEON HD4670 → モニタ2

GeForce は モニタ1 の描画を行い、RADEON は モニタ2 へ出力を行っています。
Windows から使っている分には 1枚のビデオカードに複数モニタをつないだ状態と
何も変わりません。
広いデスクトップとして使用可能で双方にまたがったウィンドウも配置できます。

ここまでは理解できますが Direct3D を使った場合はどうなるでしょうか。
D3D10CreateDevice / D3D11CreateDevice 時に PC につながっている任意のアダプタ
(GPU)を与えることが可能です。
どちらの GPU でも同じように使えるし、どちらのモニタにも描画することができました。
行き来もできるし中間に配置しても動いています。

このあたりをコントロールするのが DXGI で、非常に柔軟な使い方が可能となっています。

・IDXGIAdapter = GPU
・IDXGIOutput = モニタ

現在利用可能なアダプタとモニタは

IDXGIFactory::EnumAdapters()
IDXGIAdapter::EnumOutputs()

で列挙可能です。モニタに接続されていないアダプタも描画に使うことができます。
DXGI1.1 ではこれにさらに Command Remoting が加わります。
リモートデスクトップでアクセスしている場合に、ホストとクライアントどちらの
アダプタでレンダリングするか選択できるわけです。

●サンプルで確認

DirectX SDK 付属の Direct3D 11 のサンプルプログラムを起動するとウィンドウに
アダプタ名 (GPU名) が表示されています。
モニタ間 (アダプタ間) を行き来するとアダプタ名が切り替わるので違いがよくわかります。
プログラム的にはわざわざアダプタを切り替える必要は無いのですが、
DXUT では敢えてこのような仕様になっているようです。(理由は後述)

●プログラムで確認

実際にプログラムを書いて任意のアダプタで動かしてみました。

・RADEON HD4850 → モニタ1
・RADEON HD4670 → モニタ2

IDXGIFactory::EnumAdapters() でアダプタを列挙して、任意のアダプタを使って
デバイスを作成します。

// 列挙
IDXGIAdapter1*	iAdapter= NULL;
IDXGIFactory1*	iFactory= NULL;
CreateDXGIFactory1( __uuidof(IDXGIFactory1), reinterpret_cast( &iFactory ) );
for( unsigned int index= 0 ;; index++ ){
	HRESULT	ret= iFactory->EnumAdapters1( index, &iAdapter );
	if( ret == DXGI_ERROR_NOT_FOUND ){
		break;
	}
	// ~ アダプタの選択
	// iAdapter->Release();
}
iFactory->Release();


// 作成
HRESULT	hr= D3D11CreateDeviceAndSwapChain(
		iAdapter,
		iAdapter ? D3D_DRIVER_TYPE_UNKNOWN : DriverType,
		NULL,	// software
		D3D11_CREATE_DEVICE_DEBUG,// flags
		NULL,	// featurelevels
		0,	// featurelevels
		D3D11_SDK_VERSION,
		&SwapChainDesc,
		&iSwapChain,
		&iDevice,
		&FeatureLevel,
		&iContext
	);

アダプタは IDXGIFactory::EnumAdapters() で列挙したハードウエアアダプタ、
もしくは IDXGIFactory::CreateSoftwareAdapter() で作成したソフトウエアアダプタです。
すでに TYPE を特定できるので、DriverType には D3D_DRIVER_TYPE_UNKNOWN を
与えなければなりません。(最初ここではまりました)

結果

RADEON HD4850 → モニタ1  : 257fps
RADEON HD4850 → モニタ2  : 205fps

RADEON HD4670 → モニタ1  : 140fps
RADEON HD4670 → モニタ2  : 164fps

HD4850 ではモニタ1 の方が高速、HD4670 ではモニタ2 の方が高速です。
アダプタから直接出力した方が速く、予想通りの結果といえます。
同時にたとえ直結されていなくても、モニタ2 の描画も HD4850 が行った方が速いこともわかります。

DirectX SDK 付属サンプルの DXUT がウィンドウの位置を監視して、モニタに応じて
デバイスを作り直しているのは少しでも高速に動作するためだと考えられます。

昔はビデオカードを何度も何度も差し直して開発していました。
GeForce と RADEON の挙動を同時に確認できるなんて、大変便利になったものです。
上のテストの最中、途中で GeForce GTX260(192sp) から HD4850 に差し直したのは
本日非常に暑かったからです。

関連エントリ
Windows7 リモートデスクトップと Direct3D