私はあなたがやりたいことの要点を理解したことを願っています-出力をそれらのモニターにマッピングする単一のグラフィックカード(グラフィックアダプター)を使用しながら、2つの異なるモニターで異なるコンテンツをレンダリングします。そのためには、1つのデバイス(単一のグラフィックカード/アダプター用)が必要であり、ユーザーのマシンにある出力の数を列挙します。
つまり、合計で、つまり1つのデバイス、2つの出力、2つのウィンドウ、つまり2つのスワップチェーンを意味します。
これが私の小さな実験の簡単な結果です:
ちょっとした紹介
DirectX 10+では、これはDXGI(DirectX Graphics Infrastructure)に分類されます。これは、DirectX 10+の開発に関連する一般的な低レベルのロジスティクスを管理します。これは、おそらくご存知のとおり、機能セットなどを列挙するという古い要件を捨てました。 APIで定義されたすべての機能を共有するDX10+対応カード。変化するのは、カードの範囲と機能だけです(つまり、アプリがクラッシュして焼けるよりも、パフォーマンスが悪い方が望ましいです)。これはすべて過去にDirectX9に含まれていましたが、Microsoftの人々はそれを引き出してDXGIと呼ぶことにしました。これで、DXGI機能を使用してマルチモニター環境をセットアップできます。
複数のID3D10RenderTargetViewが必要ですか?
はい、複数のレンダーターゲットビューが必要です。カウントは(スワップチェーンやウィンドウなど)使用しているモニターの数によって異なります。しかし、言葉を吐き出すのを防ぐために、できるだけ簡単に、必要な場所に追加情報を書きましょう。
- システムで使用可能なすべてのアダプターを列挙します。
- アダプターごとに、使用可能な(およびアクティブな)すべての出力を列挙し、それに付随するデバイスを作成します。
- 列挙されたデータを適切な構造(サイズ情報をすばやく放棄できる配列を考えてください)に格納し、それを使用してn個のウィンドウを作成し、チェーンを交換し、ターゲットビュー、深度/ステンシルテクスチャ、およびそれぞれのビューをレンダリングします。ここで、nは出力。
- すべてが作成されたら、レンダリングするウィンドウごとに、利用可能なジオメトリ(およびその他の)データを使用して結果を出力する特別なルーチンを定義できます。これは、各モニターがフルスクリーンで取得するものに解決されます(調整することを忘れないでください)それに応じてすべてのウィンドウのビューポート)。
- それぞれのウィンドウにリンクされているすべてのスワップチェーンを反復処理してデータを提示し、Present()を使用してバッファをスワップします。
さて、これは単語数が豊富ですが、いくつかのコードはもっと価値があります。これは、単純なマルチモニターアプリケーションの実装に何が入るのかを大まかに把握できるように設計されています。したがって、アダプターは1つだけ(最近はかなり大胆なステートメント)、複数の出力があり、フェイルセーフはないと想定されています。楽しい部分はお任せします。2番目の質問への答えは階下です...
関係するメモリ管理がないことに注意してください。説明の目的で必要ない場合は、すべてが魔法のようにクリーンアップされると想定しています。良い思い出の市民になりましょう。
アダプターの入手
IDXGIAdapter* adapter = NULL;
void GetAdapter() // applicable for multiple ones with little effort
{
// remember, we assume there's only one adapter (example purposes)
for( int i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters( i, &adapter ); ++i )
{
// get the description of the adapter, assuming no failure
DXGI_ADAPTER_DESC adapterDesc;
HRESULT hr = adapter->GetDesc( &adapterDesc );
// Getting the outputs active on our adapter
EnumOutputsOnAdapter();
}
アダプターの出力を取得する
std::vector<IDXGIOutput*> outputArray; // contains outputs per adapter
void EnumOutputsOnAdapter()
{
IDXGIOutput* output = NULL;
for(int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i)
{
// get the description
DXGI_OUTPUT_DESC outputDesc;
HRESULT hr = output->GetDesc( &outputDesc );
outputArray.push_back( output );
}
}
ここで、少なくともWin32 APIの考慮事項、ウィンドウクラスの作成、システムへの登録、ウィンドウの作成などを知っていると想定する必要があります。したがって、その作成を限定するのではなく、複数にどのように関係するかを詳しく説明します。ウィンドウズ。また、ここではフルスクリーンの場合のみを検討しますが、ウィンドウモードで作成することは可能以上に簡単です。
出力用の実際のウィンドウを作成する
アダプターが1つだけ存在すると想定しているため、その特定のアダプターにリンクされている列挙された出力のみを考慮します。すべてのウィンドウデータをきちんとした小さな構造に整理することが望ましいですが、この回答の目的のために、それらを単純な構造体に、次にさらに別のstd :: vectorオブジェクトに押し込みます。つまり、ハンドルをそれぞれのウィンドウ(HWND)とそのサイズ(ただし、この場合は一定です)。
ただし、それでも、ウィンドウごとに1つのスワップチェーン、1つのレンダリングターゲットビュー、1つの深度/ステンシルビューがあるという事実に対処する必要があります。では、各ウィンドウを説明する小さな構造体でそのすべてをフィードしてみませんか?理にかなっていますよね?
struct WindowDataContainer
{
//Direct3D 10 stuff per window data
IDXGISwapChain* swapChain;
ID3D10RenderTargetView* renderTargetView;
ID3D10DepthStencilView* depthStencilView;
// window goodies
HWND hWnd;
int width;
int height;
}
良い。まあ、そうではありません。しかし、それでも...先に進みます!次に、出力用のウィンドウを作成します。
std::vector<WindowDataContainer*> windowsArray;
void CreateWindowsForOutputs()
{
for( int i = 0; i < outputArray.size(); ++i )
{
IDXGIOutput* output = outputArray.at(i);
DXGI_OUTPUT_DESC outputDesc;
p_Output->GetDesc( &outputDesc );
int x = outputDesc.DesktopCoordinates.left;
int y = outputDesc.DesktopCoordinates.top;
int width = outputDesc.DesktopCoordinates.right - x;
int height = outputDesc.DesktopCoordinates.bottom - y;
// Don't forget to clean this up. And all D3D COM objects.
WindowDataContainer* window = new WindowDataContainer;
window->hWnd = CreateWindow( windowClassName,
windowName,
WS_POPUP,
x,
y,
width,
height,
NULL,
0,
instance,
NULL );
// show the window
ShowWindow( window->hWnd, SW_SHOWDEFAULT );
// set width and height
window->width = width;
window->height = height;
// shove it in the std::vector
windowsArray.push_back( window );
//if first window, associate it with DXGI so it can jump in
// when there is something of interest in the message queue
// think fullscreen mode switches etc. MSDN for more info.
if(i == 0)
factory->MakeWindowAssociation( window->hWnd, 0 );
}
}
かわいい、これで完了です。アダプターは1つしかなく、それに付随するデバイスも1つしかないため、通常どおりに作成します。私の場合、それはどこからでもアクセスできる単なるグローバルインターフェイスポインタです。ここでは今年のコードを取り上げないので、なぜ地獄ではないのですか?
スワップチェーン、ビュー、および深度/ステンシル2Dテクスチャの作成
さて、私たちのフレンドリーなスワップチェーン...あなたは「裸の」機能を呼び出すことによって実際にそれらを作成することに慣れているかもしれませんD3D10CreateDeviceAndSwapChain(...)
が、あなたが知っているように、私たちはすでに私たちのデバイスを作りました。1つだけ欲しいです。そして複数のスワップチェーン。まあ、それはピクルスです。幸いなことに、DXGIFactoryインターフェースの生産ラインには、ラム酒の補完的な樽で無料で受け取ることができるスワップチェーンがあります。次に、スワップチェーンに、ウィンドウごとに1つ作成します。
void CreateSwapChainsAndViews()
{
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// get the dxgi device
IDXGIDevice* DXGIDevice = NULL;
device->QueryInterface( IID_IDXGIDevice, ( void** )&DXGIDevice ); // COM stuff, hopefully you are familiar
// create a swap chain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
// fill it in
HRESULT hr = factory->CreateSwapChain( DXGIDevice, &swapChainDesc, &p_Window->swapChain );
DXGIDevice->Release();
DXGIDevice = NULL;
// get the backbuffer
ID3D10Texture2D* backBuffer = NULL;
hr = window->swapChain->GetBuffer( 0, IID_ID3D10Texture2D, ( void** )&backBuffer );
// get the backbuffer desc
D3D10_TEXTURE2D_DESC backBufferDesc;
backBuffer->GetDesc( &backBufferDesc );
// create the render target view
D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
// fill it in
device->CreateRenderTargetView( backBuffer, &RTVDesc, &window->renderTargetView );
backBuffer->Release();
backBuffer = NULL;
// Create depth stencil texture
ID3D10Texture2D* depthStencil = NULL;
D3D10_TEXTURE2D_DESC descDepth;
// fill it in
device->CreateTexture2D( &descDepth, NULL, &depthStencil );
// Create the depth stencil view
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
// fill it in
device->CreateDepthStencilView( depthStencil, &descDSV, &window->depthStencilView );
}
}
これで、必要なものがすべて揃いました。あなたがする必要があるのは、すべてのウィンドウを反復し、さまざまなものを適切に描画する関数を定義することです。
OMSetRenderTargets(...)をどこでどのように使用する必要がありますか?
すべてのウィンドウを反復処理し、適切なレンダリングターゲットを使用する上記の関数(ウィンドウごとのデータコンテナの提供):
void MultiRender( )
{
// Clear them all
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// There is the answer to your second question:
device->OMSetRenderTargets( 1, &window->renderTargetView, window->depthStencilView );
// Don't forget to adjust the viewport, in fullscreen it's not important...
D3D10_VIEWPORT Viewport;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = window->width;
Viewport.Height = window->height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
device->RSSetViewports( 1, &Viewport );
// TO DO: AMAZING STUFF PER WINDOW
}
}
もちろん、ウィンドウごとにすべてのスワップチェーンとスワップバッファを実行することを忘れないでください。ここでのコードは、この回答を目的としたものです。好みの方法で機能させるには、もう少し作業、エラーチェック(フェイルセーフ)、および熟考が必要です。つまり、概要を簡略化する必要があります。生産ソリューション。
幸運と幸せなコーディング!シーシュ、これは巨大です。