如何通过drm(Linux)创建opengl上下文

18

我想在不使用X的情况下使用OpenGL渲染,通过谷歌我找到了这个链接:http://dvdhrm.wordpress.com/2012/08/11/kmscon-linux-kmsdrm-based-virtual-console/。它说这是可能的。我应该使用DRM和EGL。EGL可以创建OpenGL上下文,但需要一个NativeWindow。DRM可能会为我提供NativeWindow,不是吗?我应该使用KMS吗?我知道必须使用开源视频驱动程序。我需要确切的OpenGL上下文,而不是OpenGL ES(Linux)。也许有人知道教程或示例代码?

2个回答

23

是的,你需要kms栈(示例)。这里是一个简单的Linux示例,它使用OpenGL ES,但使其针对OpenGL API工作的步骤很简单。

在egl属性中设置EGL_RENDERABLE_TYPEEGL_OPENGL_BIT

并告诉egl绑定到哪个API:

eglBindAPI(EGL_OPENGL_API);

请确保您拥有最新的内核驱动程序以及 mesa-devlibdrm-devlibgbm-dev。这些代码片段在 Android 上是可移植的,只是默认的 Android 图形堆栈不太容易消除噪音。
注意:我在32位版本上遇到了问题,但仍然不知道原因。这些库正在积极开发中,所以不确定它是否是一个错误。
*注意2:根据您的 GLSL 版本,可能会支持浮点精度或不支持。
precision mediump float;

注意3:如果您在使用/dev/dri/card0时遇到权限问题,请使用以下命令授予权限:

sudo chmod 666 /dev/dri/card0

或者将当前用户添加到 video 组中,使用以下命令:
sudo adduser $user video

你也可以将可执行文件的guid设置为video组。这可能是最佳选项。

嗨,j-p!我正在寻找类似的例子,但是针对Android设备。据我所知,Android中没有DRM堆栈。我猜测低级图形API必须通过fb0和/dev/ion设备工作,但我没有例子。如果您有任何注释,请参考我的问题http://stackoverflow.com/questions/33742552/framebuffer-egl-example-segfault - Alex Hoppus
我的 Android 上有 libdrm(libdrm1.so),但我不记得是自己编译的还是本地的。 - j-p
我该如何在多个进程中实现这个?例如,合成器是如何工作的?我如何让多个进程拥有自己的EGL上下文,并从主进程中将它们合成? - Dave Butler
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglMakeCurrent.xhtml - j-p

1
我有点晚来参加这个派对,但你可能想试试SRM(简单渲染管理器)。它是一个C库,可以将所有DRM/KMS的复杂性抽象出来,这样你就可以专注于应用程序中的OpenGL ES 2.0部分。这里有一个基本示例:
#include <SRMCore.h>
#include <SRMDevice.h>
#include <SRMListener.h>
#include <SRMCrtc.h>
#include <SRMEncoder.h>
#include <SRMPlane.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;
}

此外,它还提供了其他几个功能,包括方便的自动纹理共享功能,可以从单个分配中跨多个GPU进行共享。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接