当我每帧向单个顶点缓冲区写入数千次时,如何提高 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 的性能? 的相关文章

  • 改进绩效反思 - 我应该考虑哪些替代方案?

    我需要动态地设置对象上的一堆或属性的值 将其称为传输对象 将在短时间内创建相当数量的此类传输对象并设置其属性 我想避免使用反射 还有其他选择吗 如果是的话 有我可以查看的示例实现吗 Use Delegate CreateDelegate h
  • 在 Java 中,对复杂模型使用接口是否会带来性能提升?

    标题很难理解 但我不知道如何以另一种方式总结 欢迎任何澄清的编辑 我被告知并建议使用接口来提高性能 即使在并不特别需要常规 接口 角色的情况下也是如此 在这种情况下 对象是大模型 MVC 意义上的 具有许多方法和字段 向我推荐的 好用处 是
  • 更改为通用接口对性能的影响

    我使用 Visual Studio 使用 C NET 开发应用程序 ReSharper 在我的方法原型中经常建议我用更通用的类型替换输入参数的类型 例如 如果我仅在方法主体中使用带有 foreach 的列表 则使用 List 和 IEnum
  • 为什么 Python 对于一个简单的 for 循环来说这么慢?

    我们正在做一些kNN and SVDPython 中的实现 其他人选择了 Java 我们的执行时间非常不同 我使用 cProfile 来查看我在哪里犯了错误 但一切都很好fine http wiki python org moin Pyth
  • Assimp 和 D3D 模型加载:网格未在 D3D 中显示

    我想使用 Assimp 将模型加载到 D3D 中 我想更多地了解 Assimp 如何处理索引 因为我无法让它以我理解的形式与 obj 模型一起工作 例如 对于面为 int int int 的 obj 模型 当我迭代面数时 for unsig
  • MySQL InnoDB 查询性能

    我正在尝试优化一个简单的 sql 查询 该查询将多次运行大量数据 这是场景 MySQL 与 InnoDB 表 where 和 join 中使用的所有字段都已索引 表有 FK 我不需要查询的整个缓存 但每个表的缓存是可能的 表有更多的更新 插
  • 定点数学比浮点运算快吗?

    多年前 即 20 世纪 90 年代初期 我构建了图形软件包 该软件包基于定点算术和预先计算的 cos sin 表格以及使用牛顿近似方法进行 sqrt 和对数近似的缩放方程来优化计算 这些先进技术似乎已经成为图形和内置数学处理器的一部分 大约
  • 空 while 循环有什么影响?

    我知道这可能是一个有点 愚蠢 的问题 但有时 我只想循环直到条件为假 但我不喜欢让循环保持为空 所以代替 Visible true while IsRunning Visible false 我通常prefer while IsRunnin
  • 去除字符串的最佳方法是什么?

    我需要具有最佳性能的想法来删除 过滤字符串 I have string Input view 512 3 159 删除 view 和 的最佳性能方法是什么 和引号 我可以做这个 Input Input Replace view Replac
  • 为什么 Android Eclipse 不断刷新外部文件夹并花费很长时间?

    我只有一部新的 Android 手机 我一直在修补一些基本的应用程序 每当我保存任何内容时 Eclipse 的 Android 插件就会刷新外部文件夹 这让我抓狂 通常我不会介意 但当需要 10 秒才能刷新时 我开始注意到 我已经搜索过 其
  • C# 写入文件的性能

    我的情况概述 我的任务是从文件中读取字符串 并将它们重新格式化为更有用的格式 重新格式化输入后 我必须将其写入输出文件 这是必须完成的操作的示例 文件行示例 ANO 2010 CPF 17834368168 YEARS 2010 2009
  • 嵌套辅助函数和性能

    嵌套辅助函数对于使代码更易于理解非常有用 谷歌甚至建议在他们的应用程序中使用嵌套函数时尚指南 https google styleguide googlecode com svn trunk javascriptguide xml Nest
  • 在Python列表中交换元素的最快方法

    在Python中交换两个列表元素是否有比 L a L b L b L a 或者我必须求助于Cython http cython org or Weave http www scipy org Weave或类似的 看起来 Python 编译器
  • c# GDI边缘空白检测算法

    我正在寻找解决方案检测边缘空白c 位图 来自 c 托管 GDI 库 图像将是透明的 or white 大多数 400x 图片的尺寸为 8000x8000px 边缘周围有大约 2000px 的空白 找出边缘的最有效方法是什么 x y 高度和宽
  • 隐藏类以及 {} 对象与自定义构造函数之间的等效性 (v8)

    鉴于这篇文章 http richardartoul github io jekyll update 2015 04 26 hidden classes html http richardartoul github io jekyll upd
  • Draggable JS Bootstrap 模式 - 性能问题

    对于工作中的项目 我们在 JavaScript 中使用 Bootstrap Modal 窗口 我们想让一些窗口可移动 但我们遇到了 JQuery 的性能问题 myModal draggable handle modal header Exa
  • Mxnet - 缓慢的数组复制到 GPU

    我的问题 我应该如何在 mxnet 中执行快速矩阵乘法 我的具体问题 数组复制到 GPU 的速度很慢 对此我们能做些什么呢 我创建随机数组 将它们复制到上下文中 然后相乘 import mxnet as mx import mxnet nd
  • 高效秒表

    您好 我正在用 javascript 编写一个秒表实用程序 我有一个关于效率和开销的问题 我考虑过两种制作秒表的方法 1 存储开始日期并不断测量自该日期以来经过的毫秒数 2 创建一个整数并按设定的时间间隔递增其值 我想知道哪个最有效 另外
  • 数组与列表的性能

    假设您需要一个需要频繁迭代的整数列表 数组 我的意思是非常频繁 原因可能有所不同 但可以说它位于大容量处理的最内层循环的核心 一般来说 人们会选择使用列表 List 因为它们的大小具有灵活性 最重要的是 msdn 文档声称列表在内部使用数组
  • 当跳转在 32 字节上不完全对齐时,使用 MITE(传统管道)代替 DSB(微指令缓存)

    这个问题曾经是这个 现已更新 问题 https stackoverflow com questions 59883527 unrolling 1 cycle loop reduces performance by 25 on skylake

随机推荐