本人已编写的有关Transform Feedback相关文档:
- iOS GPGPU 编程:GPU进行浮点计算并读取结果 详细描述了iOS上使用Transform Feedback的完整流程。
- NDK OpenGL ES 3 编译C/C++可执行文件(无需JNI调用)描述了NDK了配置OpenGL ES 3.0及以上版本开发环境并C++可执行文件,Android上进行通用GPU计算可参考此文档。
本文档描述了修复一份来自苹果开发者论坛的求助代码中存在的错误Transform Feedback doesn't write, crashes | Apple Developer Forums。
求助代码如下所示。
// create shader
const GLchar* vertexShaderSrc = "in float inValue; out float outValue; void main() { outValue = sqrt(inValue); } );";
GLuint shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(shader, 1, &vertexShaderSrc, 0);
glCompileShader(shader);
GLuint program = glCreateProgram();
glAttachShader(program, shader);
const GLchar* feedbackVaryings[] = { "outValue" };
glTransformFeedbackVaryings(program, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
glLinkProgram(program);
glUseProgram(program);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLfloat data[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
// create input buffer
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
GLint inputAttrib = glGetAttribLocation(program, "inValue");
glEnableVertexAttribArray(inputAttrib);
glVertexAttribPointer(inputAttrib, 1, GL_FLOAT, GL_FALSE, 0, 0);
// create output buffer
GLuint tbo;
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_STATIC_READ);
// transform
glEnable(GL_RASTERIZER_DISCARD);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, 5);
glEndTransformFeedback();
glFlush();
// write output
GLfloat *feedback;
feedback = glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(feedback), GL_MAP_READ_BIT);
NSLog(@"%f %f %f %f %f\n", feedback[0], feedback[1], feedback[2], feedback[3], feedback[4]);
提问者说feedback返回空指针,如下所述。
The value of data[] is never changed and NSLog crashes because glMapBufferRange returns a null pointer. How do you do transform feedback
现在,分析这段代码并令其可在iPhone 6上运行。猜测为简化代码,提问者并没给出Fragment Shader代码,然而,这对于OpenGL ES 3.0是必需的,所以,这应该是个Mac软件开发者。
因此,先给出片段着色器的实现及顶点着色器的细微调整。
const GLchar* vertexShaderSrc = "#version 300 es \n in float inValue; out float outValue; void main() { outValue = sqrt(inValue); } );";
// 补上Fragment Shader
const GLchar* fragmentShaderSrc = "#version 300 es void main() {}";
编译并安装至program。
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, & fragmentShaderSrc, 0);
glCompileShader(fragmentShader);
///
glAttachShader(program, fragmentShader);
修复内存映射时指定的缓冲区长度错误。整段代码的核心错误就在于此
feedback = glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(feedback), GL_MAP_READ_BIT);
///////修改为
feedback = glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(data), GL_MAP_READ_BIT);
到此,此代码可在iOS上运行。下面分析存在的其它问题。
1、输出缓冲区存在内存修饰符不合理。
// create output buffer
GLuint tbo;
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_STATIC_READ);
GL_STATIC_READ应改为GL_STATIC_DRAW
,因为整个代码只需运行一次。当然,驱动程序会忽略不合理的内存修饰符,并自动选择合适的值,但是,尽量不要依赖驱动程序的优化。
2、判断映射结果及解除内存映射
当然,这显然是段验证性代码,不应该过多要求健壮性。
if (feedback) {
NSLog(@"%f %f %f %f %f\n", feedback[0], feedback[1], feedback[2], feedback[3], feedback[4]);
}
glUnmapBuffer(GL_ARRAY_BUFFER);