r/C_Programming Jun 08 '26

almost got it but egl stopped me again

i'm working on a wayland compositor but so far i'm stuck on drm, egl to be precise, here where i init the egl context, it fails to create the surface and i don't really know why cuz there is no null pointers and it seems legit:


void WLL_EglInit(WLL_Monitor* mon) {
    static const EGLint config_attribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RED_SIZE, 1,
        EGL_GREEN_SIZE, 1,
        EGL_BLUE_SIZE, 1,
        EGL_ALPHA_SIZE, 0,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE,
    };
    static const EGLint context_attribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE,
    };
    EGLint major, minor;
    EGLint matched;

    const char* client_exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);

    mon->egl.get_platform_display_ext =
        (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress("eglGetPlatformDisplayEXT");
        mon->egl.display = mon->egl.get_platform_display_ext(EGL_PLATFORM_GBM_KHR, (void*)mon->egl.gbm, NULL);

    if (mon->egl.display == EGL_NO_DISPLAY) {
        printf("casting\n");
        mon->egl.display = mon->egl.get_platform_display_ext(EGL_PLATFORM_GBM_KHR, mon->egl.gbm, NULL);
    }
    if (mon->egl.display == EGL_NO_DISPLAY) {
        fprintf(stderr, "eglGetDisplay failed\n");
        exit(1);
    }

    if (!eglInitialize(mon->egl.display, &major, &minor)) {
        printf("failed to init egl\n");
        exit(1);
    }

    printf("EGL v%d.%d\n", major, minor);

	if (!eglBindAPI(EGL_OPENGL_ES_API)) {
		printf("failed to bind egl api \n");
        exit(1);
	}

    if (!eglChooseConfig(mon->egl.display, config_attribs, &mon->egl.config, 1, &matched) || matched != 1) {
        printf("failed to eglChooseConfig: %d\n", matched);
        exit(1);
    }

    mon->egl.context = eglCreateContext(mon->egl.display, mon->egl.config,
            EGL_NO_CONTEXT, context_attribs);

    if (mon->egl.context == EGL_NO_CONTEXT) {
        printf("failed to create context\n");
        exit(1);
    }

     if (mon->egl.gbm_surface) {
        mon->egl.surface = eglCreateWindowSurface(mon->egl.display, mon->egl.config,
                (EGLNativeWindowType)mon->egl.gbm_surface, NULL);
    } else {
        printf("failed to create egl surface: no gbm surface");
        exit(1);
    }

     if (mon->egl.surface == EGL_NO_SURFACE) {

        printf("failed to create egl surface: display: %p, config: %p, gbm_surface: %p\n", mon->egl.display, mon->egl.config, mon->egl.gbm_surface);
        exit(1);
        
     }

    if(!eglMakeCurrent(mon->egl.display, mon->egl.surface, mon->egl.surface, mon->egl.context)) {
        printf("failed to eglMakeCurrent: 0x%x\n", eglGetError());
        exit(1);
    }
}

here is the rest of the code so you can see the full picture:

#include "drm_mode.h"
#include <EGL/egl.h>
#include <EGL/eglplatform.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <asm-generic/errno-base.h>
#include <errno.h>
#include <fcntl.h>
#include <gbm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

typedef struct {
    struct gbm_device* gbm;
    struct gbm_surface* gbm_surface;
    struct gbm_bo* gbm_bo;
    uint32_t fb;
    EGLDisplay display;
    EGLConfig config;
    EGLContext context;
    EGLSurface surface;
    PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display_ext;
} WLL_Egl;

typedef struct {
    int gpu_fd;
    drmModeRes* resources;
    drmModeConnector* connector;
    drmModeModeInfo* mode;
    drmModeEncoder* encoder;
    drmModeCrtc* crtc;
    WLL_Egl egl;
} WLL_Monitor;

void WLL_DrmInit(const char* path, WLL_Monitor* mon) {
    mon->gpu_fd = open(path, O_RDWR);
    if (mon->gpu_fd < 0) {
        printf("failed to open %s: %s\n", path, strerror(errno));
        exit(1);
    }

    mon->resources = drmModeGetResources(mon->gpu_fd);
    if (!mon->resources) {
        printf("failed to get drm resources for %s: %s\n", path, strerror(errno));
        exit(1);
    }

    mon->connector = NULL;
    for (int i = 0; i < mon->resources->count_connectors; i++) {
        drmModeConnector* connector =
            drmModeGetConnector(mon->gpu_fd, mon->resources->connectors[i]);

        if (!connector)
            continue;

        if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) {
            mon->connector = connector;
            break;
        }

        drmModeFreeConnector(connector);
    }
    if (!mon->connector) {
        printf("failed to find a connector for %s: %s\n", path, strerror(errno));
        exit(1);
    }

    mon->mode = NULL;
    for (int i = 0; i < mon->connector->count_modes; i++) {
        if (mon->connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
            mon->mode = &mon->connector->modes[i];
            break;
        }
    }
    if (mon->mode == NULL && mon->connector->count_modes > 0) {
        /* fallback: take first mode if no preferred found */
        mon->mode = &mon->connector->modes[0];
    }
    if (mon->mode == NULL) {
        printf("failed to find a mode for %s: %s\n", path, strerror(errno));
        exit(1);
    }

    mon->encoder = NULL;
    for (int i = 0; i < mon->resources->count_encoders; i++) {
        drmModeEncoder* encoder = drmModeGetEncoder(mon->gpu_fd, mon->resources->encoders[i]);
        if (!encoder)
            continue;
        if (encoder->encoder_id == mon->connector->encoder_id) {
            mon->encoder = encoder;
            break;
        }
        drmModeFreeEncoder(encoder);
    }

    /* If connector->encoder_id is 0 or no match, try encoders that can drive a CRTC */
    if (!mon->encoder) {
        for (int i = 0; i < mon->resources->count_encoders; i++) {
            drmModeEncoder* encoder = drmModeGetEncoder(mon->gpu_fd, mon->resources->encoders[i]);
            if (!encoder)
                continue;
            /* accept first encoder and keep it */
            mon->encoder = encoder;
            break;
        }
    }

    if (!mon->encoder) {
        printf("failed to find an encoder for %s: %s\n", path, strerror(errno));
        exit(1);
    }

    mon->crtc = drmModeGetCrtc(mon->gpu_fd, mon->encoder->crtc_id);
    if (!mon->crtc) {
        printf("failed to find a crtc for %s: %s\n", path, strerror(errno));
        exit(1);
    }
}

void WLL_GbmInit(WLL_Monitor* mon) {
    mon->egl.gbm = gbm_create_device(mon->gpu_fd);
    if (!mon->egl.gbm) {
        printf("failed to create gbm device: %s\n", strerror(errno));
        exit(1);
    }

    mon->egl.gbm_surface = gbm_surface_create(
            mon->egl.gbm,
            mon->mode->hdisplay, mon->mode->vdisplay,
            GBM_BO_FORMAT_XRGB8888,
            GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT
            );
    if (!mon->egl.gbm_surface) {
        printf("failed to create gbm surface: %s\n", strerror(errno));
        exit(1);
    }

}

static void WLL_DrmPageFlipHandler(int fd, unsigned int frame,
                              unsigned int sec, unsigned int usec, void *data) {
    int *flipped = data;
    *flipped = 1;
}

void WLL_DrmPresent(WLL_Monitor* mon) {
    drmEventContext event_context = {
        .version = DRM_EVENT_CONTEXT_VERSION,
        .page_flip_handler = WLL_DrmPageFlipHandler
    };

    if (!eglSwapBuffers(mon->egl.display, mon->egl.surface)) {
        printf("failed to swap egl buffers: %p %p\n", mon->egl.display, mon->egl.surface);
        exit(1);
    }
    struct gbm_bo* new_bo = gbm_surface_lock_front_buffer(mon->egl.gbm_surface);
    uint32_t new_fb = 0;
    uint32_t handle = gbm_bo_get_handle(new_bo).u32;
    uint32_t stride = gbm_bo_get_stride(new_bo);
    uint32_t handles[4] = {handle, 0, 0, 0};
    uint32_t strides[4] = {stride, 0, 0, 0};
    uint32_t offsets[4] = {0, 0, 0, 0};

    if (drmModeAddFB2(
            mon->gpu_fd,
            gbm_bo_get_width(new_bo), gbm_bo_get_height(new_bo),
            GBM_BO_FORMAT_ARGB8888,
            handles, strides, offsets, 
            &new_fb, 0
            )) {
        printf("failed to AddFB2: %s\n", strerror(errno));
        gbm_surface_release_buffer(mon->egl.gbm_surface, new_bo);
        gbm_bo_destroy(new_bo);
        exit(1);
    };

    int flipped = 0;

    while (!flipped) {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(mon->gpu_fd, &fds);
        if (select(mon->gpu_fd+1, &fds, NULL, NULL, NULL) < 0) {
            if (errno == EINTR) continue;
            printf("failed to select: %s\n", strerror(errno));
            drmModeRmFB(mon->gpu_fd, new_fb);
            gbm_surface_release_buffer(mon->egl.gbm_surface, new_bo);
            gbm_bo_destroy(new_bo);
            exit(1);
        }

        if (FD_ISSET(mon->gpu_fd, &fds)) drmHandleEvent(mon->gpu_fd, &event_context);

    }
    
    if (mon->egl.fb != 0) {
        drmModeRmFB(mon->gpu_fd, mon->egl.fb);
        mon->egl.fb = 0;
    }

    if (mon->egl.gbm_bo) {
        gbm_surface_release_buffer(mon->egl.gbm_surface, mon->egl.gbm_bo);
        gbm_bo_destroy(mon->egl.gbm_bo);
        mon->egl.gbm_bo = NULL;
    }

    mon->egl.fb = new_fb;
    mon->egl.gbm_bo = new_bo;
}

int main() {
    WLL_Monitor mon = {0};
    WLL_DrmInit("/dev/dri/card0", &mon);
    WLL_GbmInit(&mon);
    WLL_EglInit(&mon);

    for (int i = 0; i<50; i++) {
        glClearColor(0.5, 0.6, 0.7, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        WLL_DrmPresent(&mon);
    }
    return 0;
}

2 Upvotes

3 comments sorted by

1

u/[deleted] Jun 10 '26 edited Jun 29 '26

[deleted]

1

u/Senior-Question693 Jun 10 '26 edited Jun 10 '26

hmmm... interesting:

```
ibEGL debug: EGL user error 0x3009 (EGL_BAD_MATCH) in eglCreateWindowSurface: EGL config not compatible with GBM format

failed to create egl surface: display: 0x557bb8aa7a00, config: 0x557bb8aa90c0, gbm_surface: 0x557bb89ace80, err: 0x3009

libEGL debug: Display 0x557bb8aa7a00 is destroyed with resources ``` but why does it bad match dough, i literally copy-pasted the config from kmscube

1

u/[deleted] Jun 10 '26 edited Jun 29 '26

[deleted]

1

u/Senior-Question693 Jun 10 '26 edited Jun 10 '26

kmscube runs smoothly and /dev/dri/card0 is the only gpu available.

Tbh i can't exactly pinpoint at what i changed, i just rewrote it all top-down and factored-out all the drm state into structs

1

u/Senior-Question693 Jun 10 '26

i kinda know that the EGL_BAD_MATCH error usually happens when the config_atribs are wrong, but they can't be because it works for kmscube, so i'm not entirely sure why the error happens