【问题标题】:Buffer drawing in OpenGLOpenGL中的缓冲区绘制
【发布时间】:2014-01-16 10:22:42
【问题描述】:

在这个问题中,我对 OpenGL 中的缓冲区绘图很感兴趣,特别是在每个数据集使用一个缓冲区与为多个数据集使用一个缓冲区的权衡。

上下文:

考虑一个包含 N 个顶点的数据集,每个顶点由一组属性(例如颜色、纹理、法线)表示。 每个属性都由一个类型(例如 GLfloat、GLint)和许多组件(2、3、4)表示。我们想绘制这些数据。示意图,

(non-interleaved representation)

   data set
<-------------->
 a_1  a_2   a_3
<---><---><---->
a_i = attribute; e.g. a2 = (3 GLfloats representing color, thus 3*N Glfloats)

我们想将其映射到 GL 状态,使用 glBufferSubData

问题

映射时,我们必须跟踪内存中的数据,因为glBufferSubData 需要startsize。这听起来像是一个分配问题:我们想要分配内存并跟踪它的位置。由于我们希望快速访问它,我们希望数据位于相同的内存位置,例如带有std::vector&lt;char&gt;。示意图,

  data set 1    data set 2
<------------><-------------->
(both have same buffer id)

我们承诺 gl 状态为:

// id is binded to one std::vector<char>, "data".
glBindBuffer(target, id);

// for each data_set (AFTER calling glBindBuffer).

// for each attribute

// "start": the start point of the attribute.
// "size":  (sizeof*components of the attribute)*N.
glBufferSubData(target, start, size, &(data[0]))

(non non-interleaved for the sake of the code).

当我们想要添加或删除顶点时,就会出现问题,例如当 LOD 改变时。因为每个数据集必须是一个块,例如允许交错绘制(即使在非交错中,每个属性都是一个块),我们最终会在我们的std::vector&lt;char&gt; 中产生碎片。


另一方面,我们也可以为每个缓冲区设置一个块:我们不是将块分配给同一个缓冲区,而是将每个块(现在是 std::vector&lt;char&gt;)分配给不同的缓冲区。示意图,

  data set 1 (buffer id1)
<------------>
  data set 2 (buffer id2)
<-------------->

我们将数据提交到 gl 状态:

// for each data_set (BEFORE calling glBindBuffer).
// "data" is the std::vector<char> of this data_set.

// id is now binded to the specific std::vector<char>
glBindBuffer(target, id);

// for each attribute

// "start": the start point of the attribute.
// "size":  (sizeof*components of the attribute)*N.
glBufferSubData(target, start, size, &(data[0]))

问题

我正在学习这个,所以,在以下任何一个之前:这个推理是否正确?

假设是,

  1. 拥有任意数量的缓冲区是否有问题?
  2. “glBindBuffer”是否会随着缓冲区的数量而扩展?
  3. 在此决定中需要考虑哪些要点?

【问题讨论】:

    标签: opengl


    【解决方案1】:

    如果您询问性能权衡,则不太清楚。但我会用这个键回答。

    1. 拥有任意数量的缓冲区是否有问题?

    这是一个来自中世纪黑暗时期的问题,当时管道由于向后兼容性的原因而被修复并暂时停止。 glBind* 被认为是现代 OpenGL 驱动程序中的(之一)性能瓶颈,由错误的 locality of references 和缓存未命中引起。简单地说,缓存是冷的,大部分时间 CPU 只是在驱动程序中等待从主内存传输的数据。驱动程序实现者对当前的 API 无能为力。阅读 Nvidia 的简短 article about it 及其无绑定扩展提案。

    2. Is "glBindBuffer" expected to scale with the number of buffers?
    

    当然,对象(在您的情况下为缓冲区)越多,绑定调用越多,驱动程序中的性能损失就越大。但合并后的巨大资源对象更难管理。

    3. What are the major points to take into consideration in this decision?
    

    只有一个。分析结果;) “过早的优化是万恶之源”,所以尽量保持客观,只相信数字。当数字变坏时,我们可以想到:

    “巨大”、“一体化”资源:

    • 更少的绑定调用
    • 更少的上下文变化
    • 更难管理和调试,需要一些额外的代码基础设施(例如更新资源数据)
    • 调整大小(重新分配)非常慢

    单独的资源:

    • 更多绑定调用,在驱动程序中浪费时间
    • 更多上下文变化
    • 更易于管理,不易出错
    • 易于调整大小、分配、重新分配

    最后,我们可以看到更新数据时存在性能-复杂性权衡和不同的行为。要坚持一种或另一种方法,您必须:

    • 决定,您是想让事情保持简单、易于管理还是增加复杂性并获得额外的 FPS(在图形分析器中进行分析以了解多少。值得吗?)
    • 了解调整缓冲区大小/重新分配缓冲区的频率(跟踪图形调试器中的 API 调用)。

    希望它以某种方式有所帮助;)

    如果你喜欢这样的理论断言,可能你会对another one, about interleaving(DirectX 一)感兴趣

    【讨论】:

      猜你喜欢
      • 2011-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多