Add GLFW_CONTEXT_RENDERER
This window hint allows choosing between hardware and software renderers
(where available) for the created OpenGL or OpenGL ES context.
Fixes #589.
diff --git a/README.md b/README.md
index 638d956..84dc23a 100644
--- a/README.md
+++ b/README.md
@@ -160,6 +160,8 @@
(#749,#842)
- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889)
- Added `GLFW_LOCK_KEY_MODS` input mode and `GLFW_MOD_*_LOCK` mod bits (#946)
+- Added `GLFW_CONTEXT_RENDERER` window hint and `GLFW_HARDWARE_RENDERER` and
+ `GLFW_SOFTWARE_RENDERER` hint values (#589)
- Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint
- Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195)
- Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935)
diff --git a/docs/window.dox b/docs/window.dox
index db29d61..bb0c922 100644
--- a/docs/window.dox
+++ b/docs/window.dox
@@ -339,6 +339,11 @@
in a single process will cause the application to segfault. Stick to one API or
the other on Linux for now.
+@anchor GLFW_CONTEXT_RENDERER_hint
+__GLFW_CONTEXT_RENDERER__ specifies whether to create the context using
+a hardware or software renderer, if that is possible to control on the current
+platform.
+
@anchor GLFW_CONTEXT_VERSION_MAJOR_hint
@anchor GLFW_CONTEXT_VERSION_MINOR_hint
__GLFW_CONTEXT_VERSION_MAJOR__ and __GLFW_CONTEXT_VERSION_MINOR__ specify the
@@ -496,6 +501,7 @@
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`, `GLFW_EGL_CONTEXT_API` or `GLFW_OSMESA_CONTEXT_API`
+GLFW_CONTEXT_RENDERER | `GLFW_HARDWARE_RENDERER` | `GLFW_HARDWARE_RENDERER` or `GLFW_SOFTWARE_RENDERER`
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`
diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index 07f8b17..1ef8e7c 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -962,6 +962,7 @@
* [attribute](@ref GLFW_CLIENT_API_attrib).
*/
#define GLFW_CONTEXT_CREATION_API 0x0002200B
+#define GLFW_CONTEXT_RENDERER 0x0002200C
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
#define GLFW_COCOA_FRAME_AUTOSAVE 0x00023002
@@ -997,6 +998,9 @@
#define GLFW_EGL_CONTEXT_API 0x00036002
#define GLFW_OSMESA_CONTEXT_API 0x00036003
+#define GLFW_HARDWARE_RENDERER 0x00037001
+#define GLFW_SOFTWARE_RENDERER 0x00037002
+
/*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes.
*
diff --git a/src/context.c b/src/context.c
index 3842f0a..3fe4b5a 100644
--- a/src/context.c
+++ b/src/context.c
@@ -50,6 +50,15 @@
return GLFW_FALSE;
}
+ if (ctxconfig->renderer != GLFW_HARDWARE_RENDERER &&
+ ctxconfig->renderer != GLFW_SOFTWARE_RENDERER)
+ {
+ _glfwInputError(GLFW_INVALID_ENUM,
+ "Invalid context renderer 0x%08X",
+ ctxconfig->renderer);
+ return GLFW_FALSE;
+ }
+
if (ctxconfig->client != GLFW_NO_API &&
ctxconfig->client != GLFW_OPENGL_API &&
ctxconfig->client != GLFW_OPENGL_ES_API)
diff --git a/src/glx_context.c b/src/glx_context.c
index 40da6c2..cea1c88 100644
--- a/src/glx_context.c
+++ b/src/glx_context.c
@@ -460,6 +460,9 @@
return GLFW_FALSE;
}
+ if (ctxconfig->renderer == GLFW_SOFTWARE_RENDERER)
+ setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
+
if (ctxconfig->client == GLFW_OPENGL_ES_API)
{
if (!_glfw.glx.ARB_create_context ||
diff --git a/src/internal.h b/src/internal.h
index 84d096c..2d760aa 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -330,6 +330,7 @@
int profile;
int robustness;
int release;
+ int renderer;
_GLFWwindow* share;
struct {
GLFWbool offline;
diff --git a/src/nsgl_context.m b/src/nsgl_context.m
index a7cbf00..688e122 100644
--- a/src/nsgl_context.m
+++ b/src/nsgl_context.m
@@ -26,6 +26,8 @@
#include "internal.h"
+#include <OpenGL/CGLRenderers.h>
+
static void makeContextCurrentNSGL(_GLFWwindow* window)
{
@@ -165,9 +167,17 @@
NSOpenGLPixelFormatAttribute attribs[40];
int index = 0;
- addAttrib(NSOpenGLPFAAccelerated);
addAttrib(NSOpenGLPFAClosestPolicy);
+ if (ctxconfig->renderer == GLFW_HARDWARE_RENDERER)
+ {
+ addAttrib(NSOpenGLPFAAccelerated);
+ }
+ else if (ctxconfig->renderer == GLFW_SOFTWARE_RENDERER)
+ {
+ setAttrib(NSOpenGLPFARendererID, kCGLRendererGenericFloatID);
+ }
+
if (ctxconfig->nsgl.offline)
{
addAttrib(NSOpenGLPFAAllowOfflineRenderers);
diff --git a/src/wgl_context.c b/src/wgl_context.c
index d864a47..ff082cd 100644
--- a/src/wgl_context.c
+++ b/src/wgl_context.c
@@ -100,10 +100,21 @@
continue;
}
- if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) ==
- WGL_NO_ACCELERATION_ARB)
+ if (ctxconfig->renderer == GLFW_HARDWARE_RENDERER)
{
- continue;
+ if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) ==
+ WGL_NO_ACCELERATION_ARB)
+ {
+ continue;
+ }
+ }
+ else if (ctxconfig->renderer == GLFW_SOFTWARE_RENDERER)
+ {
+ if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) !=
+ WGL_NO_ACCELERATION_ARB)
+ {
+ continue;
+ }
}
u->redBits = getPixelFormatAttrib(window, n, WGL_RED_BITS_ARB);
@@ -170,10 +181,21 @@
continue;
}
- if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
- (pfd.dwFlags & PFD_GENERIC_FORMAT))
+ if (ctxconfig->renderer == GLFW_HARDWARE_RENDERER)
{
- continue;
+ if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
+ (pfd.dwFlags & PFD_GENERIC_FORMAT))
+ {
+ continue;
+ }
+ }
+ else if (ctxconfig->renderer == GLFW_SOFTWARE_RENDERER)
+ {
+ if ((pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
+ !(pfd.dwFlags & PFD_GENERIC_FORMAT))
+ {
+ continue;
+ }
}
if (pfd.iPixelType != PFD_TYPE_RGBA)
diff --git a/src/window.c b/src/window.c
index f4468e1..9cec578 100644
--- a/src/window.c
+++ b/src/window.c
@@ -239,10 +239,11 @@
// The default is OpenGL with minimum version 1.0
memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context));
- _glfw.hints.context.client = GLFW_OPENGL_API;
- _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API;
- _glfw.hints.context.major = 1;
- _glfw.hints.context.minor = 0;
+ _glfw.hints.context.client = GLFW_OPENGL_API;
+ _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API;
+ _glfw.hints.context.renderer = GLFW_HARDWARE_RENDERER;
+ _glfw.hints.context.major = 1;
+ _glfw.hints.context.minor = 0;
// The default is a focused, visible, resizable window with decorations
memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window));
@@ -364,6 +365,9 @@
case GLFW_CONTEXT_CREATION_API:
_glfw.hints.context.source = value;
return;
+ case GLFW_CONTEXT_RENDERER:
+ _glfw.hints.context.renderer = value;
+ return;
case GLFW_CONTEXT_VERSION_MAJOR:
_glfw.hints.context.major = value;
return;
diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c
index da2e56e..2c7a7d7 100644
--- a/tests/glfwinfo.c
+++ b/tests/glfwinfo.c
@@ -54,6 +54,9 @@
#define BEHAVIOR_NAME_NONE "none"
#define BEHAVIOR_NAME_FLUSH "flush"
+#define RENDERER_NAME_HW "hw"
+#define RENDERER_NAME_SW "sw"
+
static void usage(void)
{
printf("Usage: glfwinfo [OPTION]...\n");
@@ -68,6 +71,9 @@
API_NAME_NATIVE " or "
API_NAME_EGL " or "
API_NAME_OSMESA ")\n");
+ printf(" --renderer=RENDERER the renderer to use ("
+ RENDERER_NAME_HW " or "
+ RENDERER_NAME_SW ")\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");
@@ -365,7 +371,8 @@
GLenum error;
GLFWwindow* window;
- enum { CLIENT, CONTEXT, BEHAVIOR, DEBUG, FORWARD, HELP, EXTENSIONS, LAYERS,
+ enum { CLIENT, CONTEXT, RENDERER, BEHAVIOR, DEBUG, FORWARD,
+ HELP, EXTENSIONS, LAYERS,
MAJOR, MINOR, PROFILE, ROBUSTNESS, VERSION,
REDBITS, GREENBITS, BLUEBITS, ALPHABITS, DEPTHBITS, STENCILBITS,
ACCUMREDBITS, ACCUMGREENBITS, ACCUMBLUEBITS, ACCUMALPHABITS,
@@ -375,6 +382,7 @@
{ "behavior", 1, NULL, BEHAVIOR },
{ "client-api", 1, NULL, CLIENT },
{ "context-api", 1, NULL, CONTEXT },
+ { "renderer", 1, NULL, RENDERER },
{ "debug", 0, NULL, DEBUG },
{ "forward", 0, NULL, FORWARD },
{ "help", 0, NULL, HELP },
@@ -464,6 +472,17 @@
exit(EXIT_FAILURE);
}
break;
+ case RENDERER:
+ if (strcasecmp(optarg, RENDERER_NAME_HW) == 0)
+ glfwWindowHint(GLFW_CONTEXT_RENDERER, GLFW_HARDWARE_RENDERER);
+ else if (strcasecmp(optarg, RENDERER_NAME_SW) == 0)
+ glfwWindowHint(GLFW_CONTEXT_RENDERER, GLFW_SOFTWARE_RENDERER);
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
case 'd':
case DEBUG:
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);