我正在尝试编写一个OpenGL包装器,使我能够使用所有现有的图形代码(针对OpenGL编写),并将OpenGL调用路由到Direct3D等效项。到目前为止,这个工作令人惊讶地顺利,但性能却成为了一个相当大的问题。
现在,我承认我很可能正在以一种它从未设计过的方式使用D3D。每次渲染循环,我都会更新单个顶点缓冲区数千次。每次绘制“精灵”,我都会向GPU发送4个带纹理坐标等信息的顶点,当屏幕上的“精灵”数量达到约1k至1.5k时,我的应用程序的FPS下降到10fps以下。
使用VS2012性能分析(顺便说一句,这很棒),我可以看到ID3D11DeviceContext->Draw方法占用了大部分时间: 此处为截图
现在,我承认我很可能正在以一种它从未设计过的方式使用D3D。每次渲染循环,我都会更新单个顶点缓冲区数千次。每次绘制“精灵”,我都会向GPU发送4个带纹理坐标等信息的顶点,当屏幕上的“精灵”数量达到约1k至1.5k时,我的应用程序的FPS下降到10fps以下。
使用VS2012性能分析(顺便说一句,这很棒),我可以看到ID3D11DeviceContext->Draw方法占用了大部分时间: 此处为截图
在设置顶点缓冲区或绘制方法时,我是否有没有正确使用的设置?如果为所有精灵使用相同的顶点缓冲区真的很糟糕吗?如果是这样,我还有哪些选项可以不会大幅改变我现有图形代码库的架构(这是围绕OpenGL范例构建的...每帧将所有内容发送到GPU!)
在我的游戏中,最大的FPS杀手是当我在屏幕上显示大量文本时。每个字符都是一个纹理四边形,每个字符都需要单独更新顶点缓冲区并调用单独的Draw。如果D3D或硬件不喜欢多次调用Draw,那么如何才能一次性向屏幕绘制大量文本呢?
如果需要更多的代码来帮助我诊断此问题,请告诉我。
谢谢!
这是我正在运行的硬件:
- Core i7 @ 3.5GHz
- 16 GB内存
- GeForce GTX 560 Ti
这是我正在运行的软件:
- Windows 8 Release Preview
- VS 2012
- DirectX 11
这里是绘制方法:
void OpenGL::Draw(const std::vector<OpenGLVertex>& vertices)
{
auto matrix = *_matrices.top();
_constantBufferData.view = DirectX::XMMatrixTranspose(matrix);
_context->UpdateSubresource(_constantBuffer, 0, NULL, &_constantBufferData, 0, 0);
_context->IASetInputLayout(_inputLayout);
_context->VSSetShader(_vertexShader, nullptr, 0);
_context->VSSetConstantBuffers(0, 1, &_constantBuffer);
D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
ID3D11ShaderResourceView* texture = _textures[_currentTextureId];
// Set shader texture resource in the pixel shader.
_context->PSSetShader(_pixelShaderTexture, nullptr, 0);
_context->PSSetShaderResources(0, 1, &texture);
D3D11_MAPPED_SUBRESOURCE mappedResource;
D3D11_MAP mapType = D3D11_MAP::D3D11_MAP_WRITE_DISCARD;
auto hr = _context->Map(_vertexBuffer, 0, mapType, 0, &mappedResource);
if (SUCCEEDED(hr))
{
OpenGLVertex *pData = reinterpret_cast<OpenGLVertex *>(mappedResource.pData);
memcpy(&(pData[_currentVertex]), &vertices[0], sizeof(OpenGLVertex) * vertices.size());
_context->Unmap(_vertexBuffer, 0);
}
UINT stride = sizeof(OpenGLVertex);
UINT offset = 0;
_context->IASetVertexBuffers(0, 1, &_vertexBuffer, &stride, &offset);
_context->IASetPrimitiveTopology(topology);
_context->Draw(vertices.size(), _currentVertex);
_currentVertex += (int)vertices.size();
}
以下是创建顶点缓冲区的方法:
void OpenGL::CreateVertexBuffer()
{
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = _maxVertices * sizeof(OpenGLVertex);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG::D3D11_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;
bd.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA initData;
ZeroMemory(&initData, sizeof(initData));
_device->CreateBuffer(&bd, NULL, &_vertexBuffer);
}
这是我的顶点着色器代码:
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix model;
matrix view;
matrix projection;
};
struct VertexShaderInput
{
float3 pos : POSITION;
float4 color : COLOR0;
float2 tex : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 pos : SV_POSITION;
float4 color : COLOR0;
float2 tex : TEXCOORD0;
};
VertexShaderOutput main(VertexShaderInput input)
{
VertexShaderOutput output;
float4 pos = float4(input.pos, 1.0f);
// Transform the vertex position into projected space.
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;
// Pass through the color without modification.
output.color = input.color;
output.tex = input.tex;
return output;
}