user2616346
user2616346

Reputation: 473

How to create opengl context via drm (Linux)

I want to use OpenGL rendering without X, with google i find it: http://dvdhrm.wordpress.com/2012/08/11/kmscon-linux-kmsdrm-based-virtual-console/ there says that it is possible. I should use DRM and EGL. EGL can create opengl context but requires a NativeWindow. DRM probably will provide me NativeWindow, is not it? Should i use KMS? I know that i must have open source video driver. I want exactly OpenGL context, but not OpenGL ES (Linux). Maybe, someone knows tutorial or example code?

Upvotes: 17

Views: 13816

Answers (3)

user14063792468
user14063792468

Reputation: 956

First things first.

sudo chmod 666 /dev/dri/card0

is plain wrong. For so many reasons. Next part

sudo adduser $user video

is hidden evil. Although it might be better if you run a dedicated X server and have a connection on a second screen. But you say you not plan to use X server. Anyway, the idea is to isolate your GPU computations as much as possible.

If you do computing on GPU, why not try to use something specific? Like cuda, or better openmp?

The answer

You should use EXT_client_extensions, EXT_platform_base and EXT_device_enumeration from EGL.

Pseudocode:

#include <egl>

if(b_egl_client_extensions != true) {
 return FALSE;
}

fn_q_devices = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
fn_get_platform_display = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");

if(b_device_platform_extensions != true) {
 return FALSE;
}

if(0 == fn_q_devices(666, devices, &n_devices)){
 return FALSE;
}

dpy = fn_get_platform_display(EGL_PLATFORM_DEVICE_EXT, devices[0], 0);
if(EGL_NO_DISPLAY == dpy){
 return FALSE;
}

if(0 == eglInitialize(dpy, &egl_v_maj, &egl_v_min)){
 return FALSE;
}

Upvotes: 0

ehopperdietzel
ehopperdietzel

Reputation: 324

I'm a bit late to the party, but you might want to give SRM (Simple Rendering Manager) a shot. It's a C library (I made) that abstracts away all the DRM/KMS complexities, so you can focus on the OpenGL ES 2.0 part of your application. Here's a basic example that simply changes the color of all connected displays with glClear():

#include <SRMCore.h>
#include <SRMDevice.h>
#include <SRMListener.h>
#include <SRMConnector.h>
#include <SRMConnectorMode.h>
#include <SRMBuffer.h>

#include <SRMList.h>
#include <SRMLog.h>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

float color = 0.f;

/* Opens a DRM device */
static int openRestricted(const char *path, int flags, void *userData)
{
    SRM_UNUSED(userData);

    // Here something like libseat could be used instead
    return open(path, flags);
}

/* Closes a DRM device */
static void closeRestricted(int fd, void *userData)
{
    SRM_UNUSED(userData);
    close(fd);
}

static SRMInterface srmInterface =
{
    .openRestricted = &openRestricted,
    .closeRestricted = &closeRestricted
};

static void initializeGL(SRMConnector *connector, void *userData)
{
    SRM_UNUSED(userData);

    /* You must not do any drawing here as it won't make it to
     * the screen. */

    SRMConnectorMode *mode = srmConnectorGetCurrentMode(connector);

    glViewport(0, 
               0, 
               srmConnectorModeGetWidth(mode), 
               srmConnectorModeGetHeight(mode));

    // Schedule a repaint (this eventually calls paintGL() later, not directly)
    srmConnectorRepaint(connector);
}

static void paintGL(SRMConnector *connector, void *userData)
{
    SRM_UNUSED(userData);

    glClearColor((sinf(color) + 1.f) / 2.f,
                 (sinf(color * 0.5f) + 1.f) / 2.f,
                 (sinf(color * 0.25f) + 1.f) / 2.f,
                 1.f);

    color += 0.01f;

    if (color > M_PI*4.f)
        color = 0.f;

    glClear(GL_COLOR_BUFFER_BIT);
    srmConnectorRepaint(connector);
}

static void resizeGL(SRMConnector *connector, void *userData)
{
    /* You must not do any drawing here as it won't make it to
     * the screen.
     * This is called when the connector changes its current mode,
     * set with srmConnectorSetMode() */

    // Reuse initializeGL() as it only sets the viewport
    initializeGL(connector, userData);
}

static void pageFlipped(SRMConnector *connector, void *userData)
{
    SRM_UNUSED(connector);
    SRM_UNUSED(userData);

    /* You must not do any drawing here as it won't make it to
     * the screen.
     * This is called when the last rendered frame is now being
     * displayed on screen.
     * Google v-sync for more info. */
}

static void uninitializeGL(SRMConnector *connector, void *userData)
{
    SRM_UNUSED(connector);
    SRM_UNUSED(userData);

    /* You must not do any drawing here as it won't make it to
     * the screen.
     * Here you should free any resource created on initializeGL()
     * like shaders, programs, textures, etc. */
}

static SRMConnectorInterface connectorInterface =
{
    .initializeGL = &initializeGL,
    .paintGL = &paintGL,
    .resizeGL = &resizeGL,
    .pageFlipped = &pageFlipped,
    .uninitializeGL = &uninitializeGL
};

static void connectorPluggedEventHandler(SRMListener *listener, SRMConnector *connector)
{
    SRM_UNUSED(listener);

    /* This is called when a new connector is avaliable (E.g. Plugging an HDMI display). */

    /* Got a new connector, let's render on it */
    if (!srmConnectorInitialize(connector, &connectorInterface, NULL))
        SRMError("[srm-basic] Failed to initialize connector %s.",
                 srmConnectorGetModel(connector));
}

static void connectorUnpluggedEventHandler(SRMListener *listener, SRMConnector *connector)
{
    SRM_UNUSED(listener);
    SRM_UNUSED(connector);

    /* This is called when a connector is no longer avaliable (E.g. Unplugging an HDMI display). */

    /* The connnector is automatically uninitialized after this event (if initialized)
     * so calling srmConnectorUninitialize() is a no-op. */
}

int main(void)
{
    setenv("SRM_DEBUG", "4", 1);
    setenv("SRM_EGL_DEBUG", "4", 1);

    SRMCore *core = srmCoreCreate(&srmInterface, NULL);

    if (!core)
    {
        SRMFatal("[srm-basic] Failed to initialize SRM core.");
        return 1;
    }

    // Subscribe to Udev events
    SRMListener *connectorPluggedEventListener = srmCoreAddConnectorPluggedEventListener(core, &connectorPluggedEventHandler, NULL);
    SRMListener *connectorUnpluggedEventListener = srmCoreAddConnectorUnpluggedEventListener(core, &connectorUnpluggedEventHandler, NULL);

    // Find and initialize avaliable connectors

    // Loop each GPU (device)
    SRMListForeach (deviceIt, srmCoreGetDevices(core))
    {
        SRMDevice *device = srmListItemGetData(deviceIt);

        // Loop each GPU connector (screen)
        SRMListForeach (connectorIt, srmDeviceGetConnectors(device))
        {
            SRMConnector *connector = srmListItemGetData(connectorIt);

            if (srmConnectorIsConnected(connector))
            {
                if (!srmConnectorInitialize(connector, &connectorInterface, NULL))
                    SRMError("[srm-basic] Failed to initialize connector %s.",
                             srmConnectorGetModel(connector));
            }
        }
    }

    while (1)
    {
        /* Udev monitor poll DRM devices/connectors hotplugging events (-1 disables timeout).
         * To get a pollable FD use srmCoreGetMonitorFD() */

        if (srmCoreProccessMonitor(core, -1) < 0)
            break;
    }

    /* Unsubscribe to DRM events
     *
     * These listeners are automatically destroyed when calling srmCoreDestroy()
     * so there is no need to free them manually.
     * This is here just to show how to unsubscribe to events on the fly. */

    srmListenerDestroy(connectorPluggedEventListener);
    srmListenerDestroy(connectorUnpluggedEventListener);

    // Finish SRM
    srmCoreDestroy(core);

    return 0;
}

Additionally, it offers several other features, including the handy ability for automatic texture sharing across GPUs from a single allocation.

Upvotes: 1

j-p
j-p

Reputation: 1632

Yes, you need kms stack (example). Here is a simple example under linux, it use OpenGL es, But the step to have it working against OpenGL api are simple.

In the egl attribs set EGL_RENRERABLE_TYPE to EGL_OPENGL_BIT

And tell egl which api to bind to:

eglBindAPI(EGL_OPENGL_API);

Be sure to have latest kernel drivers and mesa-dev, libdrm-dev, libgbm-dev. This pieces of code is portable on android, it's just not so easy to have default android graphic stack silenced.

note: I had trouble with 32bit version, but still don't know why. those libs are actively developed, so not sure it wasn't a bug.

*note2: depending on your GLSL version, float precision is supported or not.

precision mediump float;

note3: if you have permision failure with /dev/dri/card0, grant it with:

sudo chmod 666 /dev/dri/card0

or add current user to video group with

sudo adduser $user video

you may also setguid for your executable with group set to video. (maybe best option)

Upvotes: 24

Related Questions