Full minimal reproducer
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <gbm.h>
#include <epoxy/egl.h>
#include <epoxy/gl.h>
#define TARGET_SIZE 256
struct gpu_context {
int fd;
struct gbm_device *gbm;
EGLDisplay dpy;
EGLContext ctx;
};
void RenderTargetInit(const char *name, struct gpu_context *ctx)
{
ctx->fd = open(name, O_RDWR);
assert(ctx->fd >= 0);
ctx->gbm = gbm_create_device(ctx->fd);
assert(ctx->gbm != NULL);
assert((ctx->dpy = eglGetDisplay(ctx->gbm)) != EGL_NO_DISPLAY);
assert(eglInitialize(ctx->dpy, NULL, NULL) == EGL_TRUE);
eglBindAPI(EGL_OPENGL_API);
assert((ctx->ctx = eglCreateContext(ctx->dpy, NULL, EGL_NO_CONTEXT, NULL)) != EGL_NO_CONTEXT);
assert(eglMakeCurrent(ctx->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->ctx) == EGL_TRUE);
}
void Render(float r, float g, float b, float a) {
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
glFinish();
}
void Check(GLuint tex_out, float r, float g, float b, float a) {
uint32_t *data = calloc(1, TARGET_SIZE * TARGET_SIZE * 4);
assert(data);
#if 1
glBindTexture(GL_TEXTURE_2D, tex_out);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
#else
GLuint fb_out;
glGenFramebuffers(1, &fb_out);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_out, 0);
glReadPixels(0, 0, TARGET_SIZE, TARGET_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, data);
#endif
uint32_t expect =
(((uint32_t)(a * 255) & 0xff) << 24) |
(((uint32_t)(b * 255) & 0xff) << 16) |
(((uint32_t)(g * 255) & 0xff) << 8) |
(((uint32_t)(r * 255) & 0xff) << 0);
for (int i = 0; i < TARGET_SIZE * TARGET_SIZE; i++) {
if (data[i] != expect) {
printf("check buffer fail at %d: %x/%x\n", i, data[i], expect);
return;
}
}
printf("check buffer success\n");
free(data);
}
int main(int argc, char **argv)
{
char *f1 = "/dev/dri/renderD128";
if (argc > 1)
f1 = argv[1];
struct gpu_context gpu;
RenderTargetInit(f1, &gpu);
GLuint fbid;
glGenFramebuffers(1, &fbid);
glBindFramebuffer(GL_FRAMEBUFFER, fbid);
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TARGET_SIZE, TARGET_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texid, 0);
Render(1, 0, 1, 0);
EGLImage image = eglCreateImage(gpu.dpy, gpu.ctx, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(uint64_t)texid, NULL);
assert(image != EGL_NO_IMAGE);
GLuint out_tex = 0;
glGenTextures(1, &out_tex);
glBindTexture(GL_TEXTURE_2D, out_tex);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
assert(glGetError() == GL_NO_ERROR);
Check (out_tex, 1, 0, 1, 0);
assert(glGetError() == GL_NO_ERROR);
return 0;
}
Above is a code sample ready-to-compile with -lepoxy -lgbm
.
Here are the highlight points:
- Allocate texture A
- Bind framebuffer F to texture A
- Draw to framebuffer F
- Create EGLImage I from texture A
- Create texture B from EGLImage I
- Read back texture B
You may also move step 3 down to after 5, and the result will be the same.
If you read back the texture using glGetTexImage
you will get a black image.
If instead you bind the texture to a new framebuffer and use glReadPixels
, you will correctly get the image you drew.
This only happens if the EGLDisplay is created with eglGetDisplay(gbm_dev)
, or with eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, gbm_dev, NULL)
, while using eglGetDisplay(EGL_DEFAULT_DISPLAY)
gives the correct result.