2

android NDK 可能实现最快的 2D 帧速率,包括我的尝试,有更好的选择吗?

我使用 NDK 和 OpenGL ES 2.0 将帧显示为 GL_TRIANGLE_STRIP 上的纹理。这是在 HTC Desire 上完成的,硬件与 Nexus One 相同。我尝试加载多个 GL_RGBA 纹理并在纹理之间切换,因为单个纹理的正常填充率低得令人失望:

  • 1 个纹理:4.78 fps
  • 2 个纹理:19.68 fps
  • 3 种纹理:20.18 fps
  • 4 种纹理:28.52 fps
  • 5 种纹理:29.01 fps
  • 6 种纹理:30.32 fps

我认为即使是 30.32 fps RGBA 仍然太慢。

那么这是实现最快 2D 帧速率(具有相同质量)的方法吗?有什么建议可以加快速度吗?

以下是相关代码,它基于 hello-gl2 NDK 示例:

=== GL2JNIView.java:

init(false, 0, 0);
ConfigChooser(5, 6, 5, 0, depth, stencil);

=== gl_code.cpp :

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <android/log.h>

#include <stdlib.h>
#include <time.h>

typedef unsigned char byte;

static int view_width, view_height;
static byte* framebuffer;
static int framebuffer_size;
static GLuint texture_id[6];
static const char* vertexSrc =
 "precision highp float;\n"
 "precision highp int;\n"
 "attribute vec4 vertexCoords;\n"
 "attribute vec2 textureCoords;\n"
 "varying vec2 f_textureCoords;\n"
 "void main() {\n"
 "  f_textureCoords = textureCoords;\n"
 "  gl_Position = vertexCoords;\n"
 "}\n";
static const char* fragmentSrc  =
 "precision highp float;\n"
 "precision highp int;\n"
 "uniform sampler2D texture;\n"
 "varying vec2 f_textureCoords;\n"
 "void main() {\n"
 "  gl_FragColor = texture2D(texture, f_textureCoords);\n"
 "}\n";
static GLuint shaderProgram;
static GLint attrib_vertexCoords;
static GLint attrib_textureCoords;
static GLint uniform_texture;
static const GLfloat vertexCoords[] = {-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0};
static const GLfloat textureCoords[] = {0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0};

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height) {
 view_width = width;
 view_height = height;

 framebuffer_size = 4*view_width*view_height;
 framebuffer = (byte*)calloc(framebuffer_size, sizeof(byte));
 for (int i = 0; i < framebuffer_size; i++) framebuffer[i] = 0;

 glViewport(0, 0, view_width, view_height);

 glGenTextures(6, &texture_id[0]);
 glActiveTexture(GL_TEXTURE0);
 glBindTexture(GL_TEXTURE_2D, texture_id[0]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glActiveTexture(GL_TEXTURE1);
 glBindTexture(GL_TEXTURE_2D, texture_id[1]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glActiveTexture(GL_TEXTURE2);
 glBindTexture(GL_TEXTURE_2D, texture_id[2]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glActiveTexture(GL_TEXTURE3);
 glBindTexture(GL_TEXTURE_2D, texture_id[3]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glActiveTexture(GL_TEXTURE4);
 glBindTexture(GL_TEXTURE_2D, texture_id[4]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glActiveTexture(GL_TEXTURE5);
 glBindTexture(GL_TEXTURE_2D, texture_id[5]);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);

 shaderProgram = glCreateProgram();
  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexSrc, NULL);
  glCompileShader(vertexShader);
 glAttachShader(shaderProgram, vertexShader);
  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentSrc, NULL);
  glCompileShader(fragmentShader);
 glAttachShader(shaderProgram, fragmentShader);
 glLinkProgram(shaderProgram);
 glUseProgram(shaderProgram);

 uniform_texture = glGetUniformLocation(shaderProgram, "texture");
 glUniform1i(uniform_texture, 0);

 attrib_vertexCoords = glGetAttribLocation(shaderProgram, "vertexCoords");
 glEnableVertexAttribArray(attrib_vertexCoords);
 glVertexAttribPointer(attrib_vertexCoords, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords);

 attrib_textureCoords = glGetAttribLocation(shaderProgram, "textureCoords");
 glEnableVertexAttribArray(attrib_textureCoords);
 glVertexAttribPointer(attrib_textureCoords, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj) {
 static int frame_count = 0;
 static clock_t last_time = clock();
 static int last_frame_count = 0;

 frame_count++;
 if (clock()-last_time > 1e7) {
  __android_log_print(ANDROID_LOG_INFO, "libgl2jni", "fps: %f", ((float)frame_count-last_frame_count)/(clock()-last_time)*1e6);
  last_time = clock();
  last_frame_count = frame_count;
 }

 static byte val = 0;
 val++;
 if (val == 256) val = 0;
 for (int i = 0; i < framebuffer_size; i++) framebuffer[i] = val;

 int tst = frame_count%6;
 if (tst == 0) {
  glActiveTexture(GL_TEXTURE0);
 } else if (tst == 1) {
  glActiveTexture(GL_TEXTURE1);
 } else if (tst == 2) {
  glActiveTexture(GL_TEXTURE2);
 } else if (tst == 3) {
  glActiveTexture(GL_TEXTURE3);
 } else if (tst == 4) {
  glActiveTexture(GL_TEXTURE4);
 } else if (tst == 5) {
  glActiveTexture(GL_TEXTURE5);
 }
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
4

2 回答 2

9

我意识到您的问题已经很老了,您可能已经解决了它或转向了其他问题,但是如果其他人遇到此问题,我会给出建议。

首先,glTexImage2D需要图形子系统在每次调用它时执行纹理对象的释放内存和重新分配,因为纹理参数可以在调用之间改变。优化的驱动程序可能会查看宽度、高度和格式,如果它们都相同,则可以跳过重新分配,但 Android 驱动程序实现者实际上不太可能这样做。

要完全避免纹理重新分配,您可以使用glTexSubImage2D替换完整的位图,或者只是其中的一部分。如果将其与上述纹理缓冲方案结合使用,您应该会看到相当大的速度提升。您甚至可以扩展它以检测显示器的修改区域,并仅更新帧之间已更改的矩形部分。

总而言之,将纹理初始化代码更改为glTexImage2D使用 NULL 位图指针调用,因此 OpenGL 只为纹理分配内存,实际上并不像这样将任何数据复制到其中:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width, view_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

然后更新游戏循环中的每一帧:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_width, view_height, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer);
于 2011-04-04T11:34:24.140 回答
0

屏幕刷新率有效地限制了可能的最快屏幕帧率,这是特定于供应商的。我的猜测是至少 60 Hz(每秒 60 帧)。离屏渲染不受刷新率的限制,取决于您执行的计算强度。带有一些 gl 代码的无休止循环可能运行速度明显快于 60 Hz,或者,就此而言,速度更慢。

于 2014-10-13T08:10:44.803 回答