Prechádzať zdrojové kódy

Add GLFW_OSMESA_CONTEXT_API

This allows the creation of OpenGL contexts via OSMesa on existing
platforms.  It does not add a compile- or link-time dependency on
OSMesa.

Fixes #281.
Camilla Löwy 8 rokov pred
rodič
commit
e9560ef021

+ 2 - 0
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

+ 8 - 2
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

+ 12 - 6
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

+ 1 - 0
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.

+ 6 - 6
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)

+ 1 - 0
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)

+ 8 - 1
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)

+ 2 - 1
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",

+ 4 - 0
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;
 };
 
 

+ 11 - 10
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");

+ 2 - 2
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

+ 2 - 0
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
 

+ 2 - 1
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;

+ 1 - 0
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"
 

+ 8 - 1
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)

+ 1 - 0
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

+ 20 - 8
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)

+ 5 - 1
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();