// SPDX-License-Identifier: BSD-3-Clause /* * egl_frame.c - minimal EGL/GLES2 offscreen render test for the vGPU guest. * * Tries, in order: the EGL_EXT_platform_device platform with each enumerated * EGLDeviceEXT, EGL_MESA_platform_surfaceless, then EGL_DEFAULT_DISPLAY. * For the first display that initializes it creates an offscreen pbuffer * context, renders colour bands into an FBO, reads them back and writes a * PPM. One greppable line per stage. * * Build (guest): gcc -O2 -o egl_frame egl_frame.c -lEGL -lGLESv2 */ #include #include #include #include #include #include #define W 320 #define H 240 static EGLDisplay try_display(const char *name, EGLDisplay dpy, const char *dev_name) { EGLint major = 0, minor = 0; if (dpy == EGL_NO_DISPLAY) { printf("EGL_PLATFORM %s -> no display\n", name); return EGL_NO_DISPLAY; } if (!eglInitialize(dpy, &major, &minor)) { printf("EGL_PLATFORM %s -> init fail 0x%x\n", name, eglGetError()); return EGL_NO_DISPLAY; } printf("EGL_PLATFORM %s ok %d.%d vendor=%s%s%s\n", name, major, minor, eglQueryString(dpy, EGL_VENDOR), dev_name ? " device=" : "", dev_name ? dev_name : ""); return dpy; } static EGLDisplay get_display(void) { PFNEGLGETPLATFORMDISPLAYEXTPROC gp = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress( "eglGetPlatformDisplayEXT"); PFNEGLQUERYDEVICESEXTPROC qd = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress( "eglQueryDevicesEXT"); EGLDisplay dpy; if (gp && qd) { EGLDeviceEXT devs[8]; EGLint n = 0; int i; if (qd(8, devs, &n) && n > 0) { printf("EGL_DEVICES n=%d\n", n); for (i = 0; i < n; i++) { EGLDisplay d = gp(EGL_PLATFORM_DEVICE_EXT, devs[i], NULL); dpy = try_display("device", d, "enum"); if (dpy != EGL_NO_DISPLAY) return dpy; } } else { printf("EGL_DEVICES n=0\n"); } if (gp) { dpy = try_display("surfaceless", gp(EGL_PLATFORM_SURFACELESS_MESA, NULL, NULL), NULL); if (dpy != EGL_NO_DISPLAY) return dpy; } } return try_display("default", eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL); } static int render(EGLDisplay dpy) { EGLConfig cfg; EGLint ncfg = 0; EGLSurface surf; EGLContext ctx; GLuint fbo = 0, tex = 0; unsigned char *px; FILE *f; int i; EGLint attrs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; EGLint pattrs[] = { EGL_WIDTH, W, EGL_HEIGHT, H, EGL_NONE }; EGLint cattrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; if (!eglChooseConfig(dpy, attrs, &cfg, 1, &ncfg) || ncfg < 1) { printf("EGL_CONFIG_FAIL err=0x%x\n", eglGetError()); return 1; } surf = eglCreatePbufferSurface(dpy, cfg, pattrs); if (surf == EGL_NO_SURFACE) { printf("EGL_PBUFFER_FAIL err=0x%x\n", eglGetError()); return 1; } ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, cattrs); if (ctx == EGL_NO_CONTEXT) { printf("EGL_CONTEXT_FAIL err=0x%x\n", eglGetError()); return 1; } if (!eglMakeCurrent(dpy, surf, surf, ctx)) { printf("EGL_CURRENT_FAIL err=0x%x\n", eglGetError()); return 1; } printf("GL_RENDERER %s\n", glGetString(GL_RENDERER)); printf("GL_VERSION %s\n", glGetString(GL_VERSION)); glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, H, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf("GL_FBO_FAIL\n"); return 1; } for (i = 0; i < 16; i++) { glEnable(GL_SCISSOR_TEST); glScissor(0, i * H / 16, W, H / 16); glClearColor((float)i / 16.0f, 1.0f - (float)i / 16.0f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); } glFinish(); printf("GL_ERR 0x%x\n", glGetError()); px = (unsigned char *)malloc(W * H * 4); glReadPixels(0, 0, W, H, GL_RGBA, GL_UNSIGNED_BYTE, px); { const char *out = getenv("EGL_FRAME_OUT"); if (!out) out = "/tmp/egl_frame.ppm"; f = fopen(out, "wb"); if (!f) { printf("PPM_OPEN_FAIL\n"); return 1; } fprintf(f, "P6\n%d %d\n255\n", W, H); for (i = 0; i < W * H; i++) fwrite(px + i * 4, 1, 3, f); fclose(f); printf("EGL_FRAME_WRITTEN %s\n", out); } printf("EGL_FRAME_OK\n"); return 0; } int main(void) { EGLDisplay dpy; const char *ext; setvbuf(stdout, NULL, _IONBF, 0); ext = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); printf("EGL_CLIENT_EXT %s\n", ext ? ext : "(none)"); dpy = get_display(); if (dpy == EGL_NO_DISPLAY) { printf("EGL_ALL_PLATFORMS_FAILED\n"); return 1; } return render(dpy); }