我希望在Linux系统中不依赖X环境打开OpenGL上下文。是否有任何方法可以实现?
我知道对于集成了英特尔显卡的硬件来说,这是可能的,但大多数人的系统都配备了Nvidia显卡。我希望能找到适用于Nvidia显卡的解决方案。
如果没有其他方法,只能通过集成了英特尔硬件来实现,那么我想知道如何使用这些硬件进行操作。
X11协议本身过于庞大和复杂。它提供的鼠标/键盘/触控板输入复用功能对于现代程序来说太过简单了。我认为这是阻碍Linux桌面系统改进的最大障碍,因此我正在寻找替代方法。
我希望在Linux系统中不依赖X环境打开OpenGL上下文。是否有任何方法可以实现?
我知道对于集成了英特尔显卡的硬件来说,这是可能的,但大多数人的系统都配备了Nvidia显卡。我希望能找到适用于Nvidia显卡的解决方案。
如果没有其他方法,只能通过集成了英特尔硬件来实现,那么我想知道如何使用这些硬件进行操作。
X11协议本身过于庞大和复杂。它提供的鼠标/键盘/触控板输入复用功能对于现代程序来说太过简单了。我认为这是阻碍Linux桌面系统改进的最大障碍,因此我正在寻找替代方法。
更新(2017年9月17日):
NVIDIA最近发布了一篇文章详细介绍了如何在无头系统上使用OpenGL,这与问题描述的用例非常相似。
总结如下:
libOpenGL.so
和libEGL.so
,而不是libGL.so
。(因此您的链接器选项应为-lOpenGL -lEGL
)eglGetDisplay
,然后调用eglInitialize
来初始化EGL。EGL_SURFACE_TYPE
的eglChooseConfig
,然后跟随EGL_PBUFFER_BIT
。eglCreatePbufferSurface
,然后调用eglBindApi(EGL_OPENGL_API);
,然后调用eglCreateContext
和eglMakeCurrent
。从那时起,像往常一样进行OpenGL渲染,您可以在任何地方复制您的像素缓冲区表面。NVIDIA的这篇补充文章包括一个基本示例和多个GPU的示例。根据应用程序需求,PBuffer表面也可以被替换为窗口表面或像素图表面。
我很遗憾之前在我的编辑中没有做更多的研究,但是好吧。更好的答案就是更好的答案。
自2010年回答以来,Linux图形空间发生了许多重大变化。因此,更新的答案如下:
今天,nouveau和其他DRI驱动程序已经成熟到使OpenGL软件在一般情况下稳定并且性能合理。随着在Mesa中引入EGL API,现在甚至可以在Linux桌面上编写OpenGL和OpenGL ES应用程序。
您可以编写应用程序,以针对EGL进行目标设置,而无需存在窗口管理器或复合器。要做到这一点,您将调用eglGetDisplay
、eglInitialize
,最终调用eglCreateContext
和eglMakeCurrent
,而不是通常使用GLX调用来完成相同的操作。NVIDIA只提供X驱动程序,而Nouveau项目仍不成熟,并且不支持您正在寻找的那种用途,因为它们目前仅专注于X11驱动程序。
https://gitlab.freedesktop.org/mesa/kmscube/ 是一个优秀的参考实现,用于在没有依赖 X11 或 Wayland 的情况下进行 OGL(或 OGLES)硬件加速渲染。
您看过这个页面吗? 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
#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;
}