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
с атрибутом configEGL_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://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.
Вы можете посмотреть, как 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;
}
- Начните с создания экземпляра SRMCore .
- Затем выполните итерацию по каждому SRMDevice (GPU). Каждый графический процессор имеет список SRMConnectors (дисплеев).
- Инициализируйте нужные соединители, используя srmConnectorInitialize().
- Библиотека создает поток рендеринга для каждого соединителя и вызывает типичные события OpenGL, такие как инициализацияGL(), краскаGL(), resizeGL() и т. д.
- Чтобы запланировать новую перерисовку в соединителе, используйте srmConnectorRepaint().
- Если вам нужно создать текстуры OpenGL, обратитесь к документации SRMBuffer .
Я надеюсь, что вы найдете эту библиотеку полезной для вашего конкретного случая использования.