From 0408ee93f16ed7be5f897e938ce9b7702ed0fff0 Mon Sep 17 00:00:00 2001 From: Christopher Hesse <48501609+cshesse@users.noreply.github.com> Date: Sun, 8 Dec 2019 19:49:04 -0800 Subject: [PATCH] egl headless --- CMakeLists.txt | 14 ++++++- src/CMakeLists.txt | 5 +++ src/egl_context.c | 90 +++++++++++++++++++++++++++++++++++++++----- src/egl_context.h | 23 +++++++++++ src/glfw_config.h.in | 2 + src/internal.h | 2 + src/null_init.c | 12 ++++++ src/null_platform.h | 18 +++++++-- src/null_window.c | 19 +++++++++- 9 files changed, 170 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c7ac8192..f1aa4bec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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") @@ -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 #-------------------------------------------------------------------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6cbeed6d6..68cbf0a1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/egl_context.c b/src/egl_context.c index 6a33396f8..7a0cbd959 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -34,6 +34,13 @@ #include #include +#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 // @@ -91,6 +98,33 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* desired, EGLConfig* result) { + +#if defined(_GLFW_EGLHEADLESS) +{ + EGLint attribs[40]; + int index = 0; + setAttrib(EGL_SURFACE_TYPE, EGL_PBUFFER_BIT); + setAttrib(EGL_BLUE_SIZE, 8); + setAttrib(EGL_GREEN_SIZE, 8); + setAttrib(EGL_RED_SIZE, 8); + setAttrib(EGL_DEPTH_SIZE, 8); + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + setAttrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT); + } + else + { + setAttrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT); + } + setAttrib(EGL_NONE, EGL_NONE); + + int num_configs; + if (!eglChooseConfig(_glfw.egl.display, attribs, result, 1, &num_configs) || num_configs == 0) { + return GLFW_FALSE; + } + return GLFW_TRUE; +} +#else EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; @@ -186,6 +220,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, free(usableConfigs); return closest != NULL; +#endif } static void makeContextCurrentEGL(_GLFWwindow* window) @@ -239,13 +274,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; } @@ -359,6 +395,12 @@ 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"); + _glfw.egl.ChooseConfig = (PFN_eglChooseConfig) + _glfw_dlsym(_glfw.egl.handle, "eglChooseConfig"); + #endif _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) @@ -381,6 +423,10 @@ GLFWbool _glfwInitEGL(void) !_glfw.egl.DestroySurface || !_glfw.egl.DestroyContext || !_glfw.egl.CreateWindowSurface || + #if defined(_GLFW_EGLHEADLESS) + !_glfw.egl.CreatePbufferSurface || + !_glfw.egl.ChooseConfig || + #endif !_glfw.egl.MakeCurrent || !_glfw.egl.SwapBuffers || !_glfw.egl.SwapInterval || @@ -394,7 +440,11 @@ GLFWbool _glfwInitEGL(void) return GLFW_FALSE; } + #if defined(_GLFW_EGLHEADLESS) + _glfw.egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + #else _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); + #endif if (_glfw.egl.display == EGL_NO_DISPLAY) { _glfwInputError(GLFW_API_UNAVAILABLE, @@ -446,13 +496,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, @@ -462,7 +505,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; - int index = 0; if (!_glfw.egl.display) { @@ -501,6 +543,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, } } + int index = 0; if (_glfw.egl.KHR_create_context) { int mask = 0, flags = 0; @@ -573,10 +616,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) { @@ -586,6 +640,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; @@ -598,12 +664,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, @@ -656,8 +722,12 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, _GLFW_OPENGL_LIBRARY, #elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) +#else +#if defined(_GLFW_EGLHEADLESS) + "libOpenGL.so.0", #else "libGL.so.1", +#endif #endif NULL }; diff --git a/src/egl_context.h b/src/egl_context.h index 8bfb7db6a..4604d96ba 100644 --- a/src/egl_context.h +++ b/src/egl_context.h @@ -43,6 +43,10 @@ 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; #else #error "No supported EGL platform selected" #endif @@ -67,6 +71,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 @@ -87,9 +92,12 @@ 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_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 @@ -97,8 +105,11 @@ typedef struct wl_egl_window* EGLNativeWindowType; #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 @@ -127,6 +138,10 @@ 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 EGLBoolean (EGLAPIENTRY * PFN_eglChooseConfig)(EGLDisplay,const EGLint*,EGLConfig,EGLint,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); @@ -143,6 +158,10 @@ 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 eglChooseConfig _glfw.egl.ChooseConfig +#endif #define eglMakeCurrent _glfw.egl.MakeCurrent #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval @@ -192,6 +211,10 @@ typedef struct _GLFWlibraryEGL PFN_eglDestroySurface DestroySurface; PFN_eglDestroyContext DestroyContext; PFN_eglCreateWindowSurface CreateWindowSurface; +#if defined(_GLFW_EGLHEADLESS) + PFN_eglCreatePbufferSurface CreatePbufferSurface; + PFN_eglChooseConfig ChooseConfig; +#endif PFN_eglMakeCurrent MakeCurrent; PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in index 018952d2f..9c17bb9bc 100644 --- a/src/glfw_config.h.in +++ b/src/glfw_config.h.in @@ -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 diff --git a/src/internal.h b/src/internal.h index 4c75c9b1c..6388db3e1 100644 --- a/src/internal.h +++ b/src/internal.h @@ -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 diff --git a/src/null_init.c b/src/null_init.c index 20c76dc5a..aebf63879 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -42,11 +42,23 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { + #if defined(_GLFW_OSMESA) _glfwTerminateOSMesa(); + #elif defined(_GLFW_EGLHEADLESS) + _glfwTerminateEGL(); + #else + #error "No supported context selected" + #endif } const char* _glfwPlatformGetVersionString(void) { + #if defined(_GLFW_OSMESA) return _GLFW_VERSION_NUMBER " null OSMesa"; + #elif defined(_GLFW_EGLHEADLESS) + return _GLFW_VERSION_NUMBER " null EGL Headless"; + #else + #error "No supported context selected" + #endif } diff --git a/src/null_platform.h b/src/null_platform.h index fdea9906d..64b4ed8ee 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -34,10 +34,22 @@ #define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; } #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE struct { int dummyLibraryWindow; } #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } -#define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; } -#define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; } +#if !defined(_GLFW_EGLHEADLESS) + #define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; } + #define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; } +#endif +#if !defined(_GLFW_OSMESA) + #define _GLFW_OSMESA_CONTEXT_STATE struct { int dummyOSMesaContext; } + #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE struct { int dummyOSMesaLibraryContext; } +#endif -#include "osmesa_context.h" +#if defined(_GLFW_OSMESA) + #include "osmesa_context.h" +#elif defined(_GLFW_EGLHEADLESS) + #include "egl_context.h" +#else + #error "No supported context selected" +#endif #include "posix_time.h" #include "posix_thread.h" #include "null_joystick.h" diff --git a/src/null_window.c b/src/null_window.c index 936400d39..9a65ebc53 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -54,6 +54,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (ctxconfig->client != GLFW_NO_API) { + #if defined(_GLFW_OSMESA) if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { @@ -64,9 +65,25 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } else { - _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); + _glfwInputError(GLFW_API_UNAVAILABLE, "Null: context not available"); return GLFW_FALSE; } + #elif defined(_GLFW_EGLHEADLESS) + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Null: context not available"); + return GLFW_FALSE; + } + #else + #error "No supported context selected" + #endif } return GLFW_TRUE;