Wayland环境下将Cairo表面数据绑定到EGL及eglBindTexImage报错解决
问题原因
eglBindTexImage 仅支持pbuffer或pixmap类型的EGL表面,你当前用的是窗口表面(EGL_WINDOW_BIT类型),本身就不支持绑定为纹理,所以必然触发EGL_BAD_SURFACE错误。同时你完全不需要调用这个接口,也不需要额外创建pbuffer表面,你的实现思路走了弯路。
解决方法
你已经将Cairo绘制的像素数据通过glTexImage2D上传到了GL纹理中,只需要将这个纹理绘制到当前绑定的窗口表面即可,步骤如下:
- 补充纹理参数设置,OpenGL ES默认使用Mipmap过滤,你没有生成Mipmap的情况下纹理会显示为纯黑,上传纹理后添加:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- 准备最简单的全屏绘制着色器,用于把纹理贴到窗口上:
// 顶点着色器,输入位置和UV,直接输出UV const char* vert_shader = "attribute vec2 aPos;\n" "attribute vec2 aUV;\n" "varying vec2 vUV;\n" "void main() {\n" " gl_Position = vec4(aPos, 0.0, 1.0);\n" " vUV = aUV;\n" "}"; // 片段着色器,采样纹理输出 const char* frag_shader = "precision mediump float;\n" "varying vec2 vUV;\n" "uniform sampler2D uTex;\n" "void main() {\n" " gl_FragColor = texture2D(uTex, vUV);\n" "}";
你可以自行补充基础的着色器编译、链接逻辑和错误检查代码。
3. 绘制全屏矩形,替换你原来的eglBindTexImage相关代码:
// 全屏四边形的顶点和UV,坐标范围覆盖整个窗口 float vertices[] = { // 位置(x,y) UV(u,v) -1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }; unsigned int indices[] = {0, 1, 2, 0, 2, 3}; // 绑定顶点数据 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertices); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertices + 2); glEnableVertexAttribArray(1); // 绑定着色器和纹理 glUseProgram(shader_program); glUniform1i(glGetUniformLocation(shader_program, "uTex"), 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); // 绘制 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
- 绘制完成后直接调用
eglSwapBuffers即可,不需要其他操作。
额外注意事项
- Cairo的
CAIRO_FORMAT_ARGB32格式是预乘Alpha,且字节顺序为BGRA,你当前用GL_RGBA上传会出现颜色颠倒的问题,可将glTexImage2D的格式参数改为GL_BGRA_EXT(绝大多数EGL实现都支持该扩展),或在片段着色器中交换R和B通道。 - 如果你确实需要用到离屏渲染再拷贝到窗口的逻辑,才需要创建pbuffer表面,创建时需要把EGLConfig的
EGL_SURFACE_TYPE属性加上EGL_PBUFFER_BIT,再调用eglCreatePbufferSurface生成pbuffer,绘制完成后用glReadPixels读回或者绑定为纹理再绘制到窗口,对你当前的场景来说完全没有必要,反而会增加性能开销。
内容的提问来源于stack exchange,提问作者hardboiled65
相关产品推荐
相关产品推荐

