6

私はDirectX10プログラミングにかなり慣れていないので、限られたスキルで次のことを行おうとしています(ただし、OpenGLの経験は豊富です)。

モニターごとに1つずつ、2つの異なるテクスチャクワッドを表示しようとしています。そのためには、単一のD3D10デバイス、複数のスワップチェーン、および単一のVertexBufferが必要であることを理解しました。

私はそれらすべてを作成できると思いますが、それらすべてをどのように処理するかはまだかなりわかりません。複数のID3D10RenderTargetViewが必要ですか?OMSetRenderTargets(...)をどこでどのように使用する必要がありますか?

MSDNを除いて、これらの概念のドキュメントや説明はかなり限られているため、どんな助けでも大歓迎です。これが私が持っているいくつかのコードです:

これがレンダリングコードです

for(int i = 0; i < screenNumber; i++){
    //clear scene
    pD3DDevice->ClearRenderTargetView( pRenderTargetView, D3DXCOLOR(0,1,0,0) );

    //fill vertex buffer with vertices
    UINT numVertices = 4;   
    vertex* v = NULL;   

    //lock vertex buffer for CPU use
    pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &v );

    v[0] = vertex( D3DXVECTOR3(-1,-1,0), D3DXVECTOR4(1,0,0,1), D3DXVECTOR2(0.0f, 1.0f) );
    v[1] = vertex( D3DXVECTOR3(-1,1,0), D3DXVECTOR4(0,1,0,1), D3DXVECTOR2(0.0f, 0.0f) );
    v[2] = vertex( D3DXVECTOR3(1,-1,0), D3DXVECTOR4(0,0,1,1), D3DXVECTOR2(1.0f, 1.0f) );
    v[3] = vertex( D3DXVECTOR3(1,1,0), D3DXVECTOR4(1,1,0,1), D3DXVECTOR2(1.0f, 0.0f) ); 

    pVertexBuffer->Unmap();

    // Set primitive topology 
    pD3DDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

    //set texture
    pTextureSR->SetResource( textureSRV[textureIndex] );

    //get technique desc
    D3D10_TECHNIQUE_DESC techDesc;
    pBasicTechnique->GetDesc( &techDesc );


    // This is where you actually use the shader code
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        //apply technique
        pBasicTechnique->GetPassByIndex( p )->Apply( 0 );

        //draw
        pD3DDevice->Draw( numVertices, 0 );
    }

    //flip buffers
    pSwapChain[i]->Present(0,0);
}

そして、これがレンダリングターゲットを作成するためのコードですが、これが良いかどうかはわかりません

for(int i = 0; i < screenNumber; ++i){

    //try to get the back buffer
    ID3D10Texture2D* pBackBuffer;   
    if ( FAILED( pSwapChain[1]->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer) ) ) return fatalError("Could not get back buffer");

    //try to create render target view
    if ( FAILED( pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView) ) ) return fatalError("Could not create render target view");

    pBackBuffer->Release();
    pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL);
}

return true;

}

4

1 に答える 1

33

私はあなたがやりたいことの要点を理解したことを願っています-出力をそれらのモニターにマッピングする単一のグラフィックカード(グラフィックアダプター)を使用しながら、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
    }
}

もちろん、ウィンドウごとにすべてのスワップチェーンとスワップバッファを実行することを忘れないでください。ここでのコードは、この回答を目的としたものです。好みの方法で機能させるには、もう少し作業、エラーチェック(フェイルセーフ)、および熟考が必要です。つまり、概要を簡略化する必要があります。生産ソリューション。

幸運と幸せなコーディング!シーシュ、これは巨大です。

于 2012-05-25T03:28:02.307 に答える