4

我最近将当前项目中的绘图从内存阵列的标准绘图更改为 VBO。令我惊讶的是,帧速率从 60fps 显着下降到 30fps,绘制了 1200verts 的模型 8 次。进一步分析表明,与从内存中绘图相比,使用 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 上被破坏了。这个假设正确吗?

4

2 回答 2

8

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

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

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

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

于 2013-03-09T21:26:11.523 回答
1

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

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

于 2013-03-08T16:30:43.593 回答