渲染一堆分层纹理的有效方法是什么?我有一些半透明纹理矩形,我在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;
}
答案 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,而每个纹理都有一个纹理。