summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/egl_frame.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-21 15:05:29 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-21 15:05:29 -0400
commit988b8345f0b28b880aaa4384d13064c1de5b209f (patch)
tree1b0bf74f6627d398d1595da6a345c2a43867467a /tools/guest-diag/egl_frame.c
parente60bdd575c1ced490116a49035ac0483c0818860 (diff)
downloadopen-iov-988b8345f0b28b880aaa4384d13064c1de5b209f.tar.gz
open-iov-988b8345f0b28b880aaa4384d13064c1de5b209f.zip
tools/lg + docs: guest frame producer path (CUDA -> ivshmem -> host) and the Looking Glass status
- tools/cuda-ab/frame_producer.cu: renders an animated Julia frame on the guest GPU and publishes it into the ivshmem BAR (the kvmfr 128 MiB window) with a small header + RGBA payload. - tools/lg/frame_reader.py: host-side reader of /dev/shm/kvmfr-<uuid> that saves each published frame as a PPM (the transport half of the frame path). - tools/guest-diag/egl_frame.c: EGL probe (device/surfaceless/default platforms) - live result: eglQueryDevicesEXT sees the NVIDIA device and eglInitialize succeeds, but eglMakeCurrent fails EGL_BAD_ALLOC (0x3003), the same VASpace-allocation wall as Xorg's 'Failed to allocate VASpace object'. - docs/05-operations/06: the state, the blockers and the exact remaining pieces (guest GL VASpace allocation; LGMP session producer).
Diffstat (limited to 'tools/guest-diag/egl_frame.c')
-rw-r--r--tools/guest-diag/egl_frame.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/tools/guest-diag/egl_frame.c b/tools/guest-diag/egl_frame.c
new file mode 100644
index 0000000..ed10e39
--- /dev/null
+++ b/tools/guest-diag/egl_frame.c
@@ -0,0 +1,182 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+#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);
+}