我如何才能提高Direct3D的性能时,我写一个顶点缓冲成千上万的每帧的时间?(How can I

2019-09-21 16:03发布

我试图写一个OpenGL的包装,让我用我所有的现有的图形代码(OpenGL的书面)和将路由OpenGL调用到Direct3D的等价物。 这工作得非常好,到目前为止,除了性能转向了相当的问题。

现在,我承认我是最有可能使用D3D在某种程度上,它从未被设计。 我更新一个顶点缓冲数千每循环渲染时间。 每次我画一个“雪碧”我送4个顶点到GPU纹理坐标等,并在“精灵”的屏幕上一次的数量就会达到接近1K到1.5K,那么我的应用程序的FPS下降到下面10fps的。

使用VS2012性能分析(这是真棒,顺便说一句),我可以看到ID3D11DeviceContext->绘制方法占用了大量的时间: 截图这里

有没有我不使用正确,而Draw方法中设置自己的顶点缓冲,或一些设置? 是不是真的,真不好可以用我所有的精灵相同顶点缓冲? 如果是这样,还有什么其他可以怎么做,不会彻底改变我现有的图形代码库的架构(其围绕的OpenGL模式建...发送一切交给GPU每帧!)

在我的游戏最大的FPS凶手是当我展示了很多在屏幕上的文字。 每个字符是有纹理的四通道,并且每一个都需要单独的更新到顶点缓冲器和一个单独的呼叫绘制。 如果D3D或硬件不喜欢多次调用绘制,然后还能怎么你一次绘制大量的文字在屏幕上的?

让我知道如果有任何你想看到帮我诊断此问题的详细代码。

谢谢!

下面是我在其上运行的硬件:

  • 酷睿i7 3.5GHz的@
  • 的RAM 16场演唱会
  • 的GeForce GTX 560钛

这里就是我中运行的软件:

  • Windows 8的发布预览
  • 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;
}

Answer 1:

你需要做的是批量顶点尽最大可能地,然后在大块借鉴。 我已经很幸运改进此为旧直接模式OpenGL游戏。 不幸的是,这是一种痛苦做的。

最简单的概念性的解决方案是使用某种设备状态的(你很可能已经追踪)来创建一个独特的邮票为一组特定的顶点。 像混合模式和绑定纹理的东西是一个很好的集合。 如果你能找到一个快速的散列算法上是在结构上运行,可以很有效地存储。

接下来,你需要做的顶点缓存。 有两种方式来处理,既具有优势。 最积极的,最复杂的,并在多台具有类似属性的顶点的情况下,最有效的是使设备状态的结构,分配一个大(比如4KB)缓冲液,并进行到与该匹配的状态的顶点存储阵列。 然后可以转储整个阵列划分为顶点缓冲在帧的末尾,并绘制缓冲器的块(以重新创建原始顺序)。 保持所有的缓冲区和状态和秩序的轨道是困难的,但是。

在简单的方法,它可以提供在良好的情况下,高速缓存的好一点,就是缓存顶点在一个大缓冲区,直到设备状态的变化。 在这一点上, 前实际改变状态 ,转储阵列分成顶点缓冲器和绘制。 然后重置数组索引,提交状态变化,并再次走。

如果你的应用有大量类似的顶点,这是非常可能的精灵工作(纹理坐标和颜色可能会改变,但良好的精灵将使用一个纹理图集和一些混合模式),即使是第二种方法可以提供一些性能提升。

这里的技巧是建立在系统内存中缓存,最好是一大块预分配内存,那么就先绘制它转储到显存。 这使您可以执行少得多写入显存和绘图调用,这往往是昂贵的(特别是在一起)。 正如你所看到的,您拨打电话的次数变得很慢,和配料站的与帮助一个很好的机会。 诀窍是不分配内存每一帧,如果你能帮助它,批量足够大块是值得的,并且保持正确的设备状态,为了使每个抽奖。



文章来源: How can I improve performance of Direct3D when I'm writing to a single vertex buffer thousands of times per frame?