渲染一堆分层纹理的有效方法?

时间:2012-06-01 20:10:00

标签: directx windows-8 windows-runtime direct3d hlsl

渲染一堆分层纹理的有效方法是什么?我有一些半透明纹理矩形,我在3D空间中随机定位并从后向前渲染它们。

目前我调用d3dContext-> PSSetShaderResources()在每次调用d3dContext-> DrawIndexed()之前为像素着色器提供新纹理。我有一种感觉,我在每次抽奖之前将纹理复制到GPU内存。我可能有10-30个ARGB纹理,大约每个1024x1024像素,它们与我在屏幕上渲染的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
            );
    }
}

Pixel Shader

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;
}

2 个答案:

答案 0 :(得分:2)

正如Jeremiah所建议的那样,你不可能为每个帧移动纹理从CPU到GPU,因为你必须为每个帧创建新纹理或使用“UpdateSubresource”或“Map / UnMap”方法。

我不认为实例化会对这种特殊情况有所帮助,因为多边形的数量非常少(我会开始担心数百万个多边形)。您的应用程序更有可能是带宽/填充限制,因为您正在执行大量纹理采样/混合(这取决于GPU上的tecture填充率,像素填充率和ROP的nunber)。

为了获得更好的性能,强烈建议:

另一方面,您可能会在https://gamedev.stackexchange.com/上对此类问题给予更多关注。

答案 1 :(得分:1)

我认为你没有从GPU到系统内存进行任何复制。您通常必须明确地调用Map(...),或者通过blitting到您在系统内存中创建的纹理。

一个问题是,您是否正在为每个纹理进行DrawIndexed(...)调用。如果你通过批处理发送大量工作,GPU工作效率最高。实现此目的的一种方法是将n个纹理设置为PSSetShaderResources(i,...),并执行DrawIndexedInstanced(...)。然后,着色器代码将读取每个着色器资源并绘制它们。我在我的C ++ DirectCanvas代码here(SpriteInstanced.cpp)中执行此操作。这可以产生大量代码,但结果是非常高效(我甚至在着色器中执行矩阵操作以获得更快的速度)。

另一个,也许更简单的方法是给DirectXTK spritebatch一个镜头。

我在这个project中使用它...仅用于简单的blit,但是看到使用spritebatch所需的少量设置可能是一个好的开始。

另外,如果可能的话,尝试“纹理”你的纹理。例如,尽量在纹理中装入尽可能多的“图像”,然后从它们中加入blit,而每个纹理都有一个纹理。