0

レイヤー化されたテクスチャの束をレンダリングする効率的な方法は何ですか? 3D 空間にランダムに配置し、後ろから前にレンダリングする半透明のテクスチャ付きの長方形がいくつかあります。

現在、d3dContext->PSSetShaderResources() を呼び出して、d3dContext->DrawIndexed() を呼び出すたびにピクセル シェーダーに新しいテクスチャをフィードします。各描画の前にテクスチャを GPU メモリにコピーしているように感じます。それぞれ約 1024x1024 ピクセルの 10 ~ 30 個の ARGB テクスチャがあり、それらは画面にレンダリングする 100 ~ 200 個の長方形に関連付けられています。私の FPS は 100 で問題ありませんが、200 あたりでかなり悪くなります。これは私の最初のやや深刻な D3D コードであるため、他の場所で非効率性がある可能性がありますが、これはテクスチャを前後にコピーすることに関係しているのではないかと強く疑っています。30*1024*1024*4 は 120MB であり、Windows 8 デバイスを対象とする Metro スタイル アプリには少し大きい値です。だから、それらすべてをそこに入れるのは一苦労かもしれませんが、何とかして少なくともいくつかをキャッシュすることはできますか? 何か案は?

*編集 - いくつかのコード スニペットが追加されました

コンスタントバッファ

struct ModelViewProjectionConstantBuffer
{
    DirectX::XMMATRIX model;
    DirectX::XMMATRIX view;
    DirectX::XMMATRIX projection;
    float opacity;
    float3 highlight;
    float3 shadow;
    float textureTransitionAmount;
};

レンダリング方法

void RectangleRenderer::Render()
{
    // Clear background and depth stencil
    const float backgroundColorRGBA[] = { 0.35f, 0.35f, 0.85f, 1.000f };
    m_d3dContext->ClearRenderTargetView(
        m_renderTargetView.Get(),
        backgroundColorRGBA
        );

    m_d3dContext->ClearDepthStencilView(
        m_depthStencilView.Get(),
        D3D11_CLEAR_DEPTH,
        1.0f,
        0
        );

    // Don't draw anything else until all textures are loaded
    if (!m_loadingComplete)
        return;

    m_d3dContext->OMSetRenderTargets(
        1,
        m_renderTargetView.GetAddressOf(),
        m_depthStencilView.Get()
        );

    UINT stride = sizeof(BasicVertex);
    UINT offset = 0;

    // The vertext buffer only has 4 vertices of a rectangle
    m_d3dContext->IASetVertexBuffers(
        0,
        1,
        m_vertexBuffer.GetAddressOf(),
        &stride,
        &offset
        );

    // The index buffer only has 4 vertices
    m_d3dContext->IASetIndexBuffer(
        m_indexBuffer.Get(),
        DXGI_FORMAT_R16_UINT,
        0
        );

    m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    m_d3dContext->IASetInputLayout(m_inputLayout.Get());

    FLOAT blendFactors[4] = { 0, };
    m_d3dContext->OMSetBlendState(m_blendState.Get(), blendFactors, 0xffffffff);

    m_d3dContext->VSSetShader(
        m_vertexShader.Get(),
        nullptr,
        0
        );

    m_d3dContext->PSSetShader(
        m_pixelShader.Get(),
        nullptr,
        0
        );

    m_d3dContext->PSSetSamplers(
        0,                          // starting at the first sampler slot
        1,                          // set one sampler binding
        m_sampler.GetAddressOf()
        );

    // number of rectangles is in the 100-200 range
    for (int i = 0; i < m_rectangles.size(); i++)
    {
        // start rendering from the farthest rectangle
        int j = (i + m_farthestRectangle) % m_rectangles.size();

        m_vsConstantBufferData.model = m_rectangles[j].transform;
        m_vsConstantBufferData.opacity = m_rectangles[j].Opacity;
        m_vsConstantBufferData.highlight = m_rectangles[j].Highlight;
        m_vsConstantBufferData.shadow = m_rectangles[j].Shadow;
        m_vsConstantBufferData.textureTransitionAmount = m_rectangles[j].textureTransitionAmount;


        m_d3dContext->UpdateSubresource(
            m_vsConstantBuffer.Get(),
            0,
            NULL,
            &m_vsConstantBufferData,
            0,
            0
            );

        m_d3dContext->VSSetConstantBuffers(
            0,
            1,
            m_vsConstantBuffer.GetAddressOf()
            );

        m_d3dContext->PSSetConstantBuffers(
            0,
            1,
            m_vsConstantBuffer.GetAddressOf()
            );

        auto a = m_rectangles[j].textureId;
        auto b = m_rectangles[j].targetTextureId;
        auto srv1 = m_textures[m_rectangles[j].textureId].textureSRV.GetAddressOf();
        auto srv2 = m_textures[m_rectangles[j].targetTextureId].textureSRV.GetAddressOf();
        ID3D11ShaderResourceView* srvs[2];
        srvs[0] = *srv1;
        srvs[1] = *srv2;

        m_d3dContext->PSSetShaderResources(
            0,                          // starting at the first shader resource slot
            2,                          // set one shader resource binding
            srvs
            );

        m_d3dContext->DrawIndexed(
            m_indexCount,
            0,
            0
            );
    }
}

ピクセル シェーダー

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
    matrix model;
    matrix view;
    matrix projection;
    float opacity;
    float3 highlight;
    float3 shadow;
    float textureTransitionAmount;
};

Texture2D baseTexture : register(t0);
Texture2D targetTexture : register(t1);
SamplerState simpleSampler : register(s0);

struct PixelShaderInput
{
    float4 pos : SV_POSITION;
    float3 norm : NORMAL;
    float2 tex : TEXCOORD0;
};

float4 main(PixelShaderInput input) : SV_TARGET
{
    float3 lightDirection = normalize(float3(0, 0, -1));

    float4 baseTexelColor = baseTexture.Sample(simpleSampler, input.tex);
    float4 targetTexelColor = targetTexture.Sample(simpleSampler, input.tex);
    float4 texelColor = lerp(baseTexelColor, targetTexelColor, textureTransitionAmount);
    float4 shadedColor;
    shadedColor.rgb = lerp(shadow.rgb, highlight.rgb, texelColor.r);
    shadedColor.a = texelColor.a * opacity;
    return shadedColor;
}
4

2 に答える 2

2

Jeremiah が示唆しているように、フレームごとに新しいテクスチャを作成するか、「UpdateSubresource」または「Map/UnMap」メソッドを使用する必要があるため、おそらくフレームごとにテクスチャを CPU から GPU に移動することはありません。

ポリゴンの数が非常に少ないため、インスタンス化がこの特定のケースに役立つとは思いません (数百万のポリゴンで心配し始めるでしょう)。多くのテクスチャ サンプリング/ブレンドを実行しているため、アプリケーションの帯域幅/フィルレートが制限される可能性が高くなります (テクスチャ フィルレート、ピクセル フィルレート、および GPU のROPの数によって異なります)。

パフォーマンスを向上させるために、次のことを強くお勧めします。

  • すべてのテクスチャにすべてのミップマップが生成されていることを確認してください。ミップマップがない場合、GPU のキャッシュに多くの損害を与えます。(また、HLSL でtexture.Sampleメソッドを使用しており、texture.SampleLevel またはバリアントではないと仮定します)
  • GPU でDirect3D11 ブロック圧縮テクスチャを使用するには、texconv.exe などのツールまたはできれば「Windows DirectX 11 Texture Converter」のサンプルを使用します。

ちなみに、 https://gamedev.stackexchange.com/でこの種の質問をすると、おそらくもっと注目されるでしょう。

于 2012-06-02T02:52:44.747 に答える
1

GPU からシステム メモリへのコピーを行ったり来たりしているとは思いません。通常は、明示的に Map(...) を呼び出すか、システム メモリに作成したテクスチャにブリットする必要があります。

1 つの問題は、テクスチャごとに DrawIndexed(...) 呼び出しを行っていることです。GPU は、バッチ処理によって大量の作業を送信すると、最も効率的に動作します。これを実現する 1 つの方法は、n 個のテクスチャを PSSetShaderResources(i, ...) に設定し、DrawIndexedInstanced(...) を実行することです。次に、シェーダー コードは各シェーダー リソースを読み取り、それらを描画します。これは、こちらの C++ DirectCanvas コード(SpriteInstanced.cpp) で行います。これは多くのコードを作成する可能性がありますが、結果は非常に効率的です (高速化のためにシェーダーで行列演算も実行しています)。

もう 1 つ、おそらくもっと簡単な方法は、DirectXTKスプライトバッチを試してみることです。

私はこのプロジェクトでそれを使用しました...単純なブリットのためだけですが、スプライトバッチを使用するために必要な少量のセットアップを確認するための良いスタートになるかもしれません.

また、可能であれば、テクスチャを「アトラス」してみてください。たとえば、できるだけ多くの「画像」をテクスチャに合わせて、それぞれに単一のテクスチャを使用するのではなく、それらからブリットするようにしてください。

于 2012-06-01T20:38:45.690 に答える