OpenGL без X.org в Linux

Я хотел бы открыть контекст OpenGL без X в Linux. Есть ли способ сделать это?

Я знаю, что он удобен для встроенного аппаратного обеспечения видеокарт Intel, хотя у большинства людей есть карты NVIDIA в их системе. Я хотел бы получить решение, которое работает с картами NVIDIA.

Если нет другого пути, кроме как через встроенное аппаратное обеспечение Intel, я думаю, было бы хорошо узнать, как это сделать с ними.

Протокол X11 и сам протокол слишком велики и сложны. Обеспечиваемое им мультиплексирование входа мыши / клавиатуры / планшета слишком затуманено для современных программ. Я думаю, что это худший контрольно-пропускной пункт, препятствующий улучшению Linux, поэтому я ищу альтернативы.

7 ответов

Решение

Обновление (17 сентября 2017 г.):

NVIDIA недавно опубликовала статью, в которой подробно описывается, как использовать OpenGL в безголовых системах, что очень похоже на случай использования, описанный в вопросе.

В итоге:

  • Ссылка на libOpenGL.so а также libEGL.so вместо libGL.so, (Ваши параметры компоновщика должны быть -lOpenGL -lEGL
  • Вызов eglGetDisplay, затем eglInitialize инициализировать EGL.
  • Вызов eglChooseConfig с атрибутом config EGL_SURFACE_TYPE с последующим EGL_PBUFFER_BIT,
  • Вызов eglCreatePbufferSurface, затем eglBindApi(EGL_OPENGL_API);, затем eglCreateContext а также eglMakeCurrent,

Начиная с этого момента, выполняйте рендеринг OpenGL как обычно, и вы можете перетаскивать поверхность пиксельного буфера в любое время. Эта дополнительная статья от NVIDIA включает базовый пример и пример для нескольких графических процессоров. Поверхность PBuffer также может быть заменена поверхностью окна или растрового изображения, в зависимости от потребностей приложения.

Я сожалею, что не проводил больше исследований по этому вопросу в моем предыдущем издании, ну да ладно. Лучшие ответы - лучшие ответы.


Со времени моего ответа в 2010 году в графическом пространстве Linux произошел ряд серьезных потрясений. Итак, обновленный ответ:

Сегодня nouveau и другие драйверы DRI достигли такой степени, что программное обеспечение OpenGL стабильно и в целом работает достаточно хорошо. С введением EGL API в Mesa теперь можно писать приложения OpenGL и OpenGL ES даже на настольных ПК Linux.

Вы можете написать свое приложение для целевой EGL, и оно может быть запущено без присутствия оконного менеджера или даже композитора. Для этого вы бы позвонили eglGetDisplay, eglInitializeи в конечном итоге eglCreateContext а также eglMakeCurrent, вместо обычных вызовов GLX, чтобы сделать то же самое.

Я не знаю конкретного пути кода для работы без сервера дисплея, но EGL принимает дисплеи X11 и Wayland, и я знаю, что EGL может работать без него. Вы можете создавать контексты GL ES 1.1, ES 2.0, ES 3.0 (если у вас Mesa 9.1 или новее) и OpenGL 3.1 (Mesa 9.0 или новее). Mesa еще не внедрила ядро ​​OpenGL 3.2 (по состоянию на сентябрь 2013 года).

Примечательно, что на Raspberry Pi и на Android EGL и GL ES 2.0 (1.1 на Android < 3.0) поддерживаются по умолчанию. На Raspberry Pi, я не думаю, что Wayland еще работает (по состоянию на сентябрь 2013 года), но вы получаете EGL без сервера дисплея с использованием включенных двоичных драйверов. Ваш EGL-код также должен быть переносимым (с минимальными изменениями) на iOS, если это вас интересует.


Ниже приведен устаревший, ранее принятый пост:

Я хотел бы открыть контекст OpenGL без X в Linux. Есть ли способ сделать это?

Я полагаю, что Mesa обеспечивает цель кадрового буфера. Если он вообще обеспечивает какое-либо аппаратное ускорение, то это будет только с оборудованием, для которого есть драйверы с открытым исходным кодом, которые были адаптированы для поддержки такого использования.

Gallium3D также незрелый, и, насколько я знаю, его поддержка даже не включена в план.

Я хотел бы получить решение, которое работает с картами NVIDIA.

Там нет ни одного. Период.

NVIDIA предоставляет только драйвер X, а проект Nouveau все еще незрелый и не поддерживает тот вид использования, который вам нужен, поскольку в настоящее время они ориентированы только на драйвер X11.

Вас может заинтересовать проект под названием Wayland

http://en.wikipedia.org/wiki/Wayland_%28display_server%29

Вы смотрели на эту страницу? http://virtuousgeek.org/blog/index.php/jbarnes/2011/10/31/writing_stanalone_programs_with_egl_and_

Это, вероятно, немного устарело. Я еще не пробовал, но я был бы признателен за дополнительную документацию этого типа.

Вероятно, хорошей идеей на сегодняшний день является следование реализации Wayland compositor-drm.c: http://cgit.freedesktop.org/wayland/weston/tree/src/compositor-drm.c

https://gitlab.freedesktop.org/mesa/kmscube/ - хорошая эталонная реализация рендеринга с аппаратным ускорением OGL (или OGLES) без зависимости от X11 или Wayland.

Просто используйте Mesa.

Вы можете посмотреть, как Android решил эту проблему. Смотрите проект Android-x86.

Android использует мезу с egl и opengles. Android имеет свой собственный простой компонент Gralloc для настройки режима и выделения графики. Вдобавок к этому у них есть компонент SurfaceFlinger, который является механизмом композиции, который использует OpenGLES для ускорения.

Не могу понять, почему вы не можете использовать эти компоненты подобным образом и даже повторно использовать клейкий код Android.

Вы можете использовать библиотеку SRM (Simple Rendering Manager), которая представляет собой библиотеку C, предназначенную для рендеринга с использованием OpenGL ES 2.0 в контексте KMS/DRM (никаких Xorg или Wayland не требуется). Одним из его существенных преимуществ является то, что он упрощает для вас все настройки DRM/KMS. Кроме того, это облегчает совместное использование текстур OpenGL между графическими процессорами в конфигурации с несколькими графическими процессорами из одного распределения.

Он был протестирован на графических процессорах Intel (драйвер i915), графических процессорах Nvidia (драйверы nouveau и nvidia-drm) и графических процессорах Mali (драйвер Lima).

Вот базовый пример, который просто меняет цвет экрана всех подключенных экранов с помощью glClear():

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

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

#include <GLES2/gl2.h>

#include <math.h>
#include <fcntl.h>
#include <unistd.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)
{
    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;
}
  1. Начните с создания экземпляра SRMCore .
  2. Затем выполните итерацию по каждому SRMDevice (GPU). Каждый графический процессор имеет список SRMConnectors (дисплеев).
  3. Инициализируйте нужные соединители, используя srmConnectorInitialize().
  4. Библиотека создает поток рендеринга для каждого соединителя и вызывает типичные события OpenGL, такие как инициализацияGL(), краскаGL(), resizeGL() и т. д.
  5. Чтобы запланировать новую перерисовку в соединителе, используйте srmConnectorRepaint().
  6. Если вам нужно создать текстуры OpenGL, обратитесь к документации SRMBuffer .

Я надеюсь, что вы найдете эту библиотеку полезной для вашего конкретного случая использования.

Другие вопросы по тегам