EXT_platform_xcb

Name

EXT_platform_xcb

Name Strings

EGL_EXT_platform_xcb

Contributors

Yuxuan Shui <yshuiv7@gmail.com>

Contacts

Yuxuan Shui <yshuiv7@gmail.com>

Status

Complete

Version

Version 1, 2020-08-28

Number

EGL Extension #141

Extension Type

EGL client extension

Dependencies

Requires EGL_EXT_client_extensions to query its existence without
a display.

Requires EGL_EXT_platform_base.

This extension is written against the wording of version 9 of the
EGL_EXT_platform_base specification.

Overview

This extension defines how to create EGL resources from native X11
resources using the functions defined by EGL_EXT_platform_base.

The native X11 resources required by this extension are xcb resources.
All X11 types discussed here are defined by the header `xcb.h`.

New Types

None

New Procedures and Functions

None

New Tokens

Accepted as the <platform> argument of eglGetPlatformDisplayEXT:

    EGL_PLATFORM_XCB_EXT                    0x31DC

Accepted as an attribute name in the <attrib_list> argument of
eglGetPlatformDisplayEXT:

    EGL_PLATFORM_XCB_SCREEN_EXT             0x31DE

Additions to the EGL Specification

None.

New Behavior

To determine if the EGL implementation supports this extension, clients
should query the EGL_EXTENSIONS string of EGL_NO_DISPLAY.

This extension defines the same set of behaviors as EGL_EXT_platform_x11,
except Xlib types are replaced with xcb types.

To obtain an EGLDisplay backed by an X11 screen, call
eglGetPlatformDisplayEXT with <platform> set to EGL_PLATFORM_XCB_EXT. The
<native_display> parameter specifies the X11 display connection to use, and
must point to a valid xcb `xcb_connection_t` or be EGL_DEFAULT_DISPLAY.  If
<native_display> is EGL_DEFAULT_DISPLAY, then EGL will create [1] a
connection to the default X11 display. The environment variable DISPLAY
determines the default X11 display, and, unless overridden by the
EGL_PLATFORM_XCB_SCREEN_EXT attribute, the default X11 screen - as
described in the documentation of `xcb_connect`.  If the environment
variable DISPLAY is not present in this case, the result is undefined. The
value of attribute EGL_PLATFORM_XCB_SCREEN_EXT specifies the X11 screen to
use. If the attribute is omitted from <attrib_list>, and <native_display>
is not EGL_DEFAULT_DISPLAY, then screen 0 will be used. Otherwise, the
attribute's value must be a valid screen on the display connection. If the
attribute's value is not a valid screen, then an EGL_BAD_ATTRIBUTE error is
generated.

[fn1] The method by which EGL creates a connection to the default X11
display is an internal implementation detail. The implementation may use
xcb_connect, or any other method.

To obtain an on-screen rendering surface from an X11 Window, call
eglCreatePlatformWindowSurfaceEXT with a <dpy> that belongs to X11 and
a <native_window> that points to an xcb_window_t.

To obtain an offscreen rendering surface from an X11 Pixmap, call
eglCreatePlatformPixmapSurfaceEXT with a <dpy> that belongs to X11 and
a <native_pixmap> that points to an xcb_pixmap_t.

Issues

1. As xcb_connection_t doesn't carry a screen number, how should a screen be
   selected in eglGetPlatformDisplayEXT()?

   RESOLVED. The screen will be chosen with the following logic:

     * If EGL_PLATFORM_XCB_SCREEN_EXT is specified, it will always take
       precedence. Whether <native_display> is EGL_DEFAULT_DISPLAY or not.

     * Otherwise, if <native_display> is not EGL_DEFAULT_DISPLAY, then
       screen 0 will be used.

     * Otherwise, which is to say <native_display> is EGL_DEFAULT_DISPLAY.
       Then the DISPLAY environment variable will be used to determine the
       screen number. If DISPLAY contains a screen number, that will be
       used; if not, then 0 will be used.

     * If the DISPLAY environment variable is not present when
       <native_display> is EGL_DEFAULT_DISPLAY, the result will be undefined.

Example Code

// This example program creates two EGL surfaces: one from an X11 Window
// and the other from an X11 Pixmap.
//
// Compile with `cc example.c -lxcb -lEGL`.

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

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

struct my_display {
    xcb_connection_t *x11;
    int screen;
    int root_of_screen;
    EGLDisplay egl;
};

struct my_config {
    struct my_display dpy;
    xcb_colormap_t colormap;
    xcb_visualid_t visualid;
    int depth;
    EGLConfig egl;
};

struct my_window {
    struct my_config config;
    xcb_window_t x11;
    EGLSurface egl;
};

struct my_pixmap {
    struct my_config config;
    xcb_pixmap_t x11;
    EGLSurface egl;
};

static void check_extensions(void) {
    const char *client_extensions =
        eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);

    if (!client_extensions) {
        // EGL_EXT_client_extensions is unsupported.
        abort();
    }
    if (!strstr(client_extensions, "EGL_EXT_platform_xcb")) {
        abort();
    }
}

xcb_screen_t *get_screen(xcb_connection_t *c, int screen) {
    xcb_screen_iterator_t iter;

    iter = xcb_setup_roots_iterator(xcb_get_setup(c));
    for (; iter.rem; --screen, xcb_screen_next(&iter))
        if (screen == 0)
            return iter.data;

    return NULL;
}

int get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
    const xcb_setup_t *setup = xcb_get_setup(c);
    for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup); i.rem;
         xcb_screen_next(&i)) {
        for (xcb_depth_iterator_t j =
                 xcb_screen_allowed_depths_iterator(i.data);
             j.rem; xcb_depth_next(&j)) {
            const int len = xcb_depth_visuals_length(j.data);
            const xcb_visualtype_t *visuals = xcb_depth_visuals(j.data);
            for (int k = 0; k < len; k++) {
                if (visual == visuals[k].visual_id) {
                    return j.data->depth;
                }
            }
        }
    }
    abort();
}

static struct my_display get_display(void) {
    struct my_display dpy;

    dpy.x11 = xcb_connect(NULL, &dpy.screen);
    if (!dpy.x11) {
        abort();
    }

    dpy.egl = eglGetPlatformDisplayEXT(EGL_PLATFORM_XCB_EXT, dpy.x11,
                                       (const EGLint[]){
                                           EGL_PLATFORM_XCB_SCREEN_EXT,
                                           dpy.screen,
                                           EGL_NONE,
                                       });

    if (dpy.egl == EGL_NO_DISPLAY) {
        abort();
    }

    EGLint major, minor;
    if (!eglInitialize(dpy.egl, &major, &minor)) {
        abort();
    }

    xcb_screen_t *screen = get_screen(dpy.x11, dpy.screen);
    dpy.root_of_screen = screen->root;

    return dpy;
}

static struct my_config get_config(struct my_display dpy) {
    struct my_config config = {
        .dpy = dpy,
    };

    EGLint egl_config_attribs[] = {
        EGL_BUFFER_SIZE,
        32,
        EGL_RED_SIZE,
        8,
        EGL_GREEN_SIZE,
        8,
        EGL_BLUE_SIZE,
        8,
        EGL_ALPHA_SIZE,
        8,

        EGL_DEPTH_SIZE,
        EGL_DONT_CARE,
        EGL_STENCIL_SIZE,
        EGL_DONT_CARE,

        EGL_RENDERABLE_TYPE,
        EGL_OPENGL_ES2_BIT,
        EGL_SURFACE_TYPE,
        EGL_WINDOW_BIT | EGL_PIXMAP_BIT,
        EGL_NONE,
    };

    EGLint num_configs;
    if (!eglChooseConfig(dpy.egl, egl_config_attribs, &config.egl, 1,
                         &num_configs)) {
        abort();
    }
    if (num_configs == 0) {
        abort();
    }

    if (!eglGetConfigAttrib(dpy.egl, config.egl, EGL_NATIVE_VISUAL_ID,
                            (EGLint *)&config.visualid)) {
        abort();
    }

    config.colormap = xcb_generate_id(dpy.x11);
    if (xcb_request_check(dpy.x11,
                          xcb_create_colormap_checked(
                              dpy.x11, XCB_COLORMAP_ALLOC_NONE, config.colormap,
                              dpy.root_of_screen, config.visualid))) {
        abort();
    }

    config.depth = get_visual_depth(dpy.x11, config.visualid);

    return config;
}

static struct my_window get_window(struct my_config config) {
    xcb_generic_error_t *e;

    struct my_window window = {
        .config = config,
    };

    window.x11 = xcb_generate_id(config.dpy.x11);
    e = xcb_request_check(
        config.dpy.x11,
        xcb_create_window_checked(config.dpy.x11,            // connection
                                  XCB_COPY_FROM_PARENT,      // depth
                                  window.x11,                // window id
                                  config.dpy.root_of_screen, // root
                                  0, 0,                      // x, y
                                  256, 256,                  // width, height
                                  0,                         // border_width
                                  XCB_WINDOW_CLASS_INPUT_OUTPUT, // class
                                  config.visualid,               // visual
                                  XCB_CW_COLORMAP,               // mask
                                  (const int[]){
                                      config.colormap,
                                      XCB_NONE,
                                  }));
    if (e) {
        abort();
    }

    window.egl = eglCreatePlatformWindowSurfaceEXT(config.dpy.egl, config.egl,
                                                   &window.x11, NULL);

    if (window.egl == EGL_NO_SURFACE) {
        abort();
    }

    return window;
}

static struct my_pixmap get_pixmap(struct my_config config) {
    struct my_pixmap pixmap = {
        .config = config,
    };

    pixmap.x11 = xcb_generate_id(config.dpy.x11);
    if (xcb_request_check(
            config.dpy.x11,
            xcb_create_pixmap(config.dpy.x11, config.depth, pixmap.x11,
                              config.dpy.root_of_screen, 256, 256))) {
        abort();
    }

    pixmap.egl = eglCreatePlatformPixmapSurfaceEXT(config.dpy.egl, config.egl,
                                                   &pixmap.x11, NULL);

    if (pixmap.egl == EGL_NO_SURFACE) {
        abort();
    }

    return pixmap;
}

int main(void) {
    check_extensions();

    struct my_display dpy = get_display();
    struct my_config config = get_config(dpy);
    struct my_window window = get_window(config);
    struct my_pixmap pixmap = get_pixmap(config);

    return 0;
}

Revision History

Version 2, 2020.10.13 (Yuxuan Shui)
    - Some wording changes
    - Address the question about screen selection

Version 1, 2020.08.28 (Yuxuan Shui)
    - First draft