【问题标题】:OpenGL ES iOS drawing performance a lot slower with VBOs than withoutOpenGL ES iOS 绘图性能在使用 VBO 时比没有时慢很多
【发布时间】:2013-02-24 05:17:30
【问题描述】:

我最近将当前项目中的绘图从内存阵列的标准绘图更改为 VBO。令我惊讶的是,帧速率从 60fps 显着下降到 30fps,绘制了 8 次 1200verts 的模型。进一步分析表明,与从内存中绘制相比,使用 VBO 时 glDrawElements 花费的时间是其 10 倍。

我真的很困惑为什么会这样。有谁知道性能下降的原因是什么?

我正在运行 iOS 6.1.2 的 iPhone 5 上进行测试。

我已经将我的 VBO 处理隔离到一个函数中,在该函数的顶部静态创建顶点/索引缓冲区。我可以使用 #ifdef USE_VBO 在正常渲染和 VBO 渲染之间切换

- (void)drawDuck:(Toy*)toy reflection:(BOOL)reflection
{
    ModelOBJ* model = _duck[0].model;
    int stride = sizeof(ModelOBJ::Vertex);

#define USE_VBO
#ifdef USE_VBO
    static bool vboInitialized = false;
    static unsigned int vbo, ibo;
    if (!vboInitialized) {
        vboInitialized = true;

        // Generate VBO
        glGenBuffers(1, &vbo);
        int numVertices = model->getNumberOfVertices();
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, stride*numVertices, model->getVertexBuffer(), GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // Generate index buffer
        glGenBuffers(1, &ibo);
        int numIndices = model->getNumberOfIndices();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*numIndices, model->getIndexBuffer(), GL_STATIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }
#endif

    [self setupDuck:toy reflection:reflection];


#ifdef USE_VBO
    // Draw with VBO
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

    glEnableVertexAttribArray(GC_SHADER_ATTRIB_POSITION);
    glEnableVertexAttribArray(GC_SHADER_ATTRIB_NORMAL);
    glEnableVertexAttribArray(GC_SHADER_ATTRIB_TEX_COORD);

    glVertexAttribPointer(GC_SHADER_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(ModelOBJ::Vertex, position));
    glVertexAttribPointer(GC_SHADER_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(ModelOBJ::Vertex, texCoord));
    glVertexAttribPointer(GC_SHADER_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(ModelOBJ::Vertex, normal));
    glDrawElements(GL_TRIANGLES, model->getNumberOfIndices(), GL_UNSIGNED_SHORT, 0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#else
    // Draw with array
    glEnableVertexAttribArray(GC_SHADER_ATTRIB_POSITION);
    glEnableVertexAttribArray(GC_SHADER_ATTRIB_NORMAL);
    glEnableVertexAttribArray(GC_SHADER_ATTRIB_TEX_COORD);

    glVertexAttribPointer(GC_SHADER_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, stride, model->getVertexBuffer()->position);
    glVertexAttribPointer(GC_SHADER_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, stride, model->getVertexBuffer()->texCoord);
    glVertexAttribPointer(GC_SHADER_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, stride, model->getVertexBuffer()->normal);
    glDrawElements(GL_TRIANGLES, model->getNumberOfIndices(), GL_UNSIGNED_SHORT, model->getIndexBuffer());
#endif
}

ModelOBJ::Vertex 对于 pos、texcoord、normal 来说只是 3,2,3 浮点数。索引是超短的。

更新:我现在已经将绘制设置(即属性绑定调用)封装到 VAO 中,现在性能还可以,甚至比从主内存绘制要好一些。所以我的结论是,没有 VAO 的 VBO 支持在 iOS 上被破坏了。这个假设正确吗?

【问题讨论】:

  • 不应该是model->getVertexBuffer()->position是&model->getVertexBuffer()->position吗?
  • 位置定义为浮动位置[3],这样就可以了。
  • 很有趣,VAO 修复了它!你确定你在程序的其他地方没有任何杂散的启用顶点属性吗?如果是这样,那就可以解释了,因为 VAO 封装了一组启用的顶点属性。
  • 可能,我上次做 3D 是 D3D9,所有绘图都通过顶点缓冲区和索引缓冲区,我在状态块中捕获状态。 VAO 的概念对我来说是相当新的。如果我理解正确,那么 VAO 从绑定 VAO 到未绑定的那一刻捕获顶点状态(即 glEnableVertexAttribArray、glVertexAttribPointer)。所以我会假设以前绑定的顶点属性不会影响 VAO?我实际上介绍了 VAO,因为 Apple 示例代码在 VBO 之外还使用了它们,所以我尝试了一下。

标签: ios performance opengl-es vbo


【解决方案1】:

驱动程序很可能回退到软件顶点提交(CPU 从 VBO 复制到命令缓冲区)。这可能比在客户端内存中使用顶点数组更糟糕,因为我们通常缓存客户端内存,而 VBO 内容通常在 iOS 上的写入组合内存中。

在 Instruments 中使用 CPU 采样器时,您会在 gleRunVertexSubmitARM 中的 glDrawArrays/glDrawElements 下看到大量时间。

回退到 SW CPU 提交的最常见原因是一个未对齐的属性(当前 iOS 设备要求每个属性都是 4 字节对齐的),但对于您显示的 3 个属性似乎并非如此.之后,下一个最常见的原因是在单个顶点数组配置中混合客户端数组和缓冲区对象。

在这种情况下,您可能有一个杂散的顶点属性绑定:其他一些数组元素可能仍处于启用状态并指向客户端数组,从而导致所有内容都脱离硬件 DMA 路径。通过创建 VAO,您要么切换到配置错误的默认 VAO,要么尝试启用客户端 VAO,但由于客户端阵列已贬值且在与 VAO 一起使用时无法运行(引发 INVALID_OPERATION 错误)而被保存.

【讨论】:

  • 谢谢,看起来你所建议的就是原因,因为配置文件与你所说的相匹配(在 gleRunVertexSubmitARM 中的 glDrawElements 下的大量时间)。我将分析我的绘图代码以找出确切的原因。对齐不应该是这样,因为我都在浮动,但之前绘制调用的杂散绑定绝对是可能的。
【解决方案2】:

当您使用glBufferData 填充索引缓冲区时,第二个参数应该是2*numIndices 而不是stride*numIndices。

由于您的索引缓冲区比它需要的大得多,这可以解释您的性能问题。

【讨论】:

  • 好收获。抱歉,我在美化帖子的代码时打错了。我已经更正了,但这并不能解决问题。这真的让我很困惑,因为它太慢了。我理解性能没有改变,但这似乎是驱动程序中的一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多