问题描述
我想使用 OpenGL(1.5 版)将图像渲染到内存,而不在屏幕上显示它们(例如,我可以将它们保存为图像文件或在终端中将它们渲染为 ASCII)。我不想要任何 I/O。我在 SO 上发现了类似的问题,但没有一个能解决我接下来的具体要求。
现在我想我可以使用像 glx 这样的库并告诉它不要打开任何窗口,但是我也不希望我的代码依赖于任何像 X11 这样的窗口系统库,因为我的程序根本不做任何事情对于任何窗口或 I/O,我不明白为什么我的程序会受到对 X 窗口的依赖(因为有些系统根本没有 X 窗口,它们甚至可能根本没有图形界面)。我的程序应该只依赖于 OpenGL 驱动程序。
我明白我需要创建一个 OpenGL 上下文,它不是 OpenGL 的一部分,它是依赖于平台的东西,所以实际上我可能需要一些库来理想地创建一个 OpenGL 渲染到内存上下文多平台方式(即抽象掉平台相关的东西)。这样的东西存在吗? (我对任何专有的、特定于 GPU 或特定于驱动程序的软件不感兴趣,该程序应该在支持给定 OpenGL 版本的任何 GPU 上运行。)还有什么我应该考虑的吗?
基本上我希望我的程序非常小并且不会被它不需要的东西所累,因为它所需要的只是使用通用的 OpenGL 驱动程序将图像渲染到内存中,并且应该在任何具有这样的系统上工作OpenGL 驱动程序。
谢谢。
解决方法
根据您使用的操作系统和驱动程序的可用性,您可以使用 EGL 进行纯粹的、无头的、GPU 加速的 OpenGL 渲染。 Nvidia 在 https://developer.nvidia.com/blog/egl-eye-opengl-visualization-without-x-server/
上有一个很好的开发者博客,介绍了如何做到这一点其要点是,在显示设备上创建 EGL 上下文而不将其与输出相关联。来源(直接从链接的文章中复制):
#include <EGL/egl.h>
static const EGLint configAttribs[] = {
EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_BLUE_SIZE,8,EGL_GREEN_SIZE,EGL_RED_SIZE,EGL_DEPTH_SIZE,EGL_RENDERABLE_TYPE,EGL_OPENGL_BIT,EGL_NONE
};
static const int pbufferWidth = …;
static const int pbufferHeight = …;
static const EGLint pbattr[] = {
EGL_WIDTH,pbufferWidth,EGL_HEIGHT,pbufferHeight,EGL_NONE,};
int main(int argc,char *argv[])
{
EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint major,minor;
eglInitialize(eglDpy,&major,&minor);
EGLint numConfigs;
EGLConfig eglCfg;
eglChooseConfig(eglDpy,configAttribs,&eglCfg,1,&numConfigs);
EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy,eglCfg,pbattr);
eglBindAPI(EGL_OPENGL_API);
EGLContext eglCtx = eglCreateContext(eglDpy,EGL_NO_CONTEXT,NULL);
eglMakeCurrent(eglDpy,eglSurf,eglCtx);
do_opengl_stuff();
eglTerminate(eglDpy);
return 0;
}
如果您无权访问 EGL,但您的操作系统和 GPU 受 Linux DRM/DRI 支持,您可以选择 KMS/GBM 路线,并且值得使用通过扩展机制获得的帧缓冲对象(好吧,使用 Mesa您可以像使用非扩展一样使用它们,即使使用 OpenGL-1.x)。 kmscube 演示有一个“无表面”模式,它演示了正是这样做的。
简而言之:EGL 是处理它的“干净”方式。 KMS 是一种“hacky”方式。
另一个选项,现在可能完全超出您的范围,是使用 Vulkan,严格来说,无头渲染是“默认”,在屏幕上获取内容的方法是规范的实际扩展:>
VK_KHR_wayland_surface
VK_KHR_xcb_surface
VK_KHR_xlib_surface
VK_KHR_win32_surface