Linux中不带X.org的OpenGL

b91juud3  于 2023-10-18  发布在  Linux
关注(0)|答案(6)|浏览(82)

我想在Linux中打开一个没有X的OpenGL上下文。有什么办法能做到这一点吗?
我知道这是可能的集成英特尔显卡硬件,虽然大多数人有英伟达卡在他们的系统。我想得到一个解决方案,与英伟达卡工程。
如果除了通过集成的英特尔硬件之外没有其他方法,我想知道如何使用这些硬件也是可以的。
X11协议本身过于庞大和复杂。它提供的鼠标/键盘/平板电脑输入多路复用对于现代程序来说太淡化了。我认为这是阻止Linux桌面改进的最大障碍,这就是为什么我寻找替代方案的原因。

mbzjlibv

mbzjlibv1#

更新(9月)2017年7月17日):
NVIDIA最近发表了一篇文章detailing how to use OpenGL on headless systems,这是一个与问题描述非常相似的用例。
总的来说:

  • 链接到libOpenGL.solibEGL.so,而不是libGL.so。(因此,链接器选项应为-lOpenGL -lEGL
  • 调用eglGetDisplay,然后调用eglInitialize初始化EGL。
  • 使用配置属性EGL_SURFACE_TYPEEGL_PBUFFER_BIT调用eglChooseConfig
  • 调用eglCreatePbufferSurface,然后调用eglBindApi(EGL_OPENGL_API);,然后调用eglCreateContexteglMakeCurrent

从这一点开始,像往常一样进行OpenGL渲染,您可以在任何您喜欢的地方blit像素缓冲区表面。This supplementary article from NVIDIA包括一个基本示例和一个多GPU示例。根据应用程序的需要,PBuffer表面也可以被替换为窗口表面或像素图表面。
我很遗憾没有在我以前的编辑中做更多的研究,但是哦,好吧。更好的答案是更好的答案。
自从我在2010年回答这个问题以来,Linux图形领域发生了一些重大的变革。一个更新的答案:
今天,nouveau和其他DRI驱动程序已经成熟到OpenGL软件稳定并且总体上表现良好的程度。随着在梅萨中引入EGL API,现在甚至可以在Linux桌面上编写OpenGL和OpenGL ES应用程序。
您可以将您的应用程序编写到目标EGL,并且它可以在没有窗口管理器甚至合成器的情况下运行。为此,您需要调用eglGetDisplayeglInitialize,最终调用eglCreateContexteglMakeCurrent,而不是通常的glx调用。
我不知道在没有显示服务器的情况下工作的具体代码路径,但EGL接受X11显示器和Wayland显示器,我确实知道EGL可以在没有显示服务器的情况下工作。您可以创建GL ES 1.1、ES 2.0、ES 3.0(如果您有梅萨9.1或更高版本)和OpenGL 3.1(梅萨9.0或更高版本)上下文。梅萨还没有(截至9月)。2013年),但实现了OpenGL 3.2核心。
值得注意的是,在Raspberry Pi和Android上,默认支持EGL和GL ES 2.0(Android上为1.1 < 3.0)。在Raspberry Pi上,我认为Wayland还不起作用(截至9月10日)。2013),但您可以使用包含的二进制驱动程序在没有显示服务器的情况下获得EGL。如果您感兴趣的话,您的EGL代码还应该可以移植到iOS(只需最小的修改)。

以下是以前接受的过时帖子:

我想在Linux中打开一个没有X的OpenGL上下文。有什么办法能做到这一点吗?
我相信梅萨提供了一个帧缓冲区的目标。如果它提供任何硬件加速,它将只与硬件,有开源驱动程序已被改编,以支持这种用途。
Gallium3D也不成熟,据我所知,对它的支持甚至不在路线图上。
我想得到一个解决方案,与英伟达卡工程。
根本没有期
NVIDIA只提供了一个X驱动程序,而Nouveau项目还不成熟,不支持你正在寻找的那种用途,因为他们目前只专注于X11驱动程序。

tquggr8v

tquggr8v3#

你看过这一页了吗?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

4c8rllxm

4c8rllxm4#

https://gitlab.freedesktop.org/mesa/kmscube/是OGL(或OGLES)硬件加速渲染的一个很好的参考实现,没有X11或wayland依赖性。

nxagd54h

nxagd54h5#

你可以看看Android是如何解决这个问题的。Android-x86项目
Android使用梅萨和egl以及opengles。Android有自己的简单Gralloc组件用于模式设置和图形分配。最重要的是,他们有SurfaceFlinger组件,这是一个合成引擎,它使用OpenGLES进行加速。
看不出为什么你不能以类似的方式使用这些组件,甚至重用Android胶水代码。

r55awzrz

r55awzrz6#

您可以使用**SRM library (Simple Rendering Manager)**,这是一个C库,专为在KMS/DRM上下文中使用OpenGL ES 2.0进行渲染而设计(不需要Xorg或Wayland)。它的一个显著优点是它为您简化了所有DRM/KMS配置。此外,它还有助于从单个分配在多GPU设置中的GPU之间共享OpenGL纹理。
它已经在Intel GPU(i915驱动程序),Nvidia GPU(nouveau和nvidia-triple驱动程序)和Mali GPU(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示例。
1.然后,通过每个
SRMDevice(GPU)进行计算。每个GPU都有一个SRMConnectors**(显示器)列表。
1.使用srmConnectorInitialize()初始化所需的连接器。
1.该库为每个连接器创建一个渲染线程,并调用典型的OpenGL事件,如 initializeGL()paintGL()resizeGL() 等。
1.要在连接器中计划新的重绘,请使用srmConnectorRepaint()。
1.如果您需要创建OpenGL纹理,请参考SRMBuffer documentation
我希望您发现这个库对您的特定用例有用。

相关问题