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 的建议:
- 使用降低的分辨率,例如:1280x720 而不是 1920x1080,这将减少带宽使用和片段处理。当然不理想,但这可以用作确认您有带宽或片段问题的测试(如果在降低分辨率后恢复 60fps,则说明您有此类问题)
- 使用 16 位 (R5G6B5) 后备缓冲区而不是 32 位后备缓冲区 (R8G8B8A8),这可以减少带宽使用,但会损失一些视觉质量
- 减少混合表面的面积:在你的情况下,这意味着你应该按 "blocks" 组织你的文本,每个块尽可能适合 IOS 中这张图片中的文本docs :
- 想办法在您的设备上禁用垂直同步/使用三重缓冲。如果您有权访问 Vivante GPU 文档(我没有),这可能会在里面进行描述。
第 3 点是最好的做法(这是我开发的大多数手机游戏所做的),但是这需要一些不可忽略的额外工作。第 1、2 和 3 点更直接但仅 "half solutions".
我正在尝试通过在 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 的建议:
- 使用降低的分辨率,例如:1280x720 而不是 1920x1080,这将减少带宽使用和片段处理。当然不理想,但这可以用作确认您有带宽或片段问题的测试(如果在降低分辨率后恢复 60fps,则说明您有此类问题)
- 使用 16 位 (R5G6B5) 后备缓冲区而不是 32 位后备缓冲区 (R8G8B8A8),这可以减少带宽使用,但会损失一些视觉质量
- 减少混合表面的面积:在你的情况下,这意味着你应该按 "blocks" 组织你的文本,每个块尽可能适合 IOS 中这张图片中的文本docs :
- 想办法在您的设备上禁用垂直同步/使用三重缓冲。如果您有权访问 Vivante GPU 文档(我没有),这可能会在里面进行描述。
第 3 点是最好的做法(这是我开发的大多数手机游戏所做的),但是这需要一些不可忽略的额外工作。第 1、2 和 3 点更直接但仅 "half solutions".