gpt4 book ai didi

performance - 当我每帧写入单个顶点缓冲区数千次时,如何提高 Direct3D 的性能?

转载 作者:行者123 更新时间:2023-12-04 03:26:34 25 4
gpt4 key购买 nike

我正在尝试编写一个 OpenGL 包装器,它允许我使用所有现有的图形代码(为 OpenGL 编写的)并将 OpenGL 调用路由到 Direct3D 等效项。到目前为止,这种方法的效果出奇的好,只是性能变成了一个相当大的问题。

现在,我承认我很可能以从未设计过的方式使用 D3D。我在每个渲染循环中更新了数千次单个顶点缓冲区。每次我绘制一个“ Sprite ”时,我都会使用纹理坐标等向 GPU 发送 4 个顶点,当屏幕上的“ Sprite ”数量一次达到 1k 到 1.5k 左右时,我的应用程序的 FPS 下降到低于 10 帧/秒。

使用 VS2012 性能分析(这很棒,顺便说一句),我可以看到 ID3D11DeviceContext->Draw 方法占用了大部分时间:
Screenshot Here

在设置顶点缓冲区或绘制方法期间,是否有一些设置我没有正确使用?为我的所有 Sprite 使用相同的顶点缓冲区真的非常糟糕吗?如果是这样,我还有哪些其他选项不会彻底改变我现有图形代码库的架构(围绕 OpenGL 范例构建......每帧都将所有内容发送到 GPU!)

我游戏中最大的 FPS 杀手是当我在屏幕上显示大量文本时。每个字符都是一个带纹理的四边形,每个字符都需要单独更新顶点缓冲区并单独调用 Draw。如果 D3D 或硬件不喜欢多次调用 Draw,那么你怎么能一次在屏幕上绘制大量文本呢?

如果您想查看更多代码来帮助我诊断此问题,请告诉我。

谢谢!

这是我正在运行的硬件:

  • Core i7 @ 3.5GHz
  • 16 GB RAM
  • GeForce GTX 560 Ti

  • 这是我正在运行的软件:
  • 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;
    }

    最佳答案

    您需要做的是尽可能积极地批处理顶点,然后绘制大块。我很幸运地将它 retrofit 到旧的即时模式 OpenGL 游戏中。不幸的是,这样做有点痛苦。

    最简单的概念解决方案是使用某种设备状态(您可能已经在跟踪)为一组特定的顶点创建一个唯一的标记。混合模式和绑定(bind)纹理之类的东西是一个很好的集合。如果你能找到一个快速的散列算法在其中的结构上运行,你可以非常有效地存储它。

    接下来,您需要进行顶点缓存。有两种方法可以解决这个问题,两者都有优势。最激进、最复杂,并且在许多具有相似属性的顶点集的情况下,最有效的是创建一个设备状态结构,分配一个大的(比如 4KB)缓冲区,然后继续存储具有匹配状态的顶点。大批。然后,您可以将整个数组转储到帧末尾的顶点缓冲区中,并绘制缓冲区的 block (以重新创建原始顺序)。然而,跟踪所有缓冲区、状态和顺序是很困难的。

    更简单的方法可以在良好的情况下提供良好的缓存,将顶点缓存在一个大缓冲区中,直到设备状态发生变化。此时,在实际更改状态之前,将数组转储到顶点缓冲区并绘制。然后重置数组索引,提交状态更改,然后重新开始。

    如果您的应用程序有大量相似的顶点,这很可能与 Sprite 一起使用(纹理坐标和颜色可能会改变,但好的 Sprite 将使用单个纹理图集和很少的混合模式),即使是第二种方法也可以提供一些性能提升。

    这里的技巧是在系统内存中建立一个缓存,最好是一大块预先分配的内存,然后在绘图之前将其转储到视频内存中。这允许您对视频内存和绘图调用执行更少的写入,这往往是昂贵的(尤其是在一起)。正如您所看到的,您调用的电话数量变得很慢,而批处理很有可能帮助解决这个问题。诀窍是如果你能帮助它,不要在每帧分配内存,批量处理足够大的 block 是值得的,并为每次绘制保持正确的设备状态和顺序。

    关于performance - 当我每帧写入单个顶点缓冲区数千次时,如何提高 Direct3D 的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12117287/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com