3

I would like to use OpenGL (version 1.5) to render images to memory, without displaying them on screen (I can e.g. just save them as image files or render them as ASCII in terminal). I do not want any I/O. I've found similar question at SO but none that addresses my specific further requirements which will follow.

Now I think I could use a library like glx and tell it to not open any window, however I also don't want my code to depend on any windowing system library like X11, because my program simply doesn't do anything with any windows or I/O, I don't see why my program should be burdened by a dependency on X window (as some systems simply don't have X window, they may even have no graphical interface at all). My programs should only depend on an OpenGL driver.

I understand for this I need to create an OpenGL context, which is not part of OpenGL and which is something that's platform-dependent, so actually I might need some library for creating an OpenGL render-to-memory context ideally in a multiplatfom way (i.e. abstracting away the platform-dependent stuff). Does anything like this exist? (I am not interested in any proprietary, GPU-specific or driver-specific software, the program should run on any GPU that supports given OpenGL version.) Is there something else I should consider?

Basically I want my program to be very minimal and not burdened by what it doesn't need, given that all it needs is to use a generic OpenGL driver to render an image into memory, and should work on any system having such OpenGL driver.

genpfault
  • 51,148
  • 11
  • 85
  • 139
Miloslav Číž
  • 557
  • 4
  • 18
  • You could use a [GLFW offscreen context](https://www.glfw.org/docs/latest/context_guide.html#context_offscreen). Also have a look at [framebuffer objects](https://learnopengl.com/Advanced-OpenGL/Framebuffers). – G.M. Jul 08 '21 at 09:18
  • 2
    ...Just saw the `OpenGL (version 1.5)` bit -- so forget about framebuffer objects. Having said that, are you really limited to `OpenGL 1.5`? – G.M. Jul 08 '21 at 09:54
  • For now let's suppose I am, I have my reasons and have thought through the decision to not use programmable pipeline (it's not that I just don't want to "learn shaders", I've been using them for a long time). No matter the version, I still need to create the minimal OGL context, which is what I need to solve. I'd use FBOs in case there was absolutely no way around it, provided I find a way to create the minimal windowless context. The mentioned GLFW won't do as the link explicitly says it can't create a context without a window. – Miloslav Číž Jul 08 '21 at 11:51
  • 1
    You don't have to use shaders, the compatibility profile is widely supported. – Azarien Jul 09 '21 at 16:41

1 Answers1

3

Depending on the operating system you're using and the availability of drivers, you can do pure, headless, GPU accelerated OpenGL rendering using EGL. Nvidia has a nice developer blog about how to do it at https://developer.nvidia.com/blog/egl-eye-opengl-visualization-without-x-server/

The gist of it is, to create a EGL context on a display device without associating it with an output. Source (copied directly from the linked article):

#include <EGL/egl.h>
static const EGLint configAttribs[] = {
          EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
          EGL_BLUE_SIZE, 8,
          EGL_GREEN_SIZE, 8,
          EGL_RED_SIZE, 8,
          EGL_DEPTH_SIZE, 8,
          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, eglCfg, EGL_NO_CONTEXT, NULL);    
  eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx);

  do_opengl_stuff();

  eglTerminate(eglDpy);
  return 0;
}

If you don't have access to EGL, but your OS and your GPU is supported by Linux DRM/DRI, you could go the KMS/GBM route and worth with framebuffer objects obtained through the extension mechanism (well, with Mesa you can just use them as if they were non extensions, even with OpenGL-1.x). The kmscube demo has a "surfaceless" mode, which demonstrates doing exactly that.

In short: EGL is the "clean" way do to it. KMS is the "hacky" way to do it.

Another option, probably completely outside of your scope right now, would be to use Vulkan, where strictly speaking, headless rendering is the "default", and methods for getting stuff on-screen are actual extensions to the specification:

    VK_KHR_wayland_surface
    VK_KHR_xcb_surface
    VK_KHR_xlib_surface
    VK_KHR_win32_surface
    VK_EXT_metal_surface
Community
  • 1
  • 1
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • EGL indeed seems like what I want, don't know how I missed it, and also thanks for suggesting Vulkan as another open graphics API (though for better legacy support I'll try to stay with OGL). Let me check this in more detail and if it's all okay I can accept this. – Miloslav Číž Jul 08 '21 at 13:51
  • I've got the code compiled and running, I am just having an issue getting the values our of frame buffer with `glReadPixels`, but I think it may be a driver bug as if I use GLX to create the context, the same code suddenly works. Anyway I think this is the answer to my question. – Miloslav Číž Jul 08 '21 at 15:34