Skip to content

(WIP) EGLStreams init on proprietary nvidia driver (chatgpt reply)

matik edited this page Nov 15, 2025 · 2 revisions

This was an answer from chatgpt:

Setting Up EGL in a Wayland Compositor with NVIDIA Proprietary Driver

Setting up EGL with the proprietary NVIDIA driver in a Wayland compositor is a bit more involved than with open-source drivers like Mesa. This is because the proprietary NVIDIA driver uses a different approach, relying on EGLStreams instead of traditional GBM/DRM. Here's a step-by-step breakdown of how this process works and how a Wayland compositor can set up an EGL display with NVIDIA's proprietary drivers.

Key Concepts

  1. EGLStreams:
    The NVIDIA proprietary driver uses EGLStreams to provide a connection between the graphics hardware and display buffers. EGLStreams are NVIDIA's mechanism for direct rendering to a display (via Wayland, X11, etc.).

  2. EGLDevice:
    The NVIDIA driver introduces EGLDevice and EGLStream extensions to EGL, allowing the compositor to interact with the GPU device directly, bypassing the need for traditional GBM buffers used by most Wayland compositors.

  3. Wayland Compositors:
    In Wayland, a compositor is responsible for managing windows and rendering them to the screen. With NVIDIA's proprietary driver, the compositor needs to initialize EGL and use NVIDIA's EGLStream extensions to present the contents to the screen.


🧰 High-Level Steps to Set Up EGL in a Wayland Compositor

  1. Initialize EGL with NVIDIA Device:
    You must use eglQueryDevicesEXT() and eglGetPlatformDisplayEXT() to create an EGL display tied to the NVIDIA device. This is where the EGL stream begins.

  2. Configure and Create the EGLContext:
    Once the display is set up, the compositor must configure the EGL context for rendering. This involves setting up an appropriate EGLConfig that supports OpenGL ES or Vulkan, depending on the compositor's needs.

  3. Use EGLStream for Buffer Management:
    The compositor will use EGLStream to manage buffers, create surfaces, and eventually display the contents to the screen.

  4. Synchronization with Wayland:
    The compositor needs to manage synchronization between the rendering process and the display server (Wayland). Typically, this is done using EGL sync objects or using Wayland's protocol for buffer synchronization.


🎬 Step-by-Step Example for Setting Up EGL with NVIDIA in a Wayland Compositor

Here’s a simplified example that demonstrates how a Wayland compositor might set up the EGL display using NVIDIA’s proprietary driver. This assumes you're familiar with Wayland compositor development and that you have access to the necessary NVIDIA driver extensions.

1. Query for NVIDIA EGL Devices

First, the compositor queries the available EGL devices. This is done via the eglQueryDevicesEXT() function, which returns a list of devices available for use by EGL.

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdio.h>

EGLDeviceEXT egl_device;

void init_egl_device() {
    PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (void*)eglGetProcAddress("eglQueryDevicesEXT");
    if (!eglQueryDevicesEXT) {
        printf("EGL device extension not available!\n");
        return;
    }

    EGLDeviceEXT devices[4];
    EGLint num_devices;
    EGLint major_version, minor_version;

    // Query available devices
    if (eglQueryDevicesEXT(4, devices, &num_devices) == EGL_FALSE) {
        printf("Failed to query EGL devices\n");
        return;
    }

    printf("Found %d EGL devices\n", num_devices);
    egl_device = devices[0]; // Choose the first device for now
}

2. Initialize EGL with the NVIDIA Device

Once the EGLDevice is chosen, the next step is to initialize the EGL display. This is done using eglGetPlatformDisplayEXT() to obtain a display connection for the specific EGL device.

EGLDisplay egl_display;

void init_egl_display() {
    egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, egl_device, NULL);
    if (egl_display == EGL_NO_DISPLAY) {
        printf("Failed to get EGL display\n");
        return;
    }

    EGLint major, minor;
    if (!eglInitialize(egl_display, &major, &minor)) {
        printf("Failed to initialize EGL\n");
        return;
    }

    printf("EGL initialized: version %d.%d\n", major, minor);
}

3. Create EGLConfig and EGLContext

Next, create the EGLConfig and EGLContext needed for rendering.

EGLConfig egl_config;
EGLContext egl_context;

void create_egl_context() {
    EGLint config_attribs[] = {
        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_NONE
    };

    EGLint context_attribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2, // Use OpenGL ES 2.0
        EGL_NONE
    };

    EGLint num_configs;
    if (!eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &num_configs)) {
        printf("Failed to choose EGL config\n");
        return;
    }

    egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribs);
    if (egl_context == EGL_NO_CONTEXT) {
        printf("Failed to create EGL context\n");
        return;
    }

    printf("EGL context created\n");
}

4. Set Up an EGLStream for Buffer Management

This part involves setting up EGLStreams for buffer management and rendering. You will need to use NVIDIA-specific extensions to create a stream-based surface.

#include <EGL/eglext.h>

// Function to set up a stream-based surface (specific to NVIDIA's EGLStream)
EGLStreamKHR egl_stream;
EGLSurface egl_surface;

void create_egl_stream_surface() {
    PFNEGLCREATESTREAMKHRPROC eglCreateStreamKHR = (void*)eglGetProcAddress("eglCreateStreamKHR");
    if (!eglCreateStreamKHR) {
        printf("EGLStream extension not available!\n");
        return;
    }

    egl_stream = eglCreateStreamKHR(egl_display, NULL);
    if (egl_stream == EGL_NO_STREAM_KHR) {
        printf("Failed to create EGL stream\n");
        return;
    }

    egl_surface = eglCreateStreamProducerSurfaceKHR(egl_display, egl_config, egl_stream, NULL);
    if (egl_surface == EGL_NO_SURFACE) {
        printf("Failed to create EGL surface\n");
        return;
    }

    printf("EGL stream and surface created\n");
}

5. Render to the Stream and Swap Buffers

Once the context, surface, and stream are set up, you can render content to the surface and swap buffers.

void render_to_surface() {
    glClearColor(0.0, 1.0, 0.0, 1.0);  // Clear with green
    glClear(GL_COLOR_BUFFER_BIT);

    // Do rendering here...

    eglSwapBuffers(egl_display, egl_surface);
    printf("Frame rendered and swapped\n");
}

6. Synchronization with Wayland

In a full-fledged Wayland compositor, synchronization is key. You would need to manage synchronization between Wayland buffers and EGL streams. This is done using synchronization primitives like EGL sync objects or Wayland-specific mechanisms.

// Wayland synchronization (simplified for demo)
void sync_with_wayland() {
    // Synchronization code with Wayland protocol goes here
}

🎬 Conclusion

The steps above outline how to initialize EGL with NVIDIA’s proprietary driver in a Wayland compositor. The critical part is using EGLStreams to interface with the NVIDIA GPU, bypassing traditional GBM or DRM buffers. This approach allows the compositor to render to a display using the proprietary NVIDIA driver, but it requires handling NVIDIA-specific extensions like EGL_EXT_platform_device, EGL_EXT_device_base, and EGL_KHR_stream.

In a production Wayland compositor, you would need to integrate this process more deeply into the Wayland protocol, including proper synchronization, buffer management, and event handling. NVIDIA’s proprietary stack is more closed and less flexible than open-source stacks, so using EGLStreams requires care and proper configuration.

Clone this wiki locally