OpenGL ES 2.0 + 开罗:HUD

OpenGL ES 2.0 + Cairo: HUD

我正在尝试通过在 ARM Linux 平台上用 C 编写的 OpenGL ES 2.0 应用程序渲染 HUD。

我目前正在使用靠近近裁剪平面的 2 个三角形并将纹理平铺到它们上面。纹理是屏幕的大小,除了我渲染文本的部分外,大部分是透明的。使用 Pango/Cairo

生成纹理

如果我打开 HUD(取消对 render_ui 的调用的注释),我目前会受到 50% 的性能影响(从 60fps 到 30fps)。

这是渲染 HUD 的代码:

void render_ui(OGL_STATE_T *state) {

    glUseProgram(state->uiHandle);

    matIdentity(modelViewMatrix);
    matTranslate(modelViewMatrix, 0, 0, -0.51);

    const GLfloat *mvMat2 = modelViewMatrix;

    glViewport(0,0,state->screen_width, state->screen_height);

    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glBindBuffer(GL_ARRAY_BUFFER, state->uiVB);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state->uiIB);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, state->uiTex);
    glUniform1i(_uiTexUniform, 0);

    glUniformMatrix4fv(_uiProjectionUniform, 1, 0, pMat);
    glUniformMatrix4fv(_uiModelViewUniform, 1, 0, mvMat2);

    glVertexAttribPointer(_uiPositionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); 
    glVertexAttribPointer(_uiColorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
            (GLvoid *) (sizeof(GLfloat) * 3));
    glVertexAttribPointer(_uiTexCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
            (GLvoid *) (sizeof(GLfloat) * 7));

    glEnableVertexAttribArray(_uiPositionSlot);
    glEnableVertexAttribArray(_uiColorSlot);
    glEnableVertexAttribArray(_uiTexCoordSlot);

    glDrawElements(GL_TRIANGLES, uiIndicesArraySize / uiIndicesElementSize,
            GL_UNSIGNED_BYTE, 0);   

    glDisableVertexAttribArray(_uiTexCoordSlot);
    glDisable(GL_BLEND);

    GLenum err;

    if ((err = glGetError()) != GL_NO_ERROR)
        printf("There was an error");
}

必须有更明智的方法来做到这一点。

在移动设备上,GPU 对混合非常敏感,这有多种原因:

  • 混合会消耗更多带宽(需要读取当前像素以将其与新像素混合)
  • 混合会破坏隐藏表面移除优化
  • 混合也可以破坏基于图块的延迟渲染优化

简而言之 移动 GPU 喜欢不透明的多边形而讨厌透明的多边形

请注意,由于大多数移动 GPU 的 "tile based" 性质(当 tile/bin 被透明多边形覆盖,您可能会为此失去一些 GPU 优化)。

此外,既然你说你从 60fps 急剧下降到 30fps,我会断定你的设备 GPU 正在阻塞,等待屏幕 60Hz 垂直同步交换,所以这意味着你的帧 DT 只能是16 毫秒的倍数,因此您可能只能获得 fps 值,例如:60、30、15、7.5,...

因此,如果您的帧率为 60fps,但在您的应用程序主循环中添加一些内容,这会将 理论 fps 降至仅 57fps,那么由于垂直同步等待,您将突然转到 30fps。可以禁用 VSync,或者可以使用三重缓冲等技术来缓解这种情况,但是对于 OpenGLES,这样做的方式特定于您正在使用的 OS 和硬件......没有 "official way of doing it which works on all devices".

所以,了解了这一切,这里有一些恢复到 60fps 的建议:

  1. 使用降低的分辨率,例如:1280x720 而不是 1920x1080,这将减少带宽使用和片段处理。当然不理想,但这可以用作确认您有带宽或片段问题的测试(如果在降低分辨率后恢复 60fps,则说明您有此类问题)
  2. 使用 16 位 (R5G6B5) 后备缓冲区而不是 32 位后备缓冲区 (R8G8B8A8),这可以减少带宽使用,但会损失一些视觉质量
  3. 减少混合表面的面积:在你的情况下,这意味着你应该按 "blocks" 组织你的文本,每个块尽可能适合 IOS 中这张图片中的文本docs :
  4. 想办法在您的设备上禁用垂直同步/使用三重缓冲。如果您有权访问 Vivante GPU 文档(我没有),这可能会在里面进行描述。

第 3 点是最好的做法(这是我开发的大多数手机游戏所做的),但是这需要一些不可忽略的额外工作。第 1、2 和 3 点更直接但仅 "half solutions".