OpenGL 2.1 is now required to use love.graphics (resolves issue #779.)

Removed love.graphics.isSupported("canvas", "shader", "npot", "subtractive", and "mipmap"), since those features are all guaranteed by the minimum system requirements.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2014-03-12 02:26:32 -03:00
parent a94637ef2b
commit caa61563cc
19 changed files with 247 additions and 559 deletions
-5
View File
@@ -158,13 +158,8 @@ StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
{
{ "canvas", Graphics::SUPPORT_CANVAS },
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
{ "multicanvas", Graphics::SUPPORT_MULTI_CANVAS },
{ "shader", Graphics::SUPPORT_SHADER },
{ "npot", Graphics::SUPPORT_NPOT },
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
{ "mipmap", Graphics::SUPPORT_MIPMAP },
{ "dxt", Graphics::SUPPORT_DXT },
{ "bc5", Graphics::SUPPORT_BC5 },
{ "instancing", Graphics::SUPPORT_INSTANCING },
+1 -6
View File
@@ -78,13 +78,8 @@ public:
enum Support
{
SUPPORT_CANVAS = 1,
SUPPORT_HDR_CANVAS,
SUPPORT_HDR_CANVAS = 1,
SUPPORT_MULTI_CANVAS,
SUPPORT_SHADER,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
SUPPORT_MIPMAP,
SUPPORT_DXT,
SUPPORT_BC5,
SUPPORT_INSTANCING,
+2 -8
View File
@@ -213,10 +213,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
}
// set up multiple render targets
if (GLEE_VERSION_2_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
}
};
@@ -342,10 +339,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
}
// set up multiple render targets
if (GLEE_VERSION_2_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
}
};
+34 -20
View File
@@ -55,6 +55,7 @@ Graphics::Graphics()
, created(false)
, activeStencil(false)
, savedState()
, displayedMinReqWarning(false)
{
currentWindow = love::window::sdl::Window::createSingleton();
@@ -161,16 +162,38 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
this->width = width;
this->height = height;
// Okay, setup OpenGL.
gl.initContext();
// Does the system meet LOVE's minimum requirements for graphics?
if (!(GLEE_VERSION_2_0 && Shader::isSupported() && Canvas::isSupported())
&& !displayedMinReqWarning)
{
love::window::MessageBoxType type = love::window::MESSAGEBOX_ERROR;
const char *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, message.c_str());
currentWindow->showMessageBox(type, title, message.c_str());
// We should only show the message once, instead of after every setMode.
displayedMinReqWarning = true;
}
// Okay, setup OpenGL.
gl.setupContext();
created = true;
setViewportSize(width, height);
// Make sure antialiasing works when set elsewhere
if (GLEE_VERSION_1_3 || GLEE_ARB_multisample)
glEnable(GL_MULTISAMPLE);
glEnable(GL_MULTISAMPLE);
// Enable blending
glEnable(GL_BLEND);
@@ -184,8 +207,7 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
// Auto-generated mipmaps should be the best quality possible
if (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap)
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
// Enable textures
glEnable(GL_TEXTURE_2D);
@@ -609,21 +631,9 @@ void Graphics::setBlendMode(Graphics::BlendMode mode)
switch (mode)
{
case BLEND_ALPHA:
if (GLEE_VERSION_1_4 || GLEE_EXT_blend_func_separate)
{
state.srcRGB = GL_SRC_ALPHA;
state.srcA = GL_ONE;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
}
else
{
// Fallback for OpenGL implementations without support for separate blend functions.
// This will most likely only be used for the Microsoft software renderer and
// since it's still stuck with OpenGL 1.1, the only expected difference is a
// different alpha value when reading back the default framebuffer (newScreenshot).
state.srcRGB = state.srcA = GL_SRC_ALPHA;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
}
state.srcRGB = GL_SRC_ALPHA;
state.srcA = GL_ONE;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
break;
case BLEND_MULTIPLICATIVE:
state.srcRGB = state.srcA = GL_DST_COLOR;
@@ -986,8 +996,10 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
{
delete[] pixels;
delete[] screenshot;
if (curcanvas)
curcanvas->startGrab(curcanvas->getAttachedCanvases());
throw love::Exception("Out of memory.");
}
@@ -1019,8 +1031,10 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
catch (love::Exception &)
{
delete[] screenshot;
if (curcanvas)
curcanvas->startGrab(curcanvas->getAttachedCanvases());
throw;
}
+2
View File
@@ -473,6 +473,8 @@ private:
DisplayState savedState;
bool displayedMinReqWarning;
}; // Graphics
} // opengl
+28 -135
View File
@@ -39,8 +39,6 @@ float Image::defaultMipmapSharpness = 0.0f;
Image::Image(love::image::ImageData *data, Texture::Format format)
: data(data)
, cdata(nullptr)
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
@@ -58,8 +56,6 @@ Image::Image(love::image::ImageData *data, Texture::Format format)
Image::Image(love::image::CompressedData *cdata, Texture::Format format)
: data(nullptr)
, cdata(cdata)
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
@@ -112,25 +108,10 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy,
void Image::predraw()
{
bind();
if (width != paddedWidth || height != paddedHeight)
{
// NPOT image padded to POT size, so the texcoords should be scaled.
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(float(width) / float(paddedWidth), float(height) / float(paddedHeight), 0.0f);
glMatrixMode(GL_MODELVIEW);
}
}
void Image::postdraw()
{
if (width != paddedWidth || height != paddedHeight)
{
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
}
GLuint Image::getGLTexture() const
@@ -148,27 +129,18 @@ void Image::uploadCompressedMipmaps()
int count = cdata->getMipmapCount();
// We have to inform OpenGL if the image doesn't have all mipmap levels.
if (GLEE_VERSION_1_2 || GLEE_SGIS_texture_lod)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
}
else if (cdata->getWidth(count-1) > 1 || cdata->getHeight(count-1) > 1)
{
// Telling OpenGL to ignore certain levels isn't always supported.
throw love::Exception("Cannot load mipmaps: "
"compressed image does not have all required levels.");
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
for (int i = 1; i < count; i++)
{
glCompressedTexImage2DARB(GL_TEXTURE_2D,
i,
getCompressedFormat(cdata->getFormat()),
cdata->getWidth(i),
cdata->getHeight(i),
0,
GLsizei(cdata->getSize(i)),
cdata->getData(i));
glCompressedTexImage2D(GL_TEXTURE_2D,
i,
getCompressedFormat(cdata->getFormat()),
cdata->getWidth(i),
cdata->getHeight(i),
0,
GLsizei(cdata->getSize(i)),
cdata->getData(i));
}
}
@@ -178,9 +150,6 @@ void Image::createMipmaps()
if (!data || isCompressed())
return;
if (!hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system.");
// Some old drivers claim support for NPOT textures, but fail when creating
// mipmaps. We can't detect which systems will do this, so we fail gracefully
// for all NPOT images.
@@ -196,7 +165,7 @@ void Image::createMipmaps()
// Prevent other threads from changing the ImageData while we upload it.
love::thread::Lock lock(data->getMutex());
if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
{
if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
{
@@ -266,18 +235,13 @@ void Image::setWrap(const Texture::Wrap &w)
void Image::setMipmapSharpness(float sharpness)
{
if (hasMipmapSharpnessSupport())
{
// LOD bias has the range (-maxbias, maxbias)
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
// LOD bias has the range (-maxbias, maxbias)
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
bind();
bind();
// negative bias is sharper
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
}
else
mipmapSharpness = 0.0f;
// negative bias is sharper
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
}
float Image::getMipmapSharpness() const
@@ -345,28 +309,18 @@ bool Image::loadVolatile()
throw love::Exception("cannot create image: format is not supported on this system.");
}
if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
if (maxMipmapSharpness == 0.0f)
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
glGenTextures(1, &texture);
gl.bindTexture(texture);
filter.anisotropy = gl.setTextureFilter(filter);
gl.setTextureFilter(filter);
gl.setTextureWrap(wrap);
setMipmapSharpness(mipmapSharpness);
paddedWidth = width;
paddedHeight = height;
if (!hasNpot())
{
// NPOT textures will be padded to POT dimensions if necessary.
paddedWidth = next_p2(width);
paddedHeight = next_p2(height);
}
// Use a default texture if the size is too big for the system.
if (paddedWidth > gl.getMaxTextureSize() || paddedHeight > gl.getMaxTextureSize())
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
{
uploadDefaultTexture();
return true;
@@ -379,10 +333,7 @@ bool Image::loadVolatile()
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
uploadTexture();
GLenum glerr = glGetError();
if (glerr != GL_NO_ERROR)
@@ -395,51 +346,19 @@ bool Image::loadVolatile()
return true;
}
void Image::uploadTexturePadded()
{
if (isCompressed() && cdata)
{
// Padded textures don't really work if they're compressed...
throw love::Exception("Cannot create image: "
"compressed NPOT images are not supported on this system.");
}
else if (data)
{
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
glTexImage2D(GL_TEXTURE_2D,
0,
iformat,
(GLsizei)paddedWidth,
(GLsizei)paddedHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)width,
(GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::uploadTexture()
{
if (isCompressed() && cdata)
{
GLenum format = getCompressedFormat(cdata->getFormat());
glCompressedTexImage2DARB(GL_TEXTURE_2D,
0,
format,
cdata->getWidth(0),
cdata->getHeight(0),
0,
GLsizei(cdata->getSize(0)),
cdata->getData(0));
glCompressedTexImage2D(GL_TEXTURE_2D,
0,
format,
cdata->getWidth(0),
cdata->getHeight(0),
0,
GLsizei(cdata->getSize(0)),
cdata->getData(0));
}
else if (data)
{
@@ -488,10 +407,7 @@ bool Image::refresh()
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
uploadTexture();
if (glGetError() != GL_NO_ERROR)
uploadDefaultTexture();
@@ -610,36 +526,13 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
}
}
bool Image::hasNpot()
{
return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two;
}
bool Image::hasAnisotropicFilteringSupport()
{
return GLEE_EXT_texture_filter_anisotropic;
}
bool Image::hasMipmapSupport()
{
return GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap;
}
bool Image::hasMipmapSharpnessSupport()
{
return GLEE_VERSION_1_4;
}
bool Image::hasCompressedTextureSupport()
{
return GLEE_VERSION_1_3 || GLEE_ARB_texture_compression;
}
bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
{
if (!hasCompressedTextureSupport())
return false;
switch (format)
{
case image::CompressedData::FORMAT_DXT1:
-10
View File
@@ -127,14 +127,8 @@ public:
static void setDefaultMipmapFilter(FilterMode f);
static FilterMode getDefaultMipmapFilter();
static bool hasNpot();
static bool hasAnisotropicFilteringSupport();
static bool hasMipmapSupport();
static bool hasMipmapSharpnessSupport();
static bool hasCompressedTextureSupport();
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
static bool hasSRGBSupport();
private:
@@ -151,9 +145,6 @@ private:
// null if raw ImageData was used to create the texture.
love::image::CompressedData *cdata;
// Real dimensions of the texture, if it was auto-padded to POT size.
int paddedWidth, paddedHeight;
// OpenGL texture identifier.
GLuint texture;
@@ -175,7 +166,6 @@ private:
void preload();
void uploadTexturePadded();
void uploadTexture();
void uploadCompressedMipmaps();
+37 -95
View File
@@ -57,6 +57,14 @@ void OpenGL::initContext()
initOpenGLFunctions();
initVendor();
contextInitialized = true;
}
void OpenGL::setupContext()
{
if (!contextInitialized)
return;
// Store the current color so we don't have to get it through GL later.
GLfloat glcolor[4];
glGetFloatv(GL_CURRENT_COLOR, glcolor);
@@ -80,37 +88,28 @@ void OpenGL::initContext()
glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
// Initialize multiple texture unit support for shaders, if available.
// Initialize multiple texture unit support for shaders.
state.textureUnits.clear();
if (Shader::isSupported())
GLint maxtextureunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
state.textureUnits.resize(maxtextureunits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (size_t i = 0; i < state.textureUnits.size(); i++)
{
GLint maxtextureunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
state.textureUnits.resize(maxtextureunits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (size_t i = 0; i < state.textureUnits.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
}
glActiveTexture(curgltextureunit);
}
else
{
// Multitexturing not supported, so we only have 1 texture unit.
state.textureUnits.resize(1, 0);
state.curTextureUnit = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
}
glActiveTexture(curgltextureunit);
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
setBlendState(blend);
@@ -118,8 +117,6 @@ void OpenGL::initContext()
createDefaultTexture();
state.lastPseudoInstanceID = -1;
contextInitialized = true;
}
void OpenGL::deInitContext()
@@ -158,31 +155,6 @@ void OpenGL::initVendor()
void OpenGL::initOpenGLFunctions()
{
// The functionality of the core and ARB VBOs are identical, so we can
// assign the pointers of the core functions to the names of the ARB
// functions, if the latter isn't supported but the former is.
if (GLEE_VERSION_1_5 && !GLEE_ARB_vertex_buffer_object)
{
glBindBufferARB = (GLEEPFNGLBINDBUFFERARBPROC) glBindBuffer;
glBufferDataARB = (GLEEPFNGLBUFFERDATAARBPROC) glBufferData;
glBufferSubDataARB = (GLEEPFNGLBUFFERSUBDATAARBPROC) glBufferSubData;
glDeleteBuffersARB = (GLEEPFNGLDELETEBUFFERSARBPROC) glDeleteBuffers;
glGenBuffersARB = (GLEEPFNGLGENBUFFERSARBPROC) glGenBuffers;
glGetBufferParameterivARB = (GLEEPFNGLGETBUFFERPARAMETERIVARBPROC) glGetBufferParameteriv;
glGetBufferPointervARB = (GLEEPFNGLGETBUFFERPOINTERVARBPROC) glGetBufferPointerv;
glGetBufferSubDataARB = (GLEEPFNGLGETBUFFERSUBDATAARBPROC) glGetBufferSubData;
glIsBufferARB = (GLEEPFNGLISBUFFERARBPROC) glIsBuffer;
glMapBufferARB = (GLEEPFNGLMAPBUFFERARBPROC) glMapBuffer;
glUnmapBufferARB = (GLEEPFNGLUNMAPBUFFERARBPROC) glUnmapBuffer;
}
// Same deal for compressed textures.
if (GLEE_VERSION_1_3 && !GLEE_ARB_texture_compression)
{
glCompressedTexImage2DARB = (GLEEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC) glCompressedTexImage2D;
glCompressedTexSubImage2DARB = (GLEEPFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) glCompressedTexSubImage2D;
glGetCompressedTexImageARB = (GLEEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC) glGetCompressedTexImage;
}
}
void OpenGL::initMaxValues()
@@ -195,18 +167,13 @@ void OpenGL::initMaxValues()
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (Canvas::isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers))
{
int maxattachments = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
int maxattachments = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
int maxdrawbuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
int maxdrawbuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
}
else
maxRenderTargets = 0;
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
}
void OpenGL::createDefaultTexture()
@@ -369,28 +336,8 @@ OpenGL::Viewport OpenGL::getScissor() const
void OpenGL::setBlendState(const BlendState &blend)
{
if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
glBlendEquation(blend.func);
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
glBlendEquationEXT(blend.func);
else
{
if (blend.func == GL_FUNC_REVERSE_SUBTRACT)
throw love::Exception("This graphics card does not support the subtractive blend mode!");
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
}
if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA)
glBlendFunc(blend.srcRGB, blend.dstRGB);
else
{
if (GLEE_VERSION_1_4)
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else if (GLEE_EXT_blend_func_separate)
glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else
throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!");
}
glBlendEquation(blend.func);
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
state.blend = blend;
}
@@ -406,12 +353,7 @@ void OpenGL::setTextureUnit(int textureunit)
throw love::Exception("Invalid texture unit index (%d).", textureunit);
if (textureunit != state.curTextureUnit)
{
if (state.textureUnits.size() > 1)
glActiveTexture(GL_TEXTURE0 + textureunit);
else
throw love::Exception("Multitexturing is not supported.");
}
glActiveTexture(GL_TEXTURE0 + textureunit);
state.curTextureUnit = textureunit;
}
@@ -457,7 +399,7 @@ void OpenGL::deleteTexture(GLuint texture)
glDeleteTextures(1, &texture);
}
float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
{
GLint gmin, gmag;
@@ -502,8 +444,8 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
}
return f.anisotropy;
else
f.anisotropy = 1.0f;
}
graphics::Texture::Filter OpenGL::getTextureFilter()
+10 -4
View File
@@ -104,11 +104,16 @@ public:
OpenGL();
/**
* Initializes some required context state based on current and default
* OpenGL state. Call this directly after creating an OpenGL context!
* Initializes the active OpenGL context.
**/
void initContext();
/**
* Sets up some required context state based on current and default OpenGL
* state. Call this directly after initializing an OpenGL context!
**/
void setupContext();
/**
* Marks current context state as invalid and deletes OpenGL objects owned
* by this class instance. Call this directly before potentially deleting
@@ -214,9 +219,10 @@ public:
/**
* Sets the texture filter mode for the currently bound texture.
* Returns the actual amount of anisotropic filtering set.
* The anisotropy parameter of the argument is set to the actual amount of
* anisotropy that was used.
**/
float setTextureFilter(graphics::Texture::Filter &f);
void setTextureFilter(graphics::Texture::Filter &f);
/**
* Returns the texture filter mode for the currently bound texture.
+2 -6
View File
@@ -734,11 +734,7 @@ const std::map<std::string, Object *> &Shader::getBoundRetainables() const
std::string Shader::getGLSLVersion()
{
const char *tmp = nullptr;
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if (tmp == nullptr)
return "0.0";
@@ -755,7 +751,7 @@ std::string Shader::getGLSLVersion()
bool Shader::isSupported()
{
return GLEE_VERSION_2_0 && getGLSLVersion() >= "1.2";
return getGLSLVersion() >= "1.2";
}
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntries[] =
+24 -91
View File
@@ -44,16 +44,7 @@ namespace opengl
VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
{
try
{
// Try to create a VBO.
return new VBO(size, target, usage, backing);
}
catch(const love::Exception &)
{
// VBO not supported ... create regular array.
return new VertexArray(size, target, usage, backing);
}
return new VertexBuffer(size, target, usage, backing);
}
VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
@@ -63,68 +54,10 @@ VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacki
, target(target)
, usage(usage)
, backing(backing)
{
}
VertexBuffer::~VertexBuffer()
{
}
// VertexArray
VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, buf(new char[size])
{
}
VertexArray::~VertexArray()
{
delete [] buf;
}
void *VertexArray::map()
{
is_mapped = true;
return buf;
}
void VertexArray::unmap()
{
is_mapped = false;
}
void VertexArray::bind()
{
is_bound = true;
}
void VertexArray::unbind()
{
is_bound = false;
}
void VertexArray::fill(size_t offset, size_t size, const void *data)
{
memcpy(buf + offset, data, size);
}
const void *VertexArray::getPointer(size_t offset) const
{
return buf + offset;
}
// VBO
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, vbo(0)
, memory_map(0)
, is_dirty(true)
{
if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
throw love::Exception("Not supported");
if (getMemoryBacking() == BACKING_FULL)
memory_map = malloc(getSize());
@@ -137,7 +70,7 @@ VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
}
}
VBO::~VBO()
VertexBuffer::~VertexBuffer()
{
if (vbo != 0)
unload(false);
@@ -146,7 +79,7 @@ VBO::~VBO()
free(memory_map);
}
void *VBO::map()
void *VertexBuffer::map()
{
if (is_mapped)
return memory_map;
@@ -160,7 +93,7 @@ void *VBO::map()
if (is_dirty)
{
glGetBufferSubDataARB(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
glGetBufferSubData(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
is_dirty = false;
}
@@ -169,7 +102,7 @@ void *VBO::map()
return memory_map;
}
void VBO::unmap()
void VertexBuffer::unmap()
{
if (!is_mapped)
return;
@@ -178,36 +111,36 @@ void VBO::unmap()
// bound here.
if (!is_bound)
{
glBindBufferARB(getTarget(), vbo);
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
is_mapped = false;
}
void VBO::bind()
void VertexBuffer::bind()
{
if (!is_mapped)
{
glBindBufferARB(getTarget(), vbo);
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
}
void VBO::unbind()
void VertexBuffer::unbind()
{
if (is_bound)
glBindBufferARB(getTarget(), 0);
glBindBuffer(getTarget(), 0);
is_bound = false;
}
void VBO::fill(size_t offset, size_t size, const void *data)
void VertexBuffer::fill(size_t offset, size_t size, const void *data)
{
if (is_mapped || getMemoryBacking() == BACKING_FULL)
memcpy(static_cast<char *>(memory_map) + offset, data, size);
@@ -217,7 +150,7 @@ void VBO::fill(size_t offset, size_t size, const void *data)
// Not all systems have access to some faster paths...
if (GLEE_APPLE_flush_buffer_range)
{
void *mapdata = glMapBufferARB(getTarget(), GL_WRITE_ONLY);
void *mapdata = glMapBuffer(getTarget(), GL_WRITE_ONLY);
if (mapdata)
{
@@ -228,12 +161,12 @@ void VBO::fill(size_t offset, size_t size, const void *data)
glFlushMappedBufferRangeAPPLE(getTarget(), (GLintptr) offset, (GLsizei) size);
}
glUnmapBufferARB(getTarget());
glUnmapBuffer(getTarget());
}
else
{
// Fall back to a possibly slower SubData (more chance of syncing.)
glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
}
if (getMemoryBacking() != BACKING_FULL)
@@ -241,24 +174,24 @@ void VBO::fill(size_t offset, size_t size, const void *data)
}
}
const void *VBO::getPointer(size_t offset) const
const void *VertexBuffer::getPointer(size_t offset) const
{
return BUFFER_OFFSET(offset);
}
bool VBO::loadVolatile()
bool VertexBuffer::loadVolatile()
{
return load(true);
}
void VBO::unloadVolatile()
void VertexBuffer::unloadVolatile()
{
unload(true);
}
bool VBO::load(bool restore)
bool VertexBuffer::load(bool restore)
{
glGenBuffersARB(1, &vbo);
glGenBuffers(1, &vbo);
VertexBuffer::Bind bind(*this);
@@ -275,13 +208,13 @@ bool VBO::load(bool restore)
glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
// Note that if 'src' is '0', no data will be copied.
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
GLenum err = glGetError();
return (GL_NO_ERROR == err);
}
void VBO::unload(bool save)
void VertexBuffer::unload(bool save)
{
// Save data before unloading, if we need to.
if (save && getMemoryBacking() == BACKING_PARTIAL)
@@ -294,7 +227,7 @@ void VBO::unload(bool save)
is_mapped = mapped;
}
glDeleteBuffersARB(1, &vbo);
glDeleteBuffers(1, &vbo);
vbo = 0;
}
+42 -109
View File
@@ -36,15 +36,12 @@ namespace opengl
{
/**
* VertexBuffer is an abstraction over VBOs (Vertex Buffer Objects), which
* falls back to regular vertex arrays if VBOs are not supported.
*
* This allows code to take advantage of VBOs where available, but still
* work on older systems where it's *not* available. Everyone's happy.
* VertexBuffer is a thin abstraction over VBOs (Vertex Buffer Objects) and
* other OpenGL Buffer Objects.
*
* The class is (for now) meant for internal use.
*/
class VertexBuffer
class VertexBuffer : public Volatile
{
public:
@@ -61,11 +58,7 @@ public:
};
/**
* Create a new VertexBuffer (either a plain vertex array, or a VBO),
* based on what's supported on the system.
*
* If VBOs are not supported, a plain vertex array will automatically
* be created and returned instead.
* Create a new VertexBuffer.
*
* @param size The size of the VertexBuffer (in bytes).
* @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
@@ -86,7 +79,7 @@ public:
VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
/**
* Destructor. Does nothing, but must be declared virtual.
* Destructor.
*/
virtual ~VertexBuffer();
@@ -145,7 +138,7 @@ public:
*
* @return A pointer to memory which represents the buffer.
*/
virtual void *map() = 0;
virtual void *map();
/**
* Unmap a previously mapped VertexBuffer. The buffer must be unmapped
@@ -153,18 +146,18 @@ public:
*
* The VertexBuffer must be bound to use this function.
*/
virtual void unmap() = 0;
virtual void unmap();
/**
* Bind the VertexBuffer to its specified target.
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
*/
virtual void bind() = 0;
virtual void bind();
/**
* Unbind a prevously bound VertexBuffer.
*/
virtual void unbind() = 0;
virtual void unbind();
/**
* Fill a portion of the buffer with data.
@@ -175,7 +168,7 @@ public:
* @param size The size of the incoming data.
* @param data Pointer to memory to copy data from.
*/
virtual void fill(size_t offset, size_t size, const void *data) = 0;
virtual void fill(size_t offset, size_t size, const void *data);
/**
* Get a pointer which represents the specified byte offset.
@@ -183,7 +176,11 @@ public:
* @param offset The byte offset. (0 is first byte).
* @return A pointer which represents the offset.
*/
virtual const void *getPointer(size_t offset) const = 0;
virtual const void *getPointer(size_t offset) const;
// Implements Volatile.
virtual bool loadVolatile();
virtual void unloadVolatile();
/**
* This helper class can bind a VertexArray temporarily, and
@@ -214,11 +211,13 @@ public:
// VertexBuffer to work on.
VertexBuffer &buf;
};
}; // Bind
class Mapper
{
public:
/**
* Memory-maps a VertexBuffer.
*/
@@ -245,98 +244,11 @@ public:
}
private:
VertexBuffer &buf;
void *elems;
};
protected:
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
private:
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
//
MemoryBacking backing;
};
/**
* Implementation of VertexBuffer which uses plain arrays to store the data.
*
* This implementation should be supported everywhere, and acts as a fallback
* on systems which do not support VBOs.
*/
class VertexArray : public VertexBuffer
{
public:
/**
* @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
*/
VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Frees the data we've allocated.
*/
virtual ~VertexArray();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
private:
// Holds the data.
char *buf;
};
/**
* Vertex Buffer Object (VBO) implementation of VertexBuffer.
*
* This will be used on all systems that support it. It's in general
* faster than vertex arrays, but especially in use-cases where there
* is no need to update the data every frame.
**/
class VBO : public VertexBuffer, public Volatile
{
public:
/**
* @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
**/
VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Deletes the VBOs from OpenGL.
**/
virtual ~VBO();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
}; // Mapper
private:
@@ -355,6 +267,25 @@ private:
*/
void unload(bool save);
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
//
MemoryBacking backing;
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
@@ -365,7 +296,9 @@ private:
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
bool is_dirty;
};
}; // VertexBuffer
/**
* VertexIndex manages one shared VertexBuffer that stores the indices for an
@@ -344,9 +344,6 @@ int w_newCanvas(lua_State *L)
int w_newShader(lua_State *L)
{
if (!Shader::isSupported())
return luaL_error(L, "Sorry, your graphics card does not support shaders.");
// clamp stack to 2 elements
lua_settop(L, 2);
@@ -946,10 +943,6 @@ int w_isSupported(lua_State *L)
switch (support)
{
case Graphics::SUPPORT_CANVAS:
if (!Canvas::isSupported())
supported = false;
break;
case Graphics::SUPPORT_HDR_CANVAS:
if (!Canvas::isHDRSupported())
supported = false;
@@ -958,22 +951,6 @@ int w_isSupported(lua_State *L)
if (!Canvas::isMultiCanvasSupported())
supported = false;
break;
case Graphics::SUPPORT_SHADER:
if (!Shader::isSupported())
supported = false;
break;
case Graphics::SUPPORT_NPOT:
if (!Image::hasNpot())
supported = false;
break;
case Graphics::SUPPORT_SUBTRACTIVE:
if (!((GLEE_VERSION_1_4 || GLEE_ARB_imaging) || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)))
supported = false;
break;
case Graphics::SUPPORT_MIPMAP:
if (!Image::hasMipmapSupport())
supported = false;
break;
case Graphics::SUPPORT_DXT:
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_DXT5))
supported = false;
+19
View File
@@ -74,6 +74,16 @@ bool Window::getConstant(Window::Setting in, const char *&out)
return settings.find(in, out);
}
bool Window::getConstant(const char *in, MessageBoxType &out)
{
return messageBoxTypes.find(in, out);
}
bool Window::getConstant(MessageBoxType in, const char *&out)
{
return messageBoxTypes.find(in, out);
}
StringMap<Window::Setting, Window::SETTING_MAX_ENUM>::Entry Window::settingEntries[] =
{
{"fullscreen", SETTING_FULLSCREEN},
@@ -100,5 +110,14 @@ StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM>::Entry Windo
StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM> Window::fullscreenTypes(Window::fullscreenTypeEntries, sizeof(Window::fullscreenTypeEntries));
StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM>::Entry Window::messageBoxTypeEntries[] =
{
{"error", MESSAGEBOX_ERROR},
{"warning", MESSAGEBOX_WARNING},
{"info", MESSAGEBOX_INFO},
};
StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM> Window::messageBoxTypes(Window::messageBoxTypeEntries, sizeof(Window::messageBoxTypeEntries));
} // window
} // love
+16
View File
@@ -39,6 +39,14 @@ namespace window
// whole thing here because it uses the Window::Type enum.
struct WindowSettings;
enum MessageBoxType
{
MESSAGEBOX_ERROR,
MESSAGEBOX_WARNING,
MESSAGEBOX_INFO,
MESSAGEBOX_MAX_ENUM
};
class Window : public Module
{
public:
@@ -119,6 +127,8 @@ public:
virtual const void *getHandle() const = 0;
virtual void showMessageBox(MessageBoxType type, const char *title, const char *message) = 0;
//virtual static Window *createSingleton() = 0;
//virtual static Window *getSingleton() = 0;
// No virtual statics, of course, but you are supposed to implement these statics.
@@ -129,6 +139,9 @@ public:
static bool getConstant(const char *in, FullscreenType &out);
static bool getConstant(FullscreenType in, const char *&out);
static bool getConstant(const char *in, MessageBoxType &out);
static bool getConstant(MessageBoxType in, const char *&out);
protected:
static Window *singleton;
@@ -141,6 +154,9 @@ private:
static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM>::Entry fullscreenTypeEntries[];
static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM> fullscreenTypes;
static StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM>::Entry messageBoxTypeEntries[];
static StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM> messageBoxTypes;
}; // Window
struct WindowSettings
+21
View File
@@ -654,6 +654,27 @@ const void *Window::getHandle() const
return window;
}
void Window::showMessageBox(MessageBoxType type, const char *title, const char *message)
{
SDL_MessageBoxFlags sdlflags;
switch (type)
{
case MESSAGEBOX_ERROR:
sdlflags = SDL_MESSAGEBOX_ERROR;
break;
case MESSAGEBOX_WARNING:
sdlflags = SDL_MESSAGEBOX_WARNING;
break;
case MESSAGEBOX_INFO:
default:
sdlflags = SDL_MESSAGEBOX_INFORMATION;
break;
}
SDL_ShowSimpleMessageBox(sdlflags, title, message, window);
}
love::window::Window *Window::createSingleton()
{
if (!singleton)
+2
View File
@@ -83,6 +83,8 @@ public:
const void *getHandle() const;
void showMessageBox(MessageBoxType type, const char *title, const char *message);
static love::window::Window *createSingleton();
static love::window::Window *getSingleton();
+2 -14
View File
@@ -1331,13 +1331,7 @@ function love.nogame()
local create_rain
function love.load()
-- Subtractive blending isn't supported on some ancient systems, so
-- we should make sure it still looks decent in that case.
if love.graphics.isSupported("subtractive") then
love.graphics.setBackgroundColor(137, 194, 218)
else
love.graphics.setBackgroundColor(11, 88, 123)
end
love.graphics.setBackgroundColor(137, 194, 218)
local win_w = love.graphics.getWidth()
local win_h = love.graphics.getHeight()
@@ -1429,18 +1423,12 @@ function love.nogame()
end
local function draw_grid()
local blendmode = "subtractive"
if not love.graphics.isSupported("subtractive") then
-- We also change the background color in this case, so it looks OK.
blendmode = "additive"
end
local y = rain.spacing_y * rain.t
local small_y = -rain.spacing_y + y / 2
local big_y = -rain.spacing_y + y
love.graphics.setBlendMode(blendmode)
love.graphics.setBlendMode("subtractive")
love.graphics.setColor(255, 255, 255, 128)
love.graphics.draw(rain.batch, -rain.spacing_x, small_y, 0, 0.5, 0.5)
+5 -33
View File
@@ -4850,25 +4850,9 @@ const unsigned char boot_lua[] =
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x0a,
0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x61,
0x64, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x62,
0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70,
0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x63, 0x69,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x0a,
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x77, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6d, 0x61, 0x6b,
0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x69, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x6c, 0x6f,
0x6f, 0x6b, 0x73, 0x20, 0x64, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74,
0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x2e, 0x69, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x28, 0x22, 0x73, 0x75, 0x62, 0x74,
0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73,
0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
0x31, 0x33, 0x37, 0x2c, 0x20, 0x31, 0x39, 0x34, 0x2c, 0x20, 0x32, 0x31, 0x38, 0x29, 0x0a,
0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73,
0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
0x31, 0x31, 0x2c, 0x20, 0x38, 0x38, 0x2c, 0x20, 0x31, 0x32, 0x33, 0x29, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31,
0x33, 0x37, 0x2c, 0x20, 0x31, 0x39, 0x34, 0x2c, 0x20, 0x32, 0x31, 0x38, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77, 0x69, 0x6e, 0x5f, 0x77, 0x20, 0x3d, 0x20, 0x6c, 0x6f,
0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57, 0x69, 0x64,
0x74, 0x68, 0x28, 0x29, 0x0a,
@@ -5024,18 +5008,6 @@ const unsigned char boot_lua[] =
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x72,
0x61, 0x77, 0x5f, 0x67, 0x72, 0x69, 0x64, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f, 0x64, 0x65, 0x20,
0x3d, 0x20, 0x22, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70,
0x68, 0x69, 0x63, 0x73, 0x2e, 0x69, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x28, 0x22,
0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x63, 0x68, 0x61, 0x6e,
0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x61, 0x73, 0x65,
0x2c, 0x20, 0x73, 0x6f, 0x20, 0x69, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x4f, 0x4b, 0x2e, 0x0a,
0x09, 0x09, 0x09, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x61, 0x64,
0x64, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x72, 0x61, 0x69, 0x6e, 0x2e, 0x73,
0x70, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x79, 0x20, 0x2a, 0x20, 0x72, 0x61, 0x69, 0x6e, 0x2e, 0x74, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x5f, 0x79, 0x20, 0x3d, 0x20,
@@ -5044,8 +5016,8 @@ const unsigned char boot_lua[] =
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x69, 0x67, 0x5f, 0x79, 0x20, 0x3d, 0x20, 0x2d, 0x72,
0x61, 0x69, 0x6e, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x79, 0x20, 0x2b, 0x20, 0x79, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
0x74, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f,
0x64, 0x65, 0x29, 0x0a,
0x74, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x22, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61,
0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32,
0x35, 0x35, 0x2c, 0x20, 0x31, 0x32, 0x38, 0x29, 0x0a,