diff --git a/README.md b/README.md index fc1f0fa5..90b77025 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,8 @@ information on what to include when reporting a bug. - Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195) - Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935) - Added `GLFW_INCLUDE_ES32` for including the OpenGL ES 3.2 header +- Added `GLFW_OSMESA_CONTEXT_API` for creating OpenGL contexts with + [OSMesa](https://www.mesa3d.org/osmesa.html) (#281) - Removed `GLFW_USE_RETINA` compile-time option - Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored - Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding diff --git a/docs/news.dox b/docs/news.dox index f775f6a4..4df382c2 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -46,8 +46,14 @@ provided by [MoltenVK](https://moltengl.com/moltenvk/). @subsection news_33_osmesa OSMesa backend for headless software rendering -GLFW now supports headless context creation and software rendering via OSMesa, -intended for automated testing. This backend does not provide input. +GLFW now supports creating offscreen OpenGL contexts using +[OSMesa](https://www.mesa3d.org/osmesa.html) by setting +[GLFW_CONTEXT_CREATION_API](@ref GLFW_CONTEXT_CREATION_API_hint) to +`GLFW_OSMESA_CONTEXT_API`. + +There is also a new headless backend that uses OSMesa as its native context +creation API, intended for automated testing. This backend does not provide +input. @section news_32 New features in 3.2 diff --git a/docs/window.dox b/docs/window.dox index bad2df33..90aa7f01 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -299,9 +299,9 @@ This is a hard constraint. @anchor GLFW_CONTEXT_CREATION_API_hint __GLFW_CONTEXT_CREATION_API__ specifies which context creation API to use to -create the context. Possible values are `GLFW_NATIVE_CONTEXT_API` and -`GLFW_EGL_CONTEXT_API`. This is a hard constraint. If no client API is -requested, this hint is ignored. +create the context. Possible values are `GLFW_NATIVE_CONTEXT_API`, +`GLFW_EGL_CONTEXT_API` and `GLFW_OSMESA_CONTEXT_API`. This is a hard +constraint. If no client API is requested, this hint is ignored. @par @macos The EGL API is not available on this platform and requests to use it @@ -311,6 +311,12 @@ will fail. __Wayland, Mir:__ The EGL API _is_ the native context creation API, so this hint will have no effect. +@par +__OSMesa:__ As its name implies, an OpenGL context created with OSMesa does not +update the window contents when its buffers are swapped. Use OpenGL functions +or the OSMesa native access functions @ref glfwGetOSMesaColorBuffer and @ref +glfwGetOSMesaDepthBuffer to retrieve the framebuffer contents. + @note An OpenGL extension loader library that assumes it knows which context creation API is used on a given platform may fail if you change this hint. This can be resolved by having it load via @ref glfwGetProcAddress, which always uses @@ -480,7 +486,7 @@ GLFW_STEREO | `GLFW_FALSE` | `GLFW_TRUE` or `GL GLFW_SRGB_CAPABLE | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_DOUBLEBUFFER | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_CLIENT_API | `GLFW_OPENGL_API` | `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API` -GLFW_CONTEXT_CREATION_API | `GLFW_NATIVE_CONTEXT_API` | `GLFW_NATIVE_CONTEXT_API` or `GLFW_EGL_CONTEXT_API` +GLFW_CONTEXT_CREATION_API | `GLFW_NATIVE_CONTEXT_API` | `GLFW_NATIVE_CONTEXT_API`, `GLFW_EGL_CONTEXT_API` or `GLFW_OSMESA_CONTEXT_API` GLFW_CONTEXT_VERSION_MAJOR | 1 | Any valid major version number of the chosen client API GLFW_CONTEXT_VERSION_MINOR | 0 | Any valid minor version number of the chosen client API GLFW_CONTEXT_ROBUSTNESS | `GLFW_NO_ROBUSTNESS` | `GLFW_NO_ROBUSTNESS`, `GLFW_NO_RESET_NOTIFICATION` or `GLFW_LOSE_CONTEXT_ON_RESET` @@ -1110,8 +1116,8 @@ either `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API`. @anchor GLFW_CONTEXT_CREATION_API_attrib __GLFW_CONTEXT_CREATION_API__ indicates the context creation API used to create -the window's context; either `GLFW_NATIVE_CONTEXT_API` or -`GLFW_EGL_CONTEXT_API`. +the window's context; either `GLFW_NATIVE_CONTEXT_API`, `GLFW_EGL_CONTEXT_API` +or `GLFW_OSMESA_CONTEXT_API`. @anchor GLFW_CONTEXT_VERSION_MAJOR_attrib @anchor GLFW_CONTEXT_VERSION_MINOR_attrib diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index a856c3c5..3fd04cdb 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -881,6 +881,7 @@ extern "C" { #define GLFW_NATIVE_CONTEXT_API 0x00036001 #define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 /*! @defgroup shapes Standard cursor shapes * @brief Standard system cursor shapes. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a84ac5c8..4c3afb8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,22 +7,22 @@ set(common_SOURCES context.c init.c input.c monitor.c vulkan.c window.c) if (_GLFW_COCOA) set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h - posix_tls.h nsgl_context.h egl_context.h) + posix_tls.h nsgl_context.h egl_context.h osmesa_context.c) set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m cocoa_monitor.m cocoa_window.m cocoa_time.c posix_tls.c - nsgl_context.m egl_context.c) + nsgl_context.m egl_context.c osmesa_context.c) elseif (_GLFW_WIN32) set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h - wgl_context.h egl_context.h) + wgl_context.h egl_context.h osmesa_context.h) set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c win32_monitor.c win32_time.c win32_tls.c win32_window.c - wgl_context.c egl_context.c) + wgl_context.c egl_context.c osmesa_context.c) elseif (_GLFW_X11) set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h posix_time.h - posix_tls.h glx_context.h egl_context.h) + posix_tls.h glx_context.h egl_context.h osmesa_context.h) set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c xkb_unicode.c posix_time.c posix_tls.c glx_context.c - egl_context.c) + egl_context.c osmesa_context.c) if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(glfw_HEADERS ${glfw_HEADERS} linux_joystick.h) diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 6498619b..0b47d611 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -55,6 +55,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacO #include "cocoa_joystick.h" #include "nsgl_context.h" #include "egl_context.h" +#include "osmesa_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 735c27e4..c2358a35 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1065,13 +1065,20 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) diff --git a/src/context.c b/src/context.c index 0043d92e..ed921a4b 100644 --- a/src/context.c +++ b/src/context.c @@ -41,7 +41,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && - ctxconfig->source != GLFW_EGL_CONTEXT_API) + ctxconfig->source != GLFW_EGL_CONTEXT_API && + ctxconfig->source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid context creation API %i", diff --git a/src/internal.h b/src/internal.h index 5ae0e777..e8ba6eec 100644 --- a/src/internal.h +++ b/src/internal.h @@ -363,6 +363,8 @@ struct _GLFWcontext _GLFW_PLATFORM_CONTEXT_STATE; // This is defined in egl_context.h _GLFW_EGL_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_CONTEXT_STATE; }; /*! @brief Window and context structure. @@ -531,6 +533,8 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_TLS_STATE; // This is defined in egl_context.h _GLFW_EGL_LIBRARY_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; }; diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 70daa5ab..2b70e40f 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -36,25 +36,26 @@ static void makeContextCurrentOSMesa(_GLFWwindow* window) { if (window) { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + // Check to see if we need to allocate a new buffer if ((window->context.osmesa.buffer == NULL) || - (window->osmesa.width != window->context.osmesa.width) || - (window->osmesa.height != window->context.osmesa.height)) + (width != window->context.osmesa.width) || + (height != window->context.osmesa.height)) { free(window->context.osmesa.buffer); // Allocate the new buffer (width * height * 8-bit RGBA) - window->context.osmesa.buffer = - calloc(4, window->osmesa.width * window->osmesa.height); - - window->context.osmesa.width = window->osmesa.width; - window->context.osmesa.height = window->osmesa.height; + window->context.osmesa.buffer = calloc(4, width * height); + window->context.osmesa.width = width; + window->context.osmesa.height = height; } if (!OSMesaMakeCurrent(window->context.osmesa.handle, - window->context.osmesa.buffer, - GL_UNSIGNED_BYTE, - window->osmesa.width, window->osmesa.height)) + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + width, height)) { _glfwInputError(GLFW_PLATFORM_ERROR, "OSMesa: Failed to make context current"); diff --git a/src/osmesa_context.h b/src/osmesa_context.h index c50513d1..275546f1 100644 --- a/src/osmesa_context.h +++ b/src/osmesa_context.h @@ -57,8 +57,8 @@ typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextOSMesa osmesa -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa +#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data diff --git a/src/osmesa_platform.h b/src/osmesa_platform.h index 9571bab8..0d929bda 100644 --- a/src/osmesa_platform.h +++ b/src/osmesa_platform.h @@ -32,9 +32,11 @@ #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowOSMesa osmesa +#define _GLFW_PLATFORM_CONTEXT_STATE #define _GLFW_PLATFORM_MONITOR_STATE #define _GLFW_PLATFORM_CURSOR_STATE #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE #define _GLFW_EGL_CONTEXT_STATE #define _GLFW_EGL_LIBRARY_CONTEXT_STATE diff --git a/src/osmesa_window.c b/src/osmesa_window.c index d0593826..def78e9b 100644 --- a/src/osmesa_window.c +++ b/src/osmesa_window.c @@ -52,7 +52,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (ctxconfig->client != GLFW_NO_API) { - if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; diff --git a/src/win32_platform.h b/src/win32_platform.h index cf01eb69..8cf2b5a4 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -207,6 +207,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #include "win32_joystick.h" #include "wgl_context.h" #include "egl_context.h" +#include "osmesa_context.h" #define _GLFW_WNDCLASSNAME L"GLFW30" diff --git a/src/win32_window.c b/src/win32_window.c index 52549c9d..1e0a587e 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1084,13 +1084,20 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) diff --git a/src/x11_platform.h b/src/x11_platform.h index a88e133c..b2e4f5de 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -100,6 +100,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk #include "xkb_unicode.h" #include "glx_context.h" #include "egl_context.h" +#include "osmesa_context.h" #if defined(__linux__) #include "linux_joystick.h" #else diff --git a/src/x11_window.c b/src/x11_window.c index d65ba9ab..c65d3d22 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1586,12 +1586,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, Visual* visual; int depth; - if (ctxconfig->client == GLFW_NO_API) - { - visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); - depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); - } - else + if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { @@ -1600,13 +1595,25 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + } + } + + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); + depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); } if (!createNativeWindow(window, wndconfig, visual, depth)) @@ -1619,11 +1626,16 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 6e9335ee..2e492a5c 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -43,6 +43,7 @@ #define API_NAME_NATIVE "native" #define API_NAME_EGL "egl" +#define API_NAME_OSMESA "osmesa" #define PROFILE_NAME_CORE "core" #define PROFILE_NAME_COMPAT "compat" @@ -65,7 +66,8 @@ static void usage(void) BEHAVIOR_NAME_FLUSH ")\n"); printf(" -c, --context-api=API the context creation API to use (" API_NAME_NATIVE " or " - API_NAME_EGL ")\n"); + API_NAME_EGL " or " + API_NAME_OSMESA ")\n"); printf(" -d, --debug request a debug context\n"); printf(" -f, --forward require a forward-compatible context\n"); printf(" -h, --help show this help\n"); @@ -452,6 +454,8 @@ int main(int argc, char** argv) glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); else if (strcasecmp(optarg, API_NAME_EGL) == 0) glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + else if (strcasecmp(optarg, API_NAME_OSMESA) == 0) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API); else { usage();