Skip to content

Instantly share code, notes, and snippets.

@doesnotexist
Forked from Miouyouyou/Linux_DRM_OpenGLES.c
Created February 25, 2022 05:50
Show Gist options
  • Save doesnotexist/89fee13f0e623e530e5ac878b68561a7 to your computer and use it in GitHub Desktop.
Save doesnotexist/89fee13f0e623e530e5ac878b68561a7 to your computer and use it in GitHub Desktop.

Revisions

  1. @Miouyouyou Miouyouyou revised this gist Jan 11, 2017. 1 changed file with 2 additions and 24 deletions.
    26 changes: 2 additions & 24 deletions Linux_DRM_OpenGLES.c
    Original file line number Diff line number Diff line change
    @@ -121,35 +121,13 @@ static uint32_t find_crtc_for_connector(const drmModeRes *resources,

    static int init_drm(void)
    {
    static const char *modules[] = {
    "rockchip",
    "exynos",
    "i915",
    "msm",
    "nouveau",
    "omapdrm",
    "radeon",
    "tegra",
    "vc4",
    "virtio_gpu",
    "vmwgfx",
    };
    drmModeRes *resources;
    drmModeConnector *connector = NULL;
    drmModeEncoder *encoder = NULL;
    int i, area;

    for (i = 0; i < ARRAY_SIZE(modules); i++) {
    printf("trying to load module %s...", modules[i]);
    drm.fd = drmOpen(modules[i], NULL);
    if (drm.fd < 0) {
    printf("failed.\n");
    } else {
    printf("success.\n");
    break;
    }
    }

    drm.fd = open("/dev/dri/card0", O_RDWR);

    if (drm.fd < 0) {
    printf("could not open drm device\n");
    return -1;
  2. @Miouyouyou Miouyouyou revised this gist Jan 11, 2017. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Linux_DRM_OpenGLES.c
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // gcc -o drmgl Linux_DRM_OpenGLES.c `pkg-config --cflags --libs libdrm` -lgbm -lEGL -lGLESv2

    /*
    * Copyright (c) 2012 Arvin Schnell <[email protected]>
    * Copyright (c) 2012 Rob Clark <[email protected]>
  3. @Miouyouyou Miouyouyou created this gist Jan 11, 2017.
    461 changes: 461 additions & 0 deletions Linux_DRM_OpenGLES.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,461 @@
    /*
    * Copyright (c) 2012 Arvin Schnell <[email protected]>
    * Copyright (c) 2012 Rob Clark <[email protected]>
    * Copyright (c) 2017 Miouyouyou <Myy> <[email protected]>
    *
    * Permission is hereby granted, free of charge, to any person obtaining a
    * copy of this software and associated documentation files (the "Software"),
    * to deal in the Software without restriction, including without limitation
    * the rights to use, copy, modify, merge, publish, distribute, sub license,
    * and/or sell copies of the Software, and to permit persons to whom the
    * Software is furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice (including the
    * next paragraph) shall be included in all copies or substantial portions
    * of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
    * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    * DEALINGS IN THE SOFTWARE.
    */

    /* Based on a egl cube test app originally written by Arvin Schnell */

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>

    #include <xf86drm.h>
    #include <xf86drmMode.h>
    #include <gbm.h>

    #define GL_GLEXT_PROTOTYPES 1
    #include <GLES2/gl2.h>
    #include <GLES2/gl2ext.h>
    #include <EGL/egl.h>
    #include <EGL/eglext.h>

    #include <assert.h>

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))


    static struct {
    EGLDisplay display;
    EGLConfig config;
    EGLContext context;
    EGLSurface surface;
    GLuint program;
    GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
    GLuint vbo;
    GLuint positionsoffset, colorsoffset, normalsoffset;
    } gl;

    static struct {
    struct gbm_device *dev;
    struct gbm_surface *surface;
    } gbm;

    static struct {
    int fd;
    drmModeModeInfo *mode;
    uint32_t crtc_id;
    uint32_t connector_id;
    } drm;

    struct drm_fb {
    struct gbm_bo *bo;
    uint32_t fb_id;
    };

    static uint32_t find_crtc_for_encoder(const drmModeRes *resources,
    const drmModeEncoder *encoder) {
    int i;

    for (i = 0; i < resources->count_crtcs; i++) {
    /* possible_crtcs is a bitmask as described here:
    * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api
    */
    const uint32_t crtc_mask = 1 << i;
    const uint32_t crtc_id = resources->crtcs[i];
    if (encoder->possible_crtcs & crtc_mask) {
    return crtc_id;
    }
    }

    /* no match found */
    return -1;
    }

    static uint32_t find_crtc_for_connector(const drmModeRes *resources,
    const drmModeConnector *connector) {
    int i;

    for (i = 0; i < connector->count_encoders; i++) {
    const uint32_t encoder_id = connector->encoders[i];
    drmModeEncoder *encoder = drmModeGetEncoder(drm.fd, encoder_id);

    if (encoder) {
    const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder);

    drmModeFreeEncoder(encoder);
    if (crtc_id != 0) {
    return crtc_id;
    }
    }
    }

    /* no match found */
    return -1;
    }

    static int init_drm(void)
    {
    static const char *modules[] = {
    "rockchip",
    "exynos",
    "i915",
    "msm",
    "nouveau",
    "omapdrm",
    "radeon",
    "tegra",
    "vc4",
    "virtio_gpu",
    "vmwgfx",
    };
    drmModeRes *resources;
    drmModeConnector *connector = NULL;
    drmModeEncoder *encoder = NULL;
    int i, area;

    for (i = 0; i < ARRAY_SIZE(modules); i++) {
    printf("trying to load module %s...", modules[i]);
    drm.fd = drmOpen(modules[i], NULL);
    if (drm.fd < 0) {
    printf("failed.\n");
    } else {
    printf("success.\n");
    break;
    }
    }

    if (drm.fd < 0) {
    printf("could not open drm device\n");
    return -1;
    }

    resources = drmModeGetResources(drm.fd);
    if (!resources) {
    printf("drmModeGetResources failed: %s\n", strerror(errno));
    return -1;
    }

    /* find a connected connector: */
    for (i = 0; i < resources->count_connectors; i++) {
    connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
    if (connector->connection == DRM_MODE_CONNECTED) {
    /* it's connected, let's use this! */
    break;
    }
    drmModeFreeConnector(connector);
    connector = NULL;
    }

    if (!connector) {
    /* we could be fancy and listen for hotplug events and wait for
    * a connector..
    */
    printf("no connected connector!\n");
    return -1;
    }

    /* find prefered mode or the highest resolution mode: */
    for (i = 0, area = 0; i < connector->count_modes; i++) {
    drmModeModeInfo *current_mode = &connector->modes[i];

    if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
    drm.mode = current_mode;
    }

    int current_area = current_mode->hdisplay * current_mode->vdisplay;
    if (current_area > area) {
    drm.mode = current_mode;
    area = current_area;
    }
    }

    if (!drm.mode) {
    printf("could not find mode!\n");
    return -1;
    }

    /* find encoder: */
    for (i = 0; i < resources->count_encoders; i++) {
    encoder = drmModeGetEncoder(drm.fd, resources->encoders[i]);
    if (encoder->encoder_id == connector->encoder_id)
    break;
    drmModeFreeEncoder(encoder);
    encoder = NULL;
    }

    if (encoder) {
    drm.crtc_id = encoder->crtc_id;
    } else {
    uint32_t crtc_id = find_crtc_for_connector(resources, connector);
    if (crtc_id == 0) {
    printf("no crtc found!\n");
    return -1;
    }

    drm.crtc_id = crtc_id;
    }

    drm.connector_id = connector->connector_id;

    return 0;
    }

    static int init_gbm(void)
    {
    gbm.dev = gbm_create_device(drm.fd);

    gbm.surface = gbm_surface_create(gbm.dev,
    drm.mode->hdisplay, drm.mode->vdisplay,
    GBM_FORMAT_XRGB8888,
    GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
    if (!gbm.surface) {
    printf("failed to create gbm surface\n");
    return -1;
    }

    return 0;
    }

    static int init_gl(void)
    {
    EGLint major, minor, n;
    GLuint vertex_shader, fragment_shader;
    GLint ret;

    static const EGLint context_attribs[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
    };

    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
    };

    PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
    get_platform_display =
    (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
    assert(get_platform_display != NULL);

    gl.display = get_platform_display(EGL_PLATFORM_GBM_KHR, gbm.dev, NULL);

    if (!eglInitialize(gl.display, &major, &minor)) {
    printf("failed to initialize\n");
    return -1;
    }

    printf("Using display %p with EGL version %d.%d\n",
    gl.display, major, minor);

    printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
    printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
    printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));

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

    if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
    printf("failed to choose config: %d\n", n);
    return -1;
    }

    gl.context = eglCreateContext(gl.display, gl.config,
    EGL_NO_CONTEXT, context_attribs);
    if (gl.context == NULL) {
    printf("failed to create context\n");
    return -1;
    }

    gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
    if (gl.surface == EGL_NO_SURFACE) {
    printf("failed to create egl surface\n");
    return -1;
    }

    /* connect the context to the surface */
    eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);

    printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));

    return 0;
    }

    /* Draw code here */
    static void draw(uint32_t i)
    {
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.2f, 0.3f, 0.5f, 1.0f);
    }

    static void
    drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
    {
    struct drm_fb *fb = data;
    struct gbm_device *gbm = gbm_bo_get_device(bo);

    if (fb->fb_id)
    drmModeRmFB(drm.fd, fb->fb_id);

    free(fb);
    }

    static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
    {
    struct drm_fb *fb = gbm_bo_get_user_data(bo);
    uint32_t width, height, stride, handle;
    int ret;

    if (fb)
    return fb;

    fb = calloc(1, sizeof *fb);
    fb->bo = bo;

    width = gbm_bo_get_width(bo);
    height = gbm_bo_get_height(bo);
    stride = gbm_bo_get_stride(bo);
    handle = gbm_bo_get_handle(bo).u32;

    ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
    if (ret) {
    printf("failed to create fb: %s\n", strerror(errno));
    free(fb);
    return NULL;
    }

    gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);

    return fb;
    }

    static void page_flip_handler(int fd, unsigned int frame,
    unsigned int sec, unsigned int usec, void *data)
    {
    int *waiting_for_flip = data;
    *waiting_for_flip = 0;
    }

    int main(int argc, char *argv[])
    {
    fd_set fds;
    drmEventContext evctx = {
    .version = DRM_EVENT_CONTEXT_VERSION,
    .page_flip_handler = page_flip_handler,
    };
    struct gbm_bo *bo;
    struct drm_fb *fb;
    uint32_t i = 0;
    int ret;

    ret = init_drm();
    if (ret) {
    printf("failed to initialize DRM\n");
    return ret;
    }

    FD_ZERO(&fds);
    FD_SET(0, &fds);
    FD_SET(drm.fd, &fds);

    ret = init_gbm();
    if (ret) {
    printf("failed to initialize GBM\n");
    return ret;
    }

    ret = init_gl();
    if (ret) {
    printf("failed to initialize EGL\n");
    return ret;
    }

    /* clear the color buffer */
    glClearColor(0.5, 0.5, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(gl.display, gl.surface);
    bo = gbm_surface_lock_front_buffer(gbm.surface);
    fb = drm_fb_get_from_bo(bo);

    /* set mode: */
    ret = drmModeSetCrtc(drm.fd, drm.crtc_id, fb->fb_id, 0, 0,
    &drm.connector_id, 1, drm.mode);
    if (ret) {
    printf("failed to set mode: %s\n", strerror(errno));
    return ret;
    }

    while (1) {
    struct gbm_bo *next_bo;
    int waiting_for_flip = 1;

    draw(i++);

    eglSwapBuffers(gl.display, gl.surface);
    next_bo = gbm_surface_lock_front_buffer(gbm.surface);
    fb = drm_fb_get_from_bo(next_bo);

    /*
    * Here you could also update drm plane layers if you want
    * hw composition
    */

    ret = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id,
    DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
    if (ret) {
    printf("failed to queue page flip: %s\n", strerror(errno));
    return -1;
    }

    while (waiting_for_flip) {
    ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
    if (ret < 0) {
    printf("select err: %s\n", strerror(errno));
    return ret;
    } else if (ret == 0) {
    printf("select timeout!\n");
    return -1;
    } else if (FD_ISSET(0, &fds)) {
    printf("user interrupted!\n");
    break;
    }
    drmHandleEvent(drm.fd, &evctx);
    }

    /* release last buffer to render on again: */
    gbm_surface_release_buffer(gbm.surface, bo);
    bo = next_bo;
    }

    return ret;
    }