Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EGL headless mode #1608

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 13 additions & 1 deletion CMakeLists.txt
Expand Up @@ -28,8 +28,10 @@ option(GLFW_VULKAN_STATIC "Assume the Vulkan loader is linked with the applicati
include(GNUInstallDirs)
include(CMakeDependentOption)

cmake_dependent_option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF
cmake_dependent_option(GLFW_USE_EGLHEADLESS "Use EGL for offscreen context creation" OFF
"UNIX" OFF)
cmake_dependent_option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF
"UNIX" OFF)
cmake_dependent_option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF
"WIN32" OFF)
cmake_dependent_option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF
Expand Down Expand Up @@ -143,6 +145,9 @@ if (GLFW_USE_WAYLAND)
elseif (GLFW_USE_OSMESA)
set(_GLFW_OSMESA 1)
message(STATUS "Using OSMesa for headless context creation")
elseif (GLFW_USE_EGLHEADLESS)
set(_GLFW_EGLHEADLESS 1)
message(STATUS "Using EGL for headless context creation")
elseif (WIN32)
set(_GLFW_WIN32 1)
message(STATUS "Using Win32 for window creation")
Expand Down Expand Up @@ -278,6 +283,13 @@ if (_GLFW_OSMESA)
list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
endif()

#--------------------------------------------------------------------
# Use EGL for offscreen context creation
#--------------------------------------------------------------------
if (_GLFW_EGLHEADLESS)
list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
endif()

#--------------------------------------------------------------------
# Use Cocoa for window creation and NSOpenGL for context creation
#--------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions docs/window.dox
Expand Up @@ -503,6 +503,12 @@ ASCII encoded class and instance parts of the ICCCM `WM_CLASS` window property.
These are set with @ref glfwWindowHintString.


@subsubsection window_hints_egl_headless EGL headless specific window hints

@anchor GLFW_EGL_HEADLESS_DEVICE_INDEX_hint
__GLFW_EGL_HEADLESS_DEVICE_INDEX__ specifies the device index to choose when using the EGL headless backend.


@subsubsection window_hints_values Supported and default values

Window hint | Default value | Supported values
Expand Down
6 changes: 5 additions & 1 deletion include/GLFW/glfw3.h
Expand Up @@ -1011,9 +1011,13 @@ extern "C" {
*/
#define GLFW_X11_CLASS_NAME 0x00024001
/*! @brief X11 specific
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
* [window hint](@ref GLFW_X11_INSTANCE_NAME_hint).
*/
#define GLFW_X11_INSTANCE_NAME 0x00024002
/*! @brief EGL Headless specific
* [window hint](@ref GLFW_EGL_HEADLESS_DEVICE_INDEX_hint).
*/
#define GLFW_EGL_HEADLESS_DEVICE_INDEX 0x00024003
#define GLFW_WIN32_KEYBOARD_MENU 0x00025001
/*! @} */

Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -60,6 +60,11 @@ elseif (_GLFW_OSMESA)
posix_time.h posix_thread.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c
null_joystick.c posix_time.c posix_thread.c osmesa_context.c)
elseif (_GLFW_EGLHEADLESS)
set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h
posix_time.h posix_thread.h egl_context.h)
set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c
null_joystick.c posix_time.c posix_thread.c egl_context.c)
endif()

if (_GLFW_X11 OR _GLFW_WAYLAND)
Expand Down
98 changes: 89 additions & 9 deletions src/egl_context.c
Expand Up @@ -34,6 +34,13 @@
#include <stdlib.h>
#include <assert.h>

#define setAttrib(a, v) \
{ \
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = a; \
attribs[index++] = v; \
}


// Return a description of the specified EGL error
//
Expand Down Expand Up @@ -118,9 +125,14 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER)
continue;

#if defined(_GLFW_EGLHEADLESS)
if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_PBUFFER_BIT))
continue;
#else
// Only consider window EGLConfigs
if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
continue;
#endif

#if defined(_GLFW_X11)
XVisualInfo vi = {0};
Expand Down Expand Up @@ -239,13 +251,14 @@ static void swapIntervalEGL(int interval)

static int extensionSupportedEGL(const char* extension)
{
#if !defined(_GLFW_EGLHEADLESS)
const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS);
if (extensions)
{
if (_glfwStringInExtensionString(extension, extensions))
return GLFW_TRUE;
}

#endif
return GLFW_FALSE;
}

Expand Down Expand Up @@ -359,6 +372,10 @@ GLFWbool _glfwInitEGL(void)
_glfw_dlsym(_glfw.egl.handle, "eglDestroyContext");
_glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface)
_glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface");
#if defined(_GLFW_EGLHEADLESS)
_glfw.egl.CreatePbufferSurface = (PFN_eglCreatePbufferSurface)
_glfw_dlsym(_glfw.egl.handle, "eglCreatePbufferSurface");
#endif
_glfw.egl.MakeCurrent = (PFN_eglMakeCurrent)
_glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent");
_glfw.egl.SwapBuffers = (PFN_eglSwapBuffers)
Expand All @@ -381,6 +398,9 @@ GLFWbool _glfwInitEGL(void)
!_glfw.egl.DestroySurface ||
!_glfw.egl.DestroyContext ||
!_glfw.egl.CreateWindowSurface ||
#if defined(_GLFW_EGLHEADLESS)
!_glfw.egl.CreatePbufferSurface ||
#endif
!_glfw.egl.MakeCurrent ||
!_glfw.egl.SwapBuffers ||
!_glfw.egl.SwapInterval ||
Expand All @@ -394,7 +414,47 @@ GLFWbool _glfwInitEGL(void)
return GLFW_FALSE;
}

#if defined(_GLFW_EGLHEADLESS)
_glfw.egl.QueryDevicesEXT = (PFN_eglQueryDevicesEXT)
eglGetProcAddress("eglQueryDevicesEXT");
_glfw.egl.GetPlatformDisplayEXT = (PFN_eglGetPlatformDisplayEXT)
eglGetProcAddress("eglGetPlatformDisplayEXT");

if (!_glfw.egl.QueryDevicesEXT ||
!_glfw.egl.GetPlatformDisplayEXT)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to load required extension entry points");

_glfwTerminateEGL();
return GLFW_FALSE;
}

EGLDeviceEXT devices[16];
EGLint num_devices;
if (!eglQueryDevicesEXT(sizeof(devices) / sizeof(devices[0]), devices, &num_devices)) {
_glfwInputError(GLFW_API_UNAVAILABLE,
"EGL: Failed to get EGL devices: %s",
getEGLErrorString(eglGetError()));

_glfwTerminateEGL();
return GLFW_FALSE;
}

if (_glfw.hints.deviceIndex >= num_devices)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
"EGL: Invalid device index: %d",
_glfw.hints.deviceIndex);

_glfwTerminateEGL();
return GLFW_FALSE;
}

_glfw.egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[_glfw.hints.deviceIndex], NULL);
#else
_glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY);
#endif
if (_glfw.egl.display == EGL_NO_DISPLAY)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
Expand Down Expand Up @@ -446,13 +506,6 @@ void _glfwTerminateEGL(void)
}
}

#define setAttrib(a, v) \
{ \
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = a; \
attribs[index++] = v; \
}

// Create the OpenGL or OpenGL ES context
//
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
Expand Down Expand Up @@ -573,10 +626,21 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
}
}

#if defined(_GLFW_EGLHEADLESS)
setAttrib(EGL_CONTEXT_MAJOR_VERSION, ctxconfig->major);
setAttrib(EGL_CONTEXT_MINOR_VERSION, ctxconfig->minor);
setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
#endif

setAttrib(EGL_NONE, EGL_NONE);

#if defined(_GLFW_EGLHEADLESS)
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
config, share, NULL);
#else
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
config, share, attribs);
#endif

if (window->context.egl.handle == EGL_NO_CONTEXT)
{
Expand All @@ -586,6 +650,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
return GLFW_FALSE;
}

#if defined(_GLFW_EGLHEADLESS)
// Set up attributes for surface creation
{
int width, height;
_glfwPlatformGetFramebufferSize(window, &width, &height);
int index = 0;
setAttrib(EGL_WIDTH, width);
setAttrib(EGL_HEIGHT, height);
setAttrib(EGL_NONE, EGL_NONE);
}
window->context.egl.surface = eglCreatePbufferSurface(_glfw.egl.display, config, attribs);
#else
// Set up attributes for surface creation
{
int index = 0;
Expand All @@ -598,12 +674,12 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,

setAttrib(EGL_NONE, EGL_NONE);
}

window->context.egl.surface =
eglCreateWindowSurface(_glfw.egl.display,
config,
_GLFW_EGL_NATIVE_WINDOW,
attribs);
#endif
if (window->context.egl.surface == EGL_NO_SURFACE)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
Expand Down Expand Up @@ -656,8 +732,12 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
_GLFW_OPENGL_LIBRARY,
#elif defined(_GLFW_WIN32)
#elif defined(_GLFW_COCOA)
#else
#if defined(_GLFW_EGLHEADLESS)
"libOpenGL.so.0",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear why this is necessary on my system. It could be glvnd or else my system is misconfigured somehow.

#else
"libGL.so.1",
#endif
#endif
NULL
};
Expand Down
28 changes: 28 additions & 0 deletions src/egl_context.h
Expand Up @@ -43,6 +43,11 @@ typedef Window EGLNativeWindowType;
#define EGLAPIENTRY
typedef struct wl_display* EGLNativeDisplayType;
typedef struct wl_egl_window* EGLNativeWindowType;
#elif defined(_GLFW_EGLHEADLESS)
#define EGLAPIENTRY
typedef void* EGLNativeDisplayType;
typedef void* EGLNativeWindowType;
typedef void *EGLDeviceEXT;
#else
#error "No supported EGL platform selected"
#endif
Expand All @@ -67,6 +72,7 @@ typedef struct wl_egl_window* EGLNativeWindowType;
#define EGL_SURFACE_TYPE 0x3033
#define EGL_WINDOW_BIT 0x0004
#define EGL_RENDERABLE_TYPE 0x3040
#define EGL_PBUFFER_BIT 0x0001
#define EGL_OPENGL_ES_BIT 0x0001
#define EGL_OPENGL_ES2_BIT 0x0004
#define EGL_OPENGL_BIT 0x0008
Expand All @@ -87,18 +93,25 @@ typedef struct wl_egl_window* EGLNativeWindowType;
#define EGL_NO_DISPLAY ((EGLDisplay) 0)
#define EGL_NO_CONTEXT ((EGLContext) 0)
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0)
#define EGL_HEIGHT 0x3056
#define EGL_WIDTH 0x3057
#define EGL_PLATFORM_DEVICE_EXT 0x313F

#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd
#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be
#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
#define EGL_CONTEXT_MAJOR_VERSION 0x3098
#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb
#define EGL_CONTEXT_MINOR_VERSION 0x30FB
#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd
#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
#define EGL_CONTEXT_FLAGS_KHR 0x30fc
#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3
#define EGL_GL_COLORSPACE_KHR 0x309d
Expand Down Expand Up @@ -127,6 +140,11 @@ typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGL
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext);
typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*);
#if defined(_GLFW_EGLHEADLESS)
typedef EGLSurface (EGLAPIENTRY * PFN_eglCreatePbufferSurface)(EGLDisplay,EGLConfig,const EGLint*);
typedef EGLSurface (EGLAPIENTRY * PFN_eglQueryDevicesEXT)(EGLint,EGLDeviceEXT*,EGLint*);
typedef EGLSurface (EGLAPIENTRY * PFN_eglGetPlatformDisplayEXT)(EGLenum,EGLNativeDisplayType,const EGLint*);
#endif
typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint);
Expand All @@ -143,6 +161,11 @@ typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
#define eglDestroySurface _glfw.egl.DestroySurface
#define eglDestroyContext _glfw.egl.DestroyContext
#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface
#if defined(_GLFW_EGLHEADLESS)
#define eglCreatePbufferSurface _glfw.egl.CreatePbufferSurface
#define eglQueryDevicesEXT _glfw.egl.QueryDevicesEXT
#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT
#endif
#define eglMakeCurrent _glfw.egl.MakeCurrent
#define eglSwapBuffers _glfw.egl.SwapBuffers
#define eglSwapInterval _glfw.egl.SwapInterval
Expand Down Expand Up @@ -192,6 +215,11 @@ typedef struct _GLFWlibraryEGL
PFN_eglDestroySurface DestroySurface;
PFN_eglDestroyContext DestroyContext;
PFN_eglCreateWindowSurface CreateWindowSurface;
#if defined(_GLFW_EGLHEADLESS)
PFN_eglCreatePbufferSurface CreatePbufferSurface;
PFN_eglQueryDevicesEXT QueryDevicesEXT;
PFN_eglGetPlatformDisplayEXT GetPlatformDisplayEXT;
#endif
PFN_eglMakeCurrent MakeCurrent;
PFN_eglSwapBuffers SwapBuffers;
PFN_eglSwapInterval SwapInterval;
Expand Down
2 changes: 2 additions & 0 deletions src/glfw_config.h.in
Expand Up @@ -44,6 +44,8 @@
#cmakedefine _GLFW_WAYLAND
// Define this to 1 if building GLFW for OSMesa
#cmakedefine _GLFW_OSMESA
// Define this to 1 if building GLFW for EGLHeadless
#cmakedefine _GLFW_EGLHEADLESS

// Define this to 1 if building as a shared library / dynamic library / DLL
#cmakedefine _GLFW_BUILD_DLL
Expand Down
3 changes: 3 additions & 0 deletions src/internal.h
Expand Up @@ -189,6 +189,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
#include "wl_platform.h"
#elif defined(_GLFW_OSMESA)
#include "null_platform.h"
#elif defined(_GLFW_EGLHEADLESS)
#include "null_platform.h"
#else
#error "No supported window creation API selected"
#endif
Expand Down Expand Up @@ -523,6 +525,7 @@ struct _GLFWlibrary
_GLFWwndconfig window;
_GLFWctxconfig context;
int refreshRate;
int deviceIndex;
} hints;

_GLFWerror* errorListHead;
Expand Down