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

2024-01-31

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

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

使用 VS2012 性能分析(顺便说一句,非常棒),我可以看到 ID3D11DeviceContext->Draw 方法占用了大部分时间:截图在这里 https://i.stack.imgur.com/trHOr.png

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

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

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

Thanks!

这是我运行的硬件:

  • 酷睿 i7 @ 3.5GHz
  • 16 GB 内存
  • 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;
}

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

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

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

更简单的方法是在大缓冲区中缓存顶点,直到设备状态发生变化,这种方法可以在良好的情况下提供良好的缓存。在那时候,在实际改变状态之前,将数组转储到顶点缓冲区并绘制。然后重置数组索引,提交状态更改,然后再次进行。

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我每帧向单个顶点缓冲区写入数千次时,如何提高 Direct3D 的性能? 的相关文章

  • PostgreSQL:在所有表字段的长度上创建索引

    我有一张桌子叫profile 我想按照填写最多的内容对它们进行排序 每列都是 JSONB 列或 TEXT 列 我不需要很大程度的确定性 所以通常我会按如下方式订购 SELECT FROM profile ORDER BY LENGTH CO
  • 如何在 C++ 中对静态缓冲区执行字符串格式化?

    我正在处理一段对性能要求非常高的代码 我需要执行一些格式化的字符串操作 但我试图避免内存分配 甚至是内部库的内存分配 在过去 我会做类似以下的事情 假设是 C 11 constexpr int BUFFER SIZE 200 char bu
  • 加快写入文件的速度

    我已经分析了一些我用 cProfile 继承的遗留代码 我已经做了很多有帮助的更改 例如使用 simplejson 的 C 扩展 基本上 该脚本将数据从一个系统导出到 ASCII 固定宽度文件 每一行都是一条记录 并且有许多值 每行有 71
  • 如何为 CUDA 内核选择网格和块尺寸?

    这是一个关于如何确定CUDA网格 块和线程大小的问题 这是对已发布问题的附加问题here https stackoverflow com a 5643838 1292251 通过此链接 talonmies 的答案包含一个代码片段 见下文 我
  • Xcode“使用性能工具运行”被禁用?

    我正在尝试从我的 Xcode 项目中查找内存泄漏 我不知道发生了什么 我无法选择任何内容Run gt Run with performance tool 事物列表被禁用 请帮助我 我是初学者 问题是我已经删除了构建文件夹并尝试使用性能工具运
  • 为单个方法引用大 DLL

    我想在 C 中使用大型类库 dll 中的单个方法 是否有性能或其他方面的缺点 我应该使用反射工具 读取 方法代码并将其复制粘贴到我的项目中吗 更新 硬盘空间不是问题 我的应用程序是网络应用程序 是否有性能或其他方面的缺点 唯一真正重要的是可
  • 有没有办法分析 WCF 应用程序的性能?

    我们正在尝试测量我们的系统的性能 该系统是一个使用 WCF 调用的 NET 3 5 应用程序 问题是到目前为止 我们无法分析这些调用中的方法 编写了一个 winforms 客户端应用程序来测试我们的系统 我们尝试使用ANTS 4 Profi
  • .NET 图形重影

    我正在为我们正在开发的新应用程序制作一个示例 GUI 我已经决定了语言 但我可以使用任何第 3 方 DLL 或插件或任何我需要的东西 以使 GUI 尽可能无缝地工作 他们希望它非常像 mac ubuntu vista Windows 7 所
  • java charAt() 和startsWith() 哪个更快? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我的问题是 如果我想检查特定索引中字符串的一个字符 仅检查一个字符 哪种方法非常有效charAt or startsWith 我的意思是 据我所
  • R:使用带有 .Call 和 C/C++ 包装器的 Fortran 子例程而不是 .Fortran 的优点?

    我有一个 R 包 它使用大量 Fortran 子例程来进行递归线性代数计算的嵌套循环 很大程度上依赖于 BLAS 和 LAPACK 例程 作为 Fortran 的接口 我使用 Fortran功能 我刚刚读过乔纳森卡拉汉的博客文章 http
  • 为什么 pandas 在简单的数学运算上比 numpy 更快?

    最近 我观察到 pandas 的乘法速度更快 我在下面的例子中向您展示了这一点 如此简单的操作怎么可能做到这一点 这怎么可能呢 pandas 数据帧中的底层数据容器是 numpy 数组 测量 我使用形状为 10k 10k 的数组 数据框 i
  • 时间复杂度和运行时间有什么区别?

    时间复杂度和运行时间有什么区别 它们是一样的吗 运行时间是指程序运行所需的时间 时间复杂度是对输入大小趋于无穷大时运行时间渐进行为的描述 您可以说运行时间 是 O n 2 或其他什么 因为这是描述复杂性类和大 O 表示法的惯用方式 事实上
  • 通过增加索引之和来生成排序组合的有效方法

    对于启发式算法 我需要一个接一个地评估特定集合的组合 直到达到停止标准 由于它们很多 目前我正在使用以下内存高效迭代器块生成它们 受到 python 的启发 itertools combinations http docs python o
  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 优化 LATERAL join 中的慢速聚合

    在我的 PostgreSQL 9 6 2 数据库中 我有一个查询 该查询根据一些股票数据构建计算字段表 它为表中的每一行计算 1 到 10 年的移动平均窗口 并将其用于周期性调整 具体来说 CAPE CAPB CAPC CAPS 和 CAP
  • .pdbs 会减慢发布应用程序的速度吗?

    如果 dll 中包含 pdb 程序调试 文件 则行号将出现在引发的任何异常的堆栈跟踪中 这会影响应用程序的性能吗 这个问题与发布与调试 即优化 无关 这是关于拥有 pdb 文件的性能影响 每次抛出异常时都会读取 pdb 文件吗 加载程序集时
  • Paper.js 中的事件处理程序

    我是 Paper js 的新手 在阅读教程时我对事件系统感到好奇 这就是事件处理中描述的方式tutorial http paperjs org tutorials interaction mouse tool events var path
  • Haskell:IORef 的性能

    我一直在尝试在 Haskell 中编码一个需要使用大量可变引用的算法 但与纯粹的惰性代码相比 它 也许并不奇怪 非常慢 考虑一个非常简单的例子 module Main where import Data IORef import Contr
  • Pandas hub_table 更快的替代品

    我正在使用熊猫pivot table在大型数据集 1000 万行 6 列 上运行 由于执行时间至关重要 因此我尝试加快流程 目前 处理整个数据集大约需要 8 秒 这太慢了 我希望找到替代方案来提高速度 性能 我当前的 Pandas 数据透视
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对

随机推荐