我正在尝试编写一个 OpenGL 包装器,它允许我使用所有现有的图形代码(为 OpenGL 编写的)并将 OpenGL 调用路由到 Direct3D 等效项。到目前为止,这种方法的效果出奇的好,只是性能变成了一个相当大的问题。
现在,我承认我很可能以从未设计过的方式使用 D3D。我在每个渲染循环中更新了数千次单个顶点缓冲区。每次我绘制一个“精灵”时,我都会使用纹理坐标等向 GPU 发送 4 个顶点,当屏幕上的“精灵”数量一次达到 1k 到 1.5k 左右时,我的应用程序的 FPS 下降到低于 10 帧/秒。
使用 VS2012 性能分析(这很棒,顺便说一句),我可以看到 ID3D11DeviceContext->Draw 方法占用了大部分时间: Screenshot Here
在设置顶点缓冲区或绘制方法期间,是否有一些设置我没有正确使用?为我的所有精灵使用相同的顶点缓冲区真的非常糟糕吗?如果是这样,我还有哪些其他选项不会彻底改变我现有图形代码库的架构(围绕 OpenGL 范例构建......每帧都将所有内容发送到 GPU!)
我游戏中最大的 FPS 杀手是当我在屏幕上显示大量文本时。每个字符都是一个带纹理的四边形,每个字符都需要单独更新顶点缓冲区并单独调用 Draw。如果 D3D 或硬件不喜欢多次调用 Draw,那么你怎么能一次在屏幕上绘制大量文本呢?
如果您想查看更多代码来帮助我诊断此问题,请告诉我。
谢谢!
这是我正在运行的硬件:
- 酷睿 i7 @ 3.5GHz
- 16 GB RAM
- GeForce GTX 560 钛
这是我正在运行的软件:
- Windows 8 发布预览
- 对比 2012
- 直连 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;
}