|
@@ -28,6 +28,9 @@
|
|
#include <vector>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
+// C
|
|
|
|
+#include <cstdio>
|
|
|
|
+
|
|
namespace love
|
|
namespace love
|
|
{
|
|
{
|
|
namespace window
|
|
namespace window
|
|
@@ -40,7 +43,8 @@ Window::Window()
|
|
, created(false)
|
|
, created(false)
|
|
, mouseGrabbed(false)
|
|
, mouseGrabbed(false)
|
|
, window(0)
|
|
, window(0)
|
|
- , context(0)
|
|
|
|
|
|
+ , context(nullptr)
|
|
|
|
+ , displayedContextError(false)
|
|
{
|
|
{
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
throw love::Exception("%s", SDL_GetError());
|
|
throw love::Exception("%s", SDL_GetError());
|
|
@@ -156,10 +160,10 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
|
wflags &= testflags;
|
|
wflags &= testflags;
|
|
|
|
|
|
if (sdlflags != wflags || width != curMode.width || height != curMode.height
|
|
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);
|
|
SDL_DestroyWindow(window);
|
|
- window = 0;
|
|
|
|
|
|
+ window = nullptr;
|
|
|
|
|
|
// The old window may have generated pending events which are no
|
|
// The old window may have generated pending events which are no
|
|
// longer relevant. Destroy them all!
|
|
// longer relevant. Destroy them all!
|
|
@@ -172,20 +176,25 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
|
created = false;
|
|
created = false;
|
|
|
|
|
|
// In Windows and Linux, some GL attributes are set on window creation.
|
|
// 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();
|
|
const char *title = windowTitle.c_str();
|
|
-
|
|
|
|
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
|
|
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
|
|
|
|
|
|
if (!window && f.msaa > 0)
|
|
if (!window && f.msaa > 0)
|
|
{
|
|
{
|
|
// MSAA might have caused the failure, disable it and try again.
|
|
// 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);
|
|
|
|
|
|
+ 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);
|
|
window = SDL_CreateWindow(title, x, y, width, height, sdlflags);
|
|
- f.msaa = 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Make sure the window keeps any previously set icon.
|
|
// Make sure the window keeps any previously set icon.
|
|
@@ -244,45 +253,186 @@ bool Window::onWindowResize(int width, int height)
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void Window::setGLFramebufferAttributes(int msaa, bool sRGB)
|
|
|
|
+{
|
|
|
|
+ // Set GL window attributes.
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
|
|
|
|
+
|
|
|
|
+ // MSAA.
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
|
|
|
|
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0);
|
|
|
|
+
|
|
|
|
+#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)
|
|
bool Window::setContext(int msaa, bool vsync, bool sRGB)
|
|
{
|
|
{
|
|
// We would normally only need to recreate the context if MSAA changes or
|
|
// 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.
|
|
|
|
|
|
+ // SDL_GL_MakeCurrent is unsuccessful, but in Windows apparently MakeCurrent
|
|
|
|
+ // can sometimes claim success but the context will actually be trashed.
|
|
if (context)
|
|
if (context)
|
|
{
|
|
{
|
|
SDL_GL_DeleteContext(context);
|
|
SDL_GL_DeleteContext(context);
|
|
- context = 0;
|
|
|
|
|
|
+ context = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
- // Make sure the proper attributes are set.
|
|
|
|
- setWindowGLAttributes(msaa, sRGB);
|
|
|
|
|
|
+ bool preferGLES = false;
|
|
|
|
|
|
- context = SDL_GL_CreateContext(window);
|
|
|
|
|
|
+#ifdef LOVE_GRAPHICS_USE_OPENGLES
|
|
|
|
+ preferGLES = true;
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ const char *curdriver = SDL_GetCurrentVideoDriver();
|
|
|
|
+ const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt"};
|
|
|
|
|
|
- if (!context && msaa > 0)
|
|
|
|
|
|
+ // We always want to try OpenGL ES first on certain video backends.
|
|
|
|
+ for (const char *glesdriver : glesdrivers)
|
|
{
|
|
{
|
|
- // 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 (curdriver && strstr(curdriver, glesdriver) == curdriver)
|
|
|
|
+ {
|
|
|
|
+ preferGLES = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- if (!context)
|
|
|
|
|
|
+ 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 *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)
|
|
{
|
|
{
|
|
- int flags = 0;
|
|
|
|
- SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &flags);
|
|
|
|
- if (flags & SDL_GL_CONTEXT_DEBUG_FLAG)
|
|
|
|
|
|
+ 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)
|
|
{
|
|
{
|
|
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
|
|
|
|
|
|
+ // Or both are an issue!
|
|
|
|
+ setGLFramebufferAttributes(0, false);
|
|
context = SDL_GL_CreateContext(window);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
if (!context)
|
|
if (!context)
|
|
{
|
|
{
|
|
- std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
|
|
|
|
|
|
+ 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;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -296,9 +446,9 @@ bool Window::setContext(int msaa, bool vsync, bool sRGB)
|
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
|
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
|
|
|
|
|
|
// Don't fail because of this, but issue a warning.
|
|
// Don't fail because of this, but issue a warning.
|
|
- if ((!buffers && msaa) || (samples != msaa))
|
|
|
|
|
|
+ if ((!buffers && msaa > 0) || (samples != msaa))
|
|
{
|
|
{
|
|
- std::cerr << "Warning, MSAA setting failed! (Result: buffers: " << buffers << ", samples: " << samples << ")" << std::endl;
|
|
|
|
|
|
+ std::cerr << "Warning, MSAA setting failed! (Result: " << samples << "x MSAA)" << std::endl;
|
|
msaa = (buffers > 0) ? samples : 0;
|
|
msaa = (buffers > 0) ? samples : 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -308,44 +458,6 @@ bool Window::setContext(int msaa, bool vsync, bool sRGB)
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
-void Window::setWindowGLAttributes(int msaa, bool /* sRGB */) const
|
|
|
|
-{
|
|
|
|
- // Set GL window attributes.
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
|
|
|
|
-
|
|
|
|
- // MSAA.
|
|
|
|
- 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
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- // Do we want a debug context?
|
|
|
|
- const char *debugenv = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
|
|
|
|
- if (debugenv && *debugenv == '1')
|
|
|
|
- {
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
|
|
|
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
void Window::updateSettings(const WindowSettings &newsettings)
|
|
void Window::updateSettings(const WindowSettings &newsettings)
|
|
{
|
|
{
|
|
Uint32 wflags = SDL_GetWindowFlags(window);
|
|
Uint32 wflags = SDL_GetWindowFlags(window);
|