Added support for creating OpenGL ES 2 and 3 contexts, and updated the OpenGL context creation code to be more robust in general.

If the LOVE_GRAPHICS_USE_OPENGLES environment variable is set, then love will try to create an OpenGL ES context before falling back to regular OpenGL. If it's not set (or set to 0), then the opposite happens except when love is run on backends that should always use OpenGL ES.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2015-01-22 01:02:01 -04:00
parent be8b7dd504
commit 15538426d7
4 changed files with 212 additions and 113 deletions
+1 -24
View File
@@ -49,7 +49,6 @@ Graphics::Graphics()
, height(0)
, created(false)
, writingToStencil(false)
, displayedMinReqWarning(false)
{
states.reserve(10);
states.push_back(DisplayState());
@@ -207,30 +206,8 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
this->width = width;
this->height = height;
gl.initContext();
// Does the system meet LOVE's minimum requirements for graphics?
if (!(GLAD_ES_VERSION_2_0 || (GLAD_VERSION_2_0 && Shader::isSupported() && Canvas::isSupported()))
&& !displayedMinReqWarning)
{
love::window::Window::MessageBoxType type = love::window::Window::MESSAGEBOX_ERROR;
std::string title = "Minimum system requirements not met!";
std::string message;
message += "Detected OpenGL version: ";
message += (const char *) glGetString(GL_VERSION);
message += "\nRequired OpenGL version: 2.1."; // -ish
message += "\nThe program may crash or have graphical issues.";
::printf("%s\n%s\n", title.c_str(), message.c_str());
currentWindow->showMessageBox(title, message, type, true);
// We should only show the message once, instead of after every setMode.
displayedMinReqWarning = true;
}
// Okay, setup OpenGL.
gl.initContext();
gl.setupContext();
created = true;
-2
View File
@@ -487,8 +487,6 @@ private:
static const size_t MAX_USER_STACK_DEPTH = 64;
bool displayedMinReqWarning;
}; // Graphics
} // opengl
+198 -86
View File
@@ -28,6 +28,9 @@
#include <vector>
#include <algorithm>
// C
#include <cstdio>
namespace love
{
namespace window
@@ -40,7 +43,8 @@ Window::Window()
, created(false)
, mouseGrabbed(false)
, window(0)
, context(0)
, context(nullptr)
, displayedContextError(false)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
throw love::Exception("%s", SDL_GetError());
@@ -156,10 +160,10 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
wflags &= testflags;
if (sdlflags != wflags || width != curMode.width || height != curMode.height
|| f.display != curdisplay || f.msaa != curMode.settings.msaa)
|| f.display != curdisplay || f.msaa != curMode.settings.msaa || f.sRGB != curMode.settings.sRGB)
{
SDL_DestroyWindow(window);
window = 0;
window = nullptr;
// The old window may have generated pending events which are no
// longer relevant. Destroy them all!
@@ -172,20 +176,25 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
created = false;
// In Windows and Linux, some GL attributes are set on window creation.
setWindowGLAttributes(f.msaa, f.sRGB);
setGLFramebufferAttributes(f.msaa, f.sRGB);
const char *title = windowTitle.c_str();
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
if (!window && f.msaa > 0)
{
// MSAA might have caused the failure, disable it and try again.
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
f.msaa = 0;
setGLFramebufferAttributes(f.msaa, f.sRGB);
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
}
if (!window && f.sRGB)
{
// Same with sRGB support.
f.sRGB = false;
setGLFramebufferAttributes(f.msaa, f.sRGB);
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
}
// Make sure the window keeps any previously set icon.
@@ -244,71 +253,7 @@ bool Window::onWindowResize(int width, int height)
return true;
}
bool Window::setContext(int msaa, bool vsync, bool sRGB)
{
// We would normally only need to recreate the context if MSAA changes or
// SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can
// sometimes claim success but the context will actually be trashed.
if (context)
{
SDL_GL_DeleteContext(context);
context = 0;
}
// Make sure the proper attributes are set.
setWindowGLAttributes(msaa, sRGB);
context = SDL_GL_CreateContext(window);
if (!context && msaa > 0)
{
// MSAA might have caused the failure, disable it and try again.
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
context = SDL_GL_CreateContext(window);
}
if (!context)
{
int flags = 0;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &flags);
if (flags & SDL_GL_CONTEXT_DEBUG_FLAG)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
context = SDL_GL_CreateContext(window);
}
}
if (!context)
{
std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
return false;
}
// Set vertical synchronization.
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
// Verify MSAA setting.
int buffers;
int samples;
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
// Don't fail because of this, but issue a warning.
if ((!buffers && msaa) || (samples != msaa))
{
std::cerr << "Warning, MSAA setting failed! (Result: buffers: " << buffers << ", samples: " << samples << ")" << std::endl;
msaa = (buffers > 0) ? samples : 0;
}
curMode.settings.msaa = msaa;
curMode.settings.vsync = SDL_GL_GetSwapInterval() != 0;
return true;
}
void Window::setWindowGLAttributes(int msaa, bool /* sRGB */) const
void Window::setGLFramebufferAttributes(int msaa, bool sRGB)
{
// Set GL window attributes.
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@@ -323,27 +268,194 @@ void Window::setWindowGLAttributes(int msaa, bool /* sRGB */) const
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0);
/* FIXME: Enable this code but make sure to try to re-create the window and
* context with this disabled, if creation fails with it enabled.
* We can leave this out for now because in practice the framebuffer will
* already be sRGB-capable (on desktops at least.)
#if SDL_VERSION_ATLEAST(2,0,1)
SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
#endif
*/
}
void Window::setGLContextAttributes(const ContextAttribs &attribs)
{
int profilemask = 0;
int contextflags = 0;
if (attribs.gles)
profilemask |= SDL_GL_CONTEXT_PROFILE_ES;
else if (attribs.debug)
profilemask |= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
if (attribs.debug)
contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, attribs.versionMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags);
}
bool Window::checkGLVersion(int versionmajor, int versionminor)
{
typedef unsigned char GLubyte;
typedef unsigned int GLenum;
typedef const GLubyte *(*glGetStringPtr)(GLenum name);
const GLenum GL_VERSION_ENUM = 0x1F02;
// We don't have OpenGL headers or an automatic OpenGL function loader in
// this module, so we have to get the glGetString function pointer ourselves.
glGetStringPtr glGetStringFunc = (glGetStringPtr) SDL_GL_GetProcAddress("glGetString");
if (!glGetStringFunc)
return false;
const char *glversion = (const char *) glGetStringFunc(GL_VERSION_ENUM);
if (!glversion)
return false;
int glmajor = 0;
int glminor = 0;
// glGetString(GL_VERSION) returns a string with the format "major.minor",
// or "OpenGL ES major.minor" in GLES contexts.
if (sscanf(glversion, "%d.%d", &glmajor, &glminor) != 2)
return false;
if (glmajor < versionmajor || (glmajor == versionmajor && glminor < versionminor))
return false;
return true;
}
bool Window::setContext(int msaa, bool vsync, bool sRGB)
{
// We would normally only need to recreate the context if MSAA changes or
// SDL_GL_MakeCurrent is unsuccessful, but in Windows apparently MakeCurrent
// can sometimes claim success but the context will actually be trashed.
if (context)
{
SDL_GL_DeleteContext(context);
context = nullptr;
}
bool preferGLES = false;
#ifdef LOVE_GRAPHICS_USE_OPENGLES
preferGLES = true;
#endif
const char *curdriver = SDL_GetCurrentVideoDriver();
const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt"};
// We always want to try OpenGL ES first on certain video backends.
for (const char *glesdriver : glesdrivers)
{
if (curdriver && strstr(curdriver, glesdriver) == curdriver)
{
preferGLES = true;
break;
}
}
if (!preferGLES)
{
const char *gleshint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
preferGLES = (gleshint != nullptr && gleshint[0] != '0');
}
// Do we want a debug context?
const char *debugenv = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
if (debugenv && *debugenv == '1')
const char *debughint = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
bool debug = (debughint != nullptr && debughint[0] != '0');
// Different context attribute profiles to try.
std::vector<ContextAttribs> attribslist = {
{2, 1, false, debug}, // OpenGL 2.1.
{3, 0, true, debug}, // OpenGL ES 3.
{2, 0, true, debug}, // OpenGL ES 2.
};
// Move OpenGL ES to the front of the list if we should prefer GLES.
if (preferGLES)
std::rotate(attribslist.begin(), attribslist.begin() + 1, attribslist.end());
for (ContextAttribs attribs : attribslist)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
setGLFramebufferAttributes(msaa, sRGB);
setGLContextAttributes(attribs);
context = SDL_GL_CreateContext(window);
if (!context && msaa > 0)
{
// MSAA might have caused the failure, disable it and try again.
setGLFramebufferAttributes(0, sRGB);
context = SDL_GL_CreateContext(window);
}
if (!context && sRGB)
{
// Or maybe sRGB isn't supported.
setGLFramebufferAttributes(msaa, false);
context = SDL_GL_CreateContext(window);
}
if (!context && msaa > 0 && sRGB)
{
// Or both are an issue!
setGLFramebufferAttributes(0, false);
context = SDL_GL_CreateContext(window);
}
if (!context && attribs.debug)
{
attribs.debug = false;
setGLContextAttributes(attribs);
context = SDL_GL_CreateContext(window);
}
// Make sure the context's version is at least what we requested.
if (context && !checkGLVersion(attribs.versionMajor, attribs.versionMinor))
{
SDL_GL_DeleteContext(context);
context = nullptr;
}
if (context)
break;
}
else
if (!context)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
std::string title = "Unable to initialize OpenGL";
std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
// Display a message box with the error, but only once.
if (!displayedContextError)
{
showMessageBox(title, message, MESSAGEBOX_ERROR, true);
displayedContextError = true;
}
std::cerr << title << std::endl << message << std::endl;
return false;
}
// Set vertical synchronization.
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
// Verify MSAA setting.
int buffers;
int samples;
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
// Don't fail because of this, but issue a warning.
if ((!buffers && msaa > 0) || (samples != msaa))
{
std::cerr << "Warning, MSAA setting failed! (Result: " << samples << "x MSAA)" << std::endl;
msaa = (buffers > 0) ? samples : 0;
}
curMode.settings.msaa = msaa;
curMode.settings.vsync = SDL_GL_GetSwapInterval() != 0;
return true;
}
void Window::updateSettings(const WindowSettings &newsettings)
+13 -1
View File
@@ -103,8 +103,18 @@ public:
private:
struct ContextAttribs
{
int versionMajor;
int versionMinor;
bool gles;
bool debug;
};
void setGLFramebufferAttributes(int msaa, bool sRGB);
void setGLContextAttributes(const ContextAttribs &attribs);
bool checkGLVersion(int versionmajor, int versionminor);
bool setContext(int msaa, bool vsync, bool sRGB);
void setWindowGLAttributes(int msaa, bool sRGB) const;
// Update the saved window settings based on the window's actual state.
void updateSettings(const WindowSettings &newsettings);
@@ -129,6 +139,8 @@ private:
SDL_Window *window;
SDL_GLContext context;
bool displayedContextError;
}; // Window
} // sdl