diff --git a/jni/love/src/common/version.h b/jni/love/src/common/version.h index d8798f9d..5e3e15f7 100644 --- a/jni/love/src/common/version.h +++ b/jni/love/src/common/version.h @@ -27,9 +27,9 @@ namespace love // Version stuff. const int VERSION_MAJOR = 0; const int VERSION_MINOR = 9; -const int VERSION_REV = 0; -const char *VERSION = "0.9.0"; -const char *VERSION_COMPATIBILITY[] = { VERSION, 0 }; +const int VERSION_REV = 1; +const char *VERSION = "0.9.1"; +const char *VERSION_COMPATIBILITY[] = { VERSION, "0.9.0", 0 }; const char *VERSION_CODENAME = "Baby Inspector"; } // love diff --git a/jni/love/src/modules/audio/Audio.h b/jni/love/src/modules/audio/Audio.h index b7c19257..b43391db 100644 --- a/jni/love/src/modules/audio/Audio.h +++ b/jni/love/src/modules/audio/Audio.h @@ -88,7 +88,7 @@ public: * Play the specified Source. * @param source The Source to play. **/ - virtual void play(Source *source) = 0; + virtual bool play(Source *source) = 0; /** * Stops playback on the specified source. diff --git a/jni/love/src/modules/audio/Source.h b/jni/love/src/modules/audio/Source.h index b7a17d02..43c5fafc 100644 --- a/jni/love/src/modules/audio/Source.h +++ b/jni/love/src/modules/audio/Source.h @@ -53,7 +53,7 @@ public: virtual Source *clone() = 0; - virtual void play() = 0; + virtual bool play() = 0; virtual void stop() = 0; virtual void pause() = 0; virtual void resume() = 0; diff --git a/jni/love/src/modules/audio/null/Audio.cpp b/jni/love/src/modules/audio/null/Audio.cpp index 2e5031ae..6b6fd3f9 100644 --- a/jni/love/src/modules/audio/null/Audio.cpp +++ b/jni/love/src/modules/audio/null/Audio.cpp @@ -61,12 +61,9 @@ int Audio::getMaxSources() const return 0; } -void Audio::play(love::audio::Source *) -{ -} - -void Audio::play() +bool Audio::play(love::audio::Source *) { + return false; } void Audio::stop(love::audio::Source *) diff --git a/jni/love/src/modules/audio/null/Audio.h b/jni/love/src/modules/audio/null/Audio.h index 3f571da6..372f756c 100644 --- a/jni/love/src/modules/audio/null/Audio.h +++ b/jni/love/src/modules/audio/null/Audio.h @@ -48,8 +48,7 @@ public: love::audio::Source *newSource(love::sound::SoundData *soundData); int getSourceCount() const; int getMaxSources() const; - void play(love::audio::Source *source); - void play(); + bool play(love::audio::Source *source); void stop(love::audio::Source *source); void stop(); void pause(love::audio::Source *source); diff --git a/jni/love/src/modules/audio/null/Source.cpp b/jni/love/src/modules/audio/null/Source.cpp index d343587f..0fc043cc 100644 --- a/jni/love/src/modules/audio/null/Source.cpp +++ b/jni/love/src/modules/audio/null/Source.cpp @@ -42,8 +42,9 @@ love::audio::Source *Source::clone() return this; } -void Source::play() +bool Source::play() { + return false; } void Source::stop() diff --git a/jni/love/src/modules/audio/null/Source.h b/jni/love/src/modules/audio/null/Source.h index 887b3ef5..326ff23d 100644 --- a/jni/love/src/modules/audio/null/Source.h +++ b/jni/love/src/modules/audio/null/Source.h @@ -39,7 +39,7 @@ public: virtual ~Source(); virtual love::audio::Source *clone(); - virtual void play(); + virtual bool play(); virtual void stop(); virtual void pause(); virtual void resume(); diff --git a/jni/love/src/modules/audio/openal/Audio.cpp b/jni/love/src/modules/audio/openal/Audio.cpp index 07ce2959..c6af9be1 100644 --- a/jni/love/src/modules/audio/openal/Audio.cpp +++ b/jni/love/src/modules/audio/openal/Audio.cpp @@ -69,22 +69,25 @@ void Audio::PoolThread::setFinish() } Audio::Audio() - : distanceModel(DISTANCE_INVERSE_CLAMPED) + : device(nullptr) + , capture(nullptr) + , context(nullptr) + , pool(nullptr) + , poolThread(nullptr) + , distanceModel(DISTANCE_INVERSE_CLAMPED) { - // Passing zero for default device. - device = alcOpenDevice(0); + // Passing null for default device. + device = alcOpenDevice(nullptr); - if (device == 0) + if (device == nullptr) throw love::Exception("Could not open device."); - context = alcCreateContext(device, 0); + context = alcCreateContext(device, nullptr); - if (context == 0) + if (context == nullptr) throw love::Exception("Could not create context."); - alcMakeContextCurrent(context); - - if (alcGetError(device) != ALC_NO_ERROR) + if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) throw love::Exception("Could not make context current."); /*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); @@ -108,7 +111,18 @@ Audio::Audio() }*/ // pool must be allocated after AL context. - pool = new Pool(); + try + { + pool = new Pool(); + } + catch (love::Exception &) + { + alcMakeContextCurrent(nullptr); + alcDestroyContext(context); + //if (capture) alcCaptureCloseDevice(capture); + alcCloseDevice(device); + throw; + } poolThread = new PoolThread(pool); poolThread->start(); @@ -122,7 +136,7 @@ Audio::~Audio() delete poolThread; delete pool; - alcMakeContextCurrent(0); + alcMakeContextCurrent(nullptr); alcDestroyContext(context); //if (capture) alcCaptureCloseDevice(capture); alcCloseDevice(device); @@ -154,9 +168,9 @@ int Audio::getMaxSources() const return pool->getMaxSources(); } -void Audio::play(love::audio::Source *source) +bool Audio::play(love::audio::Source *source) { - source->play(); + return source->play(); } void Audio::stop(love::audio::Source *source) @@ -281,7 +295,7 @@ bool Audio::canRecord() Audio::DistanceModel Audio::getDistanceModel() const { - return this->distanceModel; + return distanceModel; } void Audio::setDistanceModel(DistanceModel distanceModel) diff --git a/jni/love/src/modules/audio/openal/Audio.h b/jni/love/src/modules/audio/openal/Audio.h index a40494b2..6e5f5069 100644 --- a/jni/love/src/modules/audio/openal/Audio.h +++ b/jni/love/src/modules/audio/openal/Audio.h @@ -67,8 +67,7 @@ public: love::audio::Source *newSource(love::sound::SoundData *soundData); int getSourceCount() const; int getMaxSources() const; - void play(love::audio::Source *source); - void play(); + bool play(love::audio::Source *source); void stop(love::audio::Source *source); void stop(); void pause(love::audio::Source *source); diff --git a/jni/love/src/modules/audio/openal/Pool.cpp b/jni/love/src/modules/audio/openal/Pool.cpp index f3d74a91..f8e61339 100644 --- a/jni/love/src/modules/audio/openal/Pool.cpp +++ b/jni/love/src/modules/audio/openal/Pool.cpp @@ -30,23 +30,43 @@ namespace openal { Pool::Pool() + : sources() + , totalSources(0) + , mutex(nullptr) { + // Clear errors. + alGetError(); + // Generate sources. - alGenSources(NUM_SOURCES, sources); + for (int i = 0; i < MAX_SOURCES; i++) + { + alGenSources(1, &sources[i]); + + // We might hit an implementation-dependent limit on the total number + // of sources before reaching MAX_SOURCES. + if (alGetError() != AL_NO_ERROR) + break; + + totalSources++; + } + + if (totalSources < 4) + throw love::Exception("Could not generate sources."); // Create the mutex. mutex = thread::newMutex(); - if (alGetError() != AL_NO_ERROR) - throw love::Exception("Could not generate sources."); + ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels"); // Make all sources available initially. - for (int i = 0; i < NUM_SOURCES; i++) + for (int i = 0; i < totalSources; i++) { -#ifdef AL_DIRECT_CHANNELS_SOFT - // Bypassing virtualization of speakers for multi-channel sources in OpenAL Soft. - alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE); -#endif + if (hasext) + { + // Bypass virtualization of speakers for multi-channel sources in OpenAL Soft. + alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE); + } + available.push(sources[i]); } } @@ -58,7 +78,7 @@ Pool::~Pool() delete mutex; // Free all sources. - alDeleteSources(NUM_SOURCES, sources); + alDeleteSources(totalSources, sources); } bool Pool::isAvailable() const @@ -113,7 +133,7 @@ int Pool::getSourceCount() const int Pool::getMaxSources() const { - return NUM_SOURCES; + return totalSources; } bool Pool::play(Source *source, ALuint &out) @@ -141,9 +161,7 @@ bool Pool::play(Source *source, ALuint &out) source->retain(); - source->playAtomic(); - - ok = true; + ok = source->playAtomic(); } else { diff --git a/jni/love/src/modules/audio/openal/Pool.h b/jni/love/src/modules/audio/openal/Pool.h index 46353e75..f0896ea9 100644 --- a/jni/love/src/modules/audio/openal/Pool.h +++ b/jni/love/src/modules/audio/openal/Pool.h @@ -36,6 +36,7 @@ #ifdef LOVE_MACOSX_USE_FRAMEWORKS #include #include +#include #else #include #include @@ -99,11 +100,15 @@ private: bool findSource(Source *source, ALuint &out); bool removeSource(Source *source); - // Number of OpenAL sources. - static const int NUM_SOURCES = 64; + + // Maximum possible number of OpenAL sources the pool attempts to generate. + static const int MAX_SOURCES = 64; // OpenAL sources - ALuint sources[NUM_SOURCES]; + ALuint sources[MAX_SOURCES]; + + // Total number of created sources in the pool. + int totalSources; // A queue of available sources. std::queue available; diff --git a/jni/love/src/modules/audio/openal/Source.cpp b/jni/love/src/modules/audio/openal/Source.cpp index 332eb336..237cf02b 100644 --- a/jni/love/src/modules/audio/openal/Source.cpp +++ b/jni/love/src/modules/audio/openal/Source.cpp @@ -165,15 +165,16 @@ love::audio::Source *Source::clone() return new Source(*this); } -void Source::play() +bool Source::play() { if (valid && paused) { pool->resume(this); - return; + return true; } valid = pool->play(this, source); + return valid; } void Source::stop() @@ -512,7 +513,7 @@ bool Source::isLooping() const return looping; } -void Source::playAtomic() +bool Source::playAtomic() { if (type == TYPE_STATIC) { @@ -539,10 +540,19 @@ void Source::playAtomic() // of the new one. reset(); + // Clear errors. + alGetError(); + alSourcePlay(source); + // alSourcePlay may fail if the system has reached its limit of simultaneous + // playing sources. + bool success = alGetError() == AL_NO_ERROR; + valid = true; //if it fails it will be set to false again //but this prevents a horrible, horrible bug + + return success; } void Source::stopAtomic() diff --git a/jni/love/src/modules/audio/openal/Source.h b/jni/love/src/modules/audio/openal/Source.h index 9a1182d7..5591c6b8 100644 --- a/jni/love/src/modules/audio/openal/Source.h +++ b/jni/love/src/modules/audio/openal/Source.h @@ -76,7 +76,7 @@ public: virtual ~Source(); virtual love::audio::Source *clone(); - virtual void play(); + virtual bool play(); virtual void stop(); virtual void pause(); virtual void resume(); @@ -118,7 +118,7 @@ public: virtual float getMaxDistance() const; virtual int getChannels() const; - void playAtomic(); + bool playAtomic(); void stopAtomic(); void pauseAtomic(); void resumeAtomic(); diff --git a/jni/love/src/modules/audio/wrap_Audio.cpp b/jni/love/src/modules/audio/wrap_Audio.cpp index 4b8e3996..217c4f98 100644 --- a/jni/love/src/modules/audio/wrap_Audio.cpp +++ b/jni/love/src/modules/audio/wrap_Audio.cpp @@ -75,8 +75,8 @@ int w_newSource(lua_State *L) int w_play(lua_State *L) { Source *s = luax_checksource(L, 1); - instance->play(s); - return 0; + luax_pushboolean(L, instance->play(s)); + return 1; } int w_stop(lua_State *L) diff --git a/jni/love/src/modules/audio/wrap_Source.cpp b/jni/love/src/modules/audio/wrap_Source.cpp index d1f3e61a..b6b152b2 100644 --- a/jni/love/src/modules/audio/wrap_Source.cpp +++ b/jni/love/src/modules/audio/wrap_Source.cpp @@ -44,8 +44,8 @@ int w_Source_clone(lua_State *L) int w_Source_play(lua_State *L) { Source *t = luax_checksource(L, 1); - t->play(); - return 0; + luax_pushboolean(L, t->play()); + return 1; } int w_Source_stop(lua_State *L) diff --git a/jni/love/src/modules/event/sdl/Event.cpp b/jni/love/src/modules/event/sdl/Event.cpp index 28904d06..f32a1ce0 100644 --- a/jni/love/src/modules/event/sdl/Event.cpp +++ b/jni/love/src/modules/event/sdl/Event.cpp @@ -224,6 +224,14 @@ Message *Event::convert(const SDL_Event &e) const txt = "touchmoved"; msg = new Message(txt, vargs); break; + case SDL_MULTIGESTURE: + vargs.push_back(new Variant((double) e.mgesture.x)); + vargs.push_back(new Variant((double) e.mgesture.y)); + vargs.push_back(new Variant((double) e.mgesture.dTheta)); + vargs.push_back(new Variant((double) e.mgesture.dDist)); + vargs.push_back(new Variant((double) e.mgesture.numFingers)); + msg = new Message("touchgestured", vargs); + break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: case SDL_JOYAXISMOTION: diff --git a/jni/love/src/modules/graphics/Drawable.h b/jni/love/src/modules/graphics/Drawable.h index aea03b34..a4cd7e2f 100644 --- a/jni/love/src/modules/graphics/Drawable.h +++ b/jni/love/src/modules/graphics/Drawable.h @@ -55,7 +55,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0; }; } // graphics diff --git a/jni/love/src/modules/graphics/Graphics.cpp b/jni/love/src/modules/graphics/Graphics.cpp index 86886c76..64f4f280 100644 --- a/jni/love/src/modules/graphics/Graphics.cpp +++ b/jni/love/src/modules/graphics/Graphics.cpp @@ -99,6 +99,16 @@ bool Graphics::getConstant(Support in, const char *&out) return support.find(in, out); } +bool Graphics::getConstant(const char *in, SystemLimit &out) +{ + return systemLimits.find(in, out); +} + +bool Graphics::getConstant(SystemLimit in, const char *&out) +{ + return systemLimits.find(in, out); +} + StringMap::Entry Graphics::drawModeEntries[] = { { "line", Graphics::DRAW_LINE }, @@ -170,5 +180,15 @@ StringMap::Entry Graphics::suppor StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); +StringMap::Entry Graphics::systemLimitEntries[] = +{ + {"pointsize", Graphics::LIMIT_POINT_SIZE}, + {"texturesize", Graphics::LIMIT_TEXTURE_SIZE}, + {"multicanvas", Graphics::LIMIT_MULTI_CANVAS}, + {"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA}, +}; + +StringMap Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries)); + } // graphics } // love diff --git a/jni/love/src/modules/graphics/Graphics.h b/jni/love/src/modules/graphics/Graphics.h index d41d24cb..f9ca36a2 100644 --- a/jni/love/src/modules/graphics/Graphics.h +++ b/jni/love/src/modules/graphics/Graphics.h @@ -114,6 +114,15 @@ public: RENDERER_INFO_MAX_ENUM }; + enum SystemLimit + { + LIMIT_POINT_SIZE, + LIMIT_TEXTURE_SIZE, + LIMIT_MULTI_CANVAS, + LIMIT_CANVAS_FSAA, + LIMIT_MAX_ENUM + }; + virtual ~Graphics(); /** @@ -155,6 +164,9 @@ public: static bool getConstant(const char *in, Support &out); static bool getConstant(Support in, const char *&out); + static bool getConstant(const char *in, SystemLimit &out); + static bool getConstant(SystemLimit in, const char *&out); + private: static StringMap::Entry drawModeEntries[]; @@ -178,6 +190,9 @@ private: static StringMap::Entry supportEntries[]; static StringMap support; + static StringMap::Entry systemLimitEntries[]; + static StringMap systemLimits; + }; // Graphics } // graphics diff --git a/jni/love/src/modules/graphics/Texture.h b/jni/love/src/modules/graphics/Texture.h index e87c432f..7b5a525a 100644 --- a/jni/love/src/modules/graphics/Texture.h +++ b/jni/love/src/modules/graphics/Texture.h @@ -88,7 +88,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0; + virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0; virtual int getWidth() const; virtual int getHeight() const; diff --git a/jni/love/src/modules/graphics/opengl/Canvas.cpp b/jni/love/src/modules/graphics/opengl/Canvas.cpp index ee255aed..efed8598 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/Canvas.cpp @@ -63,12 +63,26 @@ struct FramebufferStrategy return false; } + /// Create a MSAA renderbuffer and attach it to the active FBO. + /** + * @param[in] width Width of the MSAA buffer + * @param[in] height Height of the MSAA buffer + * @param[inout] samples Number of samples to use + * @param[in] internalformat The internal format to use for the buffer + * @param[out] buffer Name for the MSAA buffer + * @return Whether the MSAA buffer was successfully created and attached + **/ + virtual bool createMSAABuffer(int, int, int &, GLenum, GLuint &) + { + return false; + } + /// remove objects /** * @param[in] framebuffer Framebuffer name * @param[in] depth_stencil Name for packed depth and stencil buffer */ - virtual void deleteFBO(GLuint, GLuint) {} + virtual void deleteFBO(GLuint, GLuint, GLuint) {} virtual void bindFBO(GLuint) {} /// attach additional canvases to the active framebuffer for rendering @@ -93,8 +107,11 @@ struct FramebufferStrategyCore : public FramebufferStrategy glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); + if (texture != 0) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + } // check status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -118,13 +135,44 @@ struct FramebufferStrategyCore : public FramebufferStrategy glBindRenderbuffer(GL_RENDERBUFFER, 0); // check status - return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffers(1, &stencil); + stencil = 0; + return false; + } + + return true; } - virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil) + virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer) + { + glGenRenderbuffers(1, &buffer); + glBindRenderbuffer(GL_RENDERBUFFER, buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, + width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, buffer); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffers(1, &buffer); + buffer = 0; + return false; + } + + return true; + } + + virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer) { if (depth_stencil != 0) glDeleteRenderbuffers(1, &depth_stencil); + if (msaa_buffer != 0) + glDeleteRenderbuffers(1, &msaa_buffer); if (framebuffer != 0) glDeleteFramebuffers(1, &framebuffer); } @@ -212,7 +260,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy virtual bool createStencil(int width, int height, GLuint &stencil) { // create combined depth/stencil buffer - glDeleteRenderbuffers(1, &stencil); + glDeleteRenderbuffersEXT(1, &stencil); glGenRenderbuffersEXT(1, &stencil); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, @@ -223,13 +271,47 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // check status - return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffersEXT(1, &stencil); + stencil = 0; + return false; + } + + return true; } - virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil) + virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer) + { + if (!GLAD_EXT_framebuffer_multisample) + return false; + + glGenRenderbuffersEXT(1, &buffer); + glBindRenderbufferEXT(GL_RENDERBUFFER, buffer); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, + internalformat, width, height); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, buffer); + glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + + glBindRenderbufferEXT(GL_RENDERBUFFER, 0); + + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffersEXT(1, &buffer); + buffer = 0; + return false; + } + + return true; + } + + virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer) { if (depth_stencil != 0) glDeleteRenderbuffersEXT(1, &depth_stencil); + if (msaa_buffer != 0) + glDeleteRenderbuffersEXT(1, &msaa_buffer); if (framebuffer != 0) glDeleteFramebuffersEXT(1, &framebuffer); } @@ -277,7 +359,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT virtual bool createStencil(int width, int height, GLuint &stencil) { // create stencil buffer - glDeleteRenderbuffers(1, &stencil); + glDeleteRenderbuffersEXT(1, &stencil); glGenRenderbuffersEXT(1, &stencil); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, @@ -288,14 +370,21 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // check status - return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffersEXT(1, &stencil); + stencil = 0; + return false; + } + + return true; } bool isSupported() { GLuint fb = 0, stencil = 0; GLenum status = createFBO(fb, 0); - deleteFBO(fb, stencil); + deleteFBO(fb, stencil, 0); return status == GL_FRAMEBUFFER_COMPLETE; } }; @@ -335,11 +424,15 @@ static void getStrategy() static int maxFBOColorAttachments = 0; static int maxDrawBuffers = 0; -Canvas::Canvas(int width, int height, TextureType texture_type) +Canvas::Canvas(int width, int height, TextureType texture_type, int fsaa) : fbo(0) + , resolve_fbo(0) , texture(0) + , fsaa_buffer(0) , depth_stencil(0) , texture_type(texture_type) + , fsaa_samples(fsaa) + , fsaa_dirty(false) { this->width = width; this->height = height; @@ -381,9 +474,50 @@ Canvas::~Canvas() unloadVolatile(); } +bool Canvas::createFSAAFBO(GLenum internalformat) +{ + // Create our FBO without a texture. + status = strategy->createFBO(fbo, 0); + + GLuint previous = 0; + if (current != this) + { + if (current != nullptr) + previous = current->fbo; + + strategy->bindFBO(fbo); + } + + // Create and attach the MSAA buffer for our FBO. + if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer)) + status = GL_FRAMEBUFFER_COMPLETE; + else + status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + + // Create the FBO used for the MSAA resolve, and attach the texture. + if (status == GL_FRAMEBUFFER_COMPLETE) + status = strategy->createFBO(resolve_fbo, texture); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + // Clean up. + strategy->deleteFBO(fbo, 0, fsaa_buffer); + strategy->deleteFBO(resolve_fbo, 0, 0); + fbo = fsaa_buffer = resolve_fbo = 0; + fsaa_samples = 0; + } + + if (current != this) + strategy->bindFBO(previous); + + return status == GL_FRAMEBUFFER_COMPLETE; +} + bool Canvas::loadVolatile() { fbo = depth_stencil = texture = 0; + resolve_fbo = fsaa_buffer = 0; + status = GL_FRAMEBUFFER_COMPLETE; // glTexImage2D is guaranteed to error in this case. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize()) @@ -430,7 +564,25 @@ bool Canvas::loadVolatile() return false; } - status = strategy->createFBO(fbo, texture); + int max_samples = 0; + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object + || GLAD_EXT_framebuffer_multisample) + { + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + } + + if (fsaa_samples > max_samples) + fsaa_samples = max_samples; + + // Try to create a FSAA FBO if requested. + bool fsaasuccess = false; + if (fsaa_samples > 1) + fsaasuccess = createFSAAFBO(internalformat); + + // On failure (or no requested FSAA), fall back to a regular FBO. + if (!fsaasuccess) + status = strategy->createFBO(fbo, texture); + if (status != GL_FRAMEBUFFER_COMPLETE) return false; @@ -440,11 +592,13 @@ bool Canvas::loadVolatile() void Canvas::unloadVolatile() { - strategy->deleteFBO(fbo, depth_stencil); + strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer); + strategy->deleteFBO(resolve_fbo, 0, 0); gl.deleteTexture(texture); fbo = depth_stencil = texture = 0; + resolve_fbo = fsaa_buffer = 0; for (size_t i = 0; i < attachedCanvases.size(); i++) attachedCanvases[i]->release(); @@ -452,7 +606,7 @@ void Canvas::unloadVolatile() attachedCanvases.clear(); } -void Canvas::drawv(const Matrix &t, const Vertex *v) const +void Canvas::drawv(const Matrix &t, const Vertex *v) { gl.matrices.transform.push(gl.matrices.transform.top()); gl.matrices.transform.top() *= t; @@ -475,10 +629,9 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) const postdraw(); gl.matrices.transform.pop(); - } -void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -486,7 +639,7 @@ void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, f drawv(t, vertices); } -void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -514,8 +667,12 @@ GLuint Canvas::getGLTexture() const return texture; } -void Canvas::predraw() const +void Canvas::predraw() { + // We need to make sure the texture is up-to-date by resolving the MSAA + // buffer (which we render to when the canvas is active) to it. + resolveMSAA(); + gl.bindTexture(texture); } @@ -539,6 +696,9 @@ void Canvas::setupGrab() // indicate we are using this fbo current = this; + + if (fsaa_buffer != 0) + fsaa_dirty = true; } void Canvas::startGrab(const std::vector &canvases) @@ -554,6 +714,9 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments)) throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1); + + if (fsaa_samples != 0) + throw love::Exception("Multi-canvas rendering is not supported with FSAA."); } for (size_t i = 0; i < canvases.size(); i++) @@ -564,6 +727,9 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases[i]->getTextureType() != texture_type) throw love::Exception("All canvas arguments must have the same texture type."); + if (canvases[i]->getFSAA() != 0) + throw love::Exception("Multi-canvas rendering is not supported with FSAA."); + if (!canvaseschanged && canvases[i] != attachedCanvases[i]) canvaseschanged = true; } @@ -698,12 +864,22 @@ bool Canvas::checkCreateStencil() love::image::ImageData *Canvas::getImageData(love::image::Image *image) { + resolveMSAA(); + int row = 4 * width; int size = row * height; GLubyte *pixels = new GLubyte[size]; - strategy->bindFBO(fbo); + // Our texture is attached to 'resolve_fbo' when we use MSAA. + if (fsaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)) + glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (fsaa_samples > 1 && GLAD_EXT_framebuffer_multisample) + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo); + else + strategy->bindFBO(fbo); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + if (current) strategy->bindFBO(current->fbo); else @@ -716,10 +892,17 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image) void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y) { - if (current != this) + resolveMSAA(); + + // Our texture is attached to 'resolve_fbo' when we use MSAA. + if (fsaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)) + glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (fsaa_samples > 1 && GLAD_EXT_framebuffer_multisample) + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (current != this) strategy->bindFBO(fbo); - glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba); + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba); if (current && current != this) strategy->bindFBO(current->fbo); @@ -727,6 +910,44 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y) strategy->bindFBO(gl.getDefaultFBO()); } +bool Canvas::resolveMSAA() +{ + if (resolve_fbo == 0 || fsaa_buffer == 0) + return false; + + if (!fsaa_dirty) + return true; + + GLuint previous = 0; + if (current != nullptr) + previous = current->fbo; + + // Do the MSAA resolve by blitting the MSAA renderbuffer to the texture. + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else if (GLAD_EXT_framebuffer_multisample && GLAD_EXT_framebuffer_blit) + { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, resolve_fbo); + glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else + return false; + + strategy->bindFBO(previous); + + if (current != this) + fsaa_dirty = false; + + return true; +} + bool Canvas::isSupported() { getStrategy(); @@ -740,17 +961,8 @@ bool Canvas::isHDRSupported() bool Canvas::isMultiCanvasSupported() { - if (!(isSupported() && (GLAD_VERSION_2_0 || GLAD_ARB_draw_buffers))) - return false; - - if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0) - { - glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments); - glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); - } - - // system must support at least 4 simultanious active canvases - return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4; + // system must support at least 4 simultanious active canvases. + return gl.getMaxRenderTargets() >= 4; } void Canvas::bindDefaultCanvas() diff --git a/jni/love/src/modules/graphics/opengl/Canvas.h b/jni/love/src/modules/graphics/opengl/Canvas.h index e9e9f7ca..f643294f 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.h +++ b/jni/love/src/modules/graphics/opengl/Canvas.h @@ -46,7 +46,7 @@ public: TYPE_MAX_ENUM }; - Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL); + Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL, int fsaa = 0); virtual ~Canvas(); // Implements Volatile. @@ -54,14 +54,14 @@ public: virtual void unloadVolatile(); // Implements Drawable. - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); // Implements Texture. - virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); virtual void setFilter(const Texture::Filter &f); virtual void setWrap(const Texture::Wrap &w); virtual GLuint getGLTexture() const; - virtual void predraw() const; + virtual void predraw(); /** * @param canvases A list of other canvases to temporarily attach to this one, @@ -97,6 +97,13 @@ public: return texture_type; } + inline int getFSAA() const + { + return fsaa_samples; + } + + bool resolveMSAA(); + static bool isSupported(); static bool isHDRSupported(); static bool isMultiCanvasSupported(); @@ -112,8 +119,13 @@ public: private: + bool createFSAAFBO(GLenum internalformat); + GLuint fbo; + GLuint resolve_fbo; + GLuint texture; + GLuint fsaa_buffer; GLuint depth_stencil; TextureType texture_type; @@ -122,8 +134,11 @@ private: std::vector attachedCanvases; + int fsaa_samples; + bool fsaa_dirty; + void setupGrab(); - void drawv(const Matrix &t, const Vertex *v) const; + void drawv(const Matrix &t, const Vertex *v); static StringMap::Entry textureTypeEntries[]; static StringMap textureTypes; diff --git a/jni/love/src/modules/graphics/opengl/Graphics.cpp b/jni/love/src/modules/graphics/opengl/Graphics.cpp index 8a31fc2e..22f049aa 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/Graphics.cpp @@ -228,6 +228,18 @@ bool Graphics::setMode(int width, int height) Shader::defaultShader->attach(); } + bool enabledebug = false; + + if (GLAD_VERSION_3_0) + { + // Enable OpenGL's debug output if a debug context has been created. + GLint flags = 0; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + enabledebug = (flags & GL_CONTEXT_FLAG_DEBUG_BIT) != 0; + } + + setDebug(enabledebug); + return true; } @@ -246,6 +258,63 @@ void Graphics::unSetMode() } } +static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/) +{ + // Human-readable strings for the debug info. + const char *sourceStr = OpenGL::debugSourceString(source); + const char *typeStr = OpenGL::debugTypeString(type); + const char *severityStr = OpenGL::debugSeverityString(severity); + + const char *fmt = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n"; + printf(fmt, msg, sourceStr, typeStr, severityStr, id); +} + +void Graphics::setDebug(bool enable) +{ + // Make sure debug output is supported. The AMD ext. is a bit different + // so we don't make use of it, since AMD drivers now support KHR_debug. + if (!(GLAD_VERSION_4_3 || GLAD_KHR_debug || GLAD_ARB_debug_output)) + return; + + // Ugly hack to reduce code duplication. + if (GLAD_ARB_debug_output && !(GLAD_VERSION_4_3 || GLAD_KHR_debug)) + { + fp_glDebugMessageCallback = (pfn_glDebugMessageCallback) fp_glDebugMessageCallbackARB; + fp_glDebugMessageControl = (pfn_glDebugMessageControl) fp_glDebugMessageControlARB; + } + else if (GLAD_ES_VERSION_2_0 && GLAD_KHR_debug) + { + fp_glDebugMessageCallback = (pfn_glDebugMessageCallback) fp_glDebugMessageCallbackKHR; + fp_glDebugMessageControl = (pfn_glDebugMessageControl) fp_glDebugMessageControlKHR; + } + + if (!enable) + { + // Disable the debug callback function. + glDebugMessageCallback(nullptr, nullptr); + + // We can disable debug output entirely with KHR_debug. + if (GLAD_VERSION_4_3 || GLAD_KHR_debug) + glDisable(GL_DEBUG_OUTPUT); + + return; + } + + // We don't want asynchronous debug output. + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + glDebugMessageCallback(debugCB, nullptr); + + // Initially, enable everything. + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); + + // Disable messages about deprecated OpenGL functionality. + glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); + glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); + + ::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n"); +} + void Graphics::reset() { DisplayState s; @@ -361,11 +430,6 @@ void Graphics::discardStencil() glDisable(GL_STENCIL_TEST); } -int Graphics::getMaxTextureSize() const -{ - return gl.getMaxTextureSize(); -} - Image *Graphics::newImage(love::image::ImageData *data) { // Create the image. @@ -440,7 +504,7 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size) return new ParticleSystem(texture, size); } -Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type) +Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type, int fsaa) { if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported()) throw Exception("HDR Canvases are not supported by your OpenGL implementation"); @@ -453,7 +517,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t while (GL_NO_ERROR != glGetError()) /* clear opengl error flag */; - Canvas *canvas = new Canvas(width, height, texture_type); + Canvas *canvas = new Canvas(width, height, texture_type, fsaa); GLenum err = canvas->getStatus(); // everything ok, return canvas (early out) @@ -771,13 +835,6 @@ Graphics::PointStyle Graphics::getPointStyle() const return POINT_ROUGH; } -int Graphics::getMaxPointSize() const -{ - GLint max; - glGetIntegerv(GL_POINT_SIZE_MAX, &max); - return (int)max; -} - void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) { if (currentFont != nullptr) @@ -1103,6 +1160,41 @@ std::string Graphics::getRendererInfo(Graphics::RendererInfo infotype) const return std::string(infostr); } +double Graphics::getSystemLimit(SystemLimit limittype) const +{ + double limit = 0.0; + + switch (limittype) + { + case Graphics::LIMIT_POINT_SIZE: + { + GLfloat limits[2]; + glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits); + limit = limits[1]; + } + break; + case Graphics::LIMIT_TEXTURE_SIZE: + limit = (double) gl.getMaxTextureSize(); + break; + case Graphics::LIMIT_MULTI_CANVAS: + limit = (double) gl.getMaxRenderTargets(); + break; + case Graphics::LIMIT_CANVAS_FSAA: + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object + || GLAD_EXT_framebuffer_multisample) + { + GLint intlimit = 0; + glGetIntegerv(GL_MAX_SAMPLES, &intlimit); + limit = (double) intlimit; + } + break; + default: + break; + } + + return limit; +} + void Graphics::push() { if (gl.matrices.transform.size() == matrixLimit) diff --git a/jni/love/src/modules/graphics/opengl/Graphics.h b/jni/love/src/modules/graphics/opengl/Graphics.h index 1ef59246..9ddf7488 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.h +++ b/jni/love/src/modules/graphics/opengl/Graphics.h @@ -115,6 +115,8 @@ public: virtual bool setMode(int width, int height); virtual void unSetMode(); + void setDebug(bool enable); + /** * Resets the current color, background color, * line style, and so forth. (This will be called @@ -186,11 +188,6 @@ public: */ void discardStencil(); - /** - * Gets the maximum supported width or height of Textures on this system. - **/ - int getMaxTextureSize() const; - /** * Creates an Image object with padding and/or optimization. **/ @@ -208,7 +205,7 @@ public: ParticleSystem *newParticleSystem(Texture *texture, int size); - Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL); + Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL, int fsaa = 0); Shader *newShader(const Shader::ShaderSources &sources); @@ -337,12 +334,6 @@ public: **/ PointStyle getPointStyle() const; - /** - * Gets the maximum point size supported. - * This may vary from computer to computer. - **/ - int getMaxPointSize() const; - /** * Draws text at the specified coordinates, with rotation and * scaling along both axes. @@ -444,6 +435,11 @@ public: **/ std::string getRendererInfo(Graphics::RendererInfo infotype) const; + /** + * Gets the system-dependent numeric limit for the specified parameter. + **/ + double getSystemLimit(SystemLimit limittype) const; + void push(); void pop(); void rotate(float r); diff --git a/jni/love/src/modules/graphics/opengl/Image.cpp b/jni/love/src/modules/graphics/opengl/Image.cpp index 6cf9ac9d..4be217fb 100644 --- a/jni/love/src/modules/graphics/opengl/Image.cpp +++ b/jni/love/src/modules/graphics/opengl/Image.cpp @@ -91,7 +91,7 @@ love::image::CompressedData *Image::getCompressedData() const return cdata; } -void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -99,7 +99,7 @@ void Image::draw(float x, float y, float angle, float sx, float sy, float ox, fl drawv(t, vertices); } -void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -107,7 +107,7 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, drawv(t, quad->getVertices()); } -void Image::predraw() const +void Image::predraw() { bind(); @@ -121,7 +121,7 @@ void Image::predraw() const } } -void Image::postdraw() const +void Image::postdraw() { if (width != paddedWidth || height != paddedHeight) { @@ -511,7 +511,7 @@ void Image::uploadDefaultTexture() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); } -void Image::drawv(const Matrix &t, const Vertex *v) const +void Image::drawv(const Matrix &t, const Vertex *v) { predraw(); diff --git a/jni/love/src/modules/graphics/opengl/Image.h b/jni/love/src/modules/graphics/opengl/Image.h index c7f4cf88..d6da44f2 100644 --- a/jni/love/src/modules/graphics/opengl/Image.h +++ b/jni/love/src/modules/graphics/opengl/Image.h @@ -76,20 +76,20 @@ public: /** * @copydoc Drawable::draw() **/ - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * @copydoc Texture::drawq() **/ - void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Call before using this Image's texture to draw. Binds the texture, * globally scales texture coordinates if the Image has NPOT dimensions and * NPOT isn't supported, etc. **/ - virtual void predraw() const; - virtual void postdraw() const; + virtual void predraw(); + virtual void postdraw(); virtual GLuint getGLTexture() const; @@ -136,7 +136,7 @@ private: void uploadDefaultTexture(); - void drawv(const Matrix &t, const Vertex *v) const; + void drawv(const Matrix &t, const Vertex *v); // The ImageData from which the texture is created. May be null if // Compressed image data was used to create the texture. diff --git a/jni/love/src/modules/graphics/opengl/Mesh.cpp b/jni/love/src/modules/graphics/opengl/Mesh.cpp index 1c43a114..a9bdb6ed 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.cpp +++ b/jni/love/src/modules/graphics/opengl/Mesh.cpp @@ -288,7 +288,7 @@ bool Mesh::isWireframe() const return wireframe; } -void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t pos_offset = offsetof(Vertex, x); const size_t tex_offset = offsetof(Vertex, s); diff --git a/jni/love/src/modules/graphics/opengl/Mesh.h b/jni/love/src/modules/graphics/opengl/Mesh.h index 7719c1c5..3a5029a4 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.h +++ b/jni/love/src/modules/graphics/opengl/Mesh.h @@ -172,7 +172,7 @@ public: bool isWireframe() const; // Implements Drawable. - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.cpp b/jni/love/src/modules/graphics/opengl/OpenGL.cpp index aa0e6cc6..47837a03 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.cpp +++ b/jni/love/src/modules/graphics/opengl/OpenGL.cpp @@ -43,6 +43,7 @@ OpenGL::OpenGL() : contextInitialized(false) , maxAnisotropy(1.0f) , maxTextureSize(0) + , maxRenderTargets(0) , vendor(VENDOR_UNKNOWN) , state() { @@ -202,6 +203,19 @@ void OpenGL::initMaxValues() maxAnisotropy = 1.0f; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + + if (Canvas::isSupported() && (GLAD_VERSION_2_0 || GLAD_ARB_draw_buffers)) + { + int maxattachments = 0; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments); + + int maxdrawbuffers = 0; + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers); + + maxRenderTargets = std::min(maxattachments, maxdrawbuffers); + } + else + maxRenderTargets = 0; } void OpenGL::initMatrices() @@ -260,6 +274,18 @@ void OpenGL::prepareDraw() glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f); state.lastPseudoInstanceID = 0; } + + // We need to make sure antialiased Canvases are properly resolved + // before sampling from their textures in a shader. + // This is kind of a big hack. :( + const std::map &r = shader->getBoundRetainables(); + for (auto it = r.begin(); it != r.end(); ++it) + { + // Even bigger hack! D: + Canvas *canvas = dynamic_cast(it->second); + if (canvas != nullptr) + canvas->resolveMSAA(); + } } if (GLAD_ES_VERSION_2_0 && shader) @@ -716,11 +742,77 @@ int OpenGL::getMaxTextureSize() const return maxTextureSize; } +int OpenGL::getMaxRenderTargets() const +{ + return maxRenderTargets; +} + OpenGL::Vendor OpenGL::getVendor() const { return vendor; } +const char *OpenGL::debugSeverityString(GLenum severity) +{ + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: + return "high"; + case GL_DEBUG_SEVERITY_MEDIUM: + return "medium"; + case GL_DEBUG_SEVERITY_LOW: + return "low"; + default: + break; + } + return "unknown"; +} + +const char *OpenGL::debugSourceString(GLenum source) +{ + switch (source) + { + case GL_DEBUG_SOURCE_API: + return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + return "window"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + return "shader"; + case GL_DEBUG_SOURCE_THIRD_PARTY: + return "external"; + case GL_DEBUG_SOURCE_APPLICATION: + return "LOVE"; + case GL_DEBUG_SOURCE_OTHER: + return "other"; + default: + break; + } + return "unknown"; +} + +const char *OpenGL::debugTypeString(GLenum type) +{ + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + return "error"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + return "deprecated behavior"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + return "undefined behavior"; + case GL_DEBUG_TYPE_PERFORMANCE: + return "performance"; + case GL_DEBUG_TYPE_PORTABILITY: + return "portability"; + case GL_DEBUG_TYPE_OTHER: + return "other"; + default: + break; + } + return "unknown"; +} + + // OpenGL class instance singleton. OpenGL gl; diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.h b/jni/love/src/modules/graphics/opengl/OpenGL.h index 26684d64..c802b148 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.h +++ b/jni/love/src/modules/graphics/opengl/OpenGL.h @@ -96,6 +96,11 @@ public: Viewport(int _x, int _y, int _w, int _h) : x(_x), y(_y), w(_w), h(_h) {} + + bool operator == (const Viewport &rhs) const + { + return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; + } }; // Transformation matrix stacks. @@ -271,11 +276,21 @@ public: **/ int getMaxTextureSize() const; + /** + * Returns the maximum supported number of simultaneous render targets. + **/ + int getMaxRenderTargets() const; + /** * Get the GPU vendor of this OpenGL context. **/ Vendor getVendor() const; + // Get human-readable strings for debug info. + static const char *debugSeverityString(GLenum severity); + static const char *debugSourceString(GLenum source); + static const char *debugTypeString(GLenum type); + private: void initVendor(); @@ -290,6 +305,7 @@ private: float maxAnisotropy; int maxTextureSize; + int maxRenderTargets; Vendor vendor; diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp index c972b13f..9c9fb0c4 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp @@ -442,14 +442,14 @@ ParticleSystem::InsertMode ParticleSystem::getInsertMode() const return insertMode; } -void ParticleSystem::setEmissionRate(int rate) +void ParticleSystem::setEmissionRate(float rate) { - if (rate < 0) + if (rate < 0.0f) throw love::Exception("Invalid emission rate"); emissionRate = rate; } -int ParticleSystem::getEmissionRate() const +float ParticleSystem::getEmissionRate() const { return emissionRate; } @@ -484,6 +484,7 @@ void ParticleSystem::getParticleLifetime(float *min, float *max) const void ParticleSystem::setPosition(float x, float y) { position = love::Vector(x, y); + prevPosition = position; } const love::Vector &ParticleSystem::getPosition() const @@ -491,6 +492,11 @@ const love::Vector &ParticleSystem::getPosition() const return position; } +void ParticleSystem::moveTo(float x, float y) +{ + position = love::Vector(x, y); +} + void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y) { areaSpread = love::Vector(x, y); @@ -790,7 +796,7 @@ bool ParticleSystem::isFull() const return activeParticles == maxParticles; } -void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { uint32 pCount = getCount(); if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr) diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.h b/jni/love/src/modules/graphics/opengl/ParticleSystem.h index a65ccbf5..7373d4bf 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.h @@ -132,12 +132,12 @@ public: * Sets the emission rate. * @param rate The amount of particles per second. **/ - void setEmissionRate(int rate); + void setEmissionRate(float rate); /** * Returns the number of particles created per second. **/ - int getEmissionRate() const; + float getEmissionRate() const; /** * Sets the lifetime of the particle emitter (-1 means eternal) @@ -177,6 +177,15 @@ public: **/ const love::Vector &getPosition() const; + /** + * Moves the position of the center of the emitter. + * When update is called, newly spawned particles will appear in a line + * between the old emitter position and where the emitter was moved to, + * resulting in a smoother-feeling particle system if moveTo is called + * repeatedly. + **/ + void moveTo(float x, float y); + /** * Sets the emission area spread parameters and distribution type. The interpretation of * the parameters depends on the distribution type: @@ -471,7 +480,7 @@ public: * @param x The x-coordinate. * @param y The y-coordinate. **/ - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Updates the particle system. @@ -551,7 +560,7 @@ protected: uint32 activeParticles; // The emission rate (particles/sec). - int emissionRate; + float emissionRate; // Used to determine when a particle should be emitted. float emitCounter; diff --git a/jni/love/src/modules/graphics/opengl/Shader.cpp b/jni/love/src/modules/graphics/opengl/Shader.cpp index 7917701a..8858fd18 100644 --- a/jni/love/src/modules/graphics/opengl/Shader.cpp +++ b/jni/love/src/modules/graphics/opengl/Shader.cpp @@ -75,6 +75,7 @@ Shader::Shader(const ShaderSources &sources) , builtinUniforms() , vertexAttributes() , lastCanvas((Canvas *) -1) + , lastViewport() { if (shaderSources.empty()) throw love::Exception("Cannot create shader: no source code!"); @@ -753,30 +754,42 @@ bool Shader::sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m void Shader::checkSetScreenParams() { - if (lastCanvas == Canvas::current) + OpenGL::Viewport view = gl.getViewport(); + + if (view == lastViewport && lastCanvas == Canvas::current) return; - // In the shader, we do pixcoord.y = gl_FragCoord.y * params[0] + params[1]. + // In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w. // This lets us flip pixcoord.y when needed, to be consistent (Canvases // have flipped y-values for pixel coordinates.) - GLfloat params[] = {0.0f, 0.0f}; + GLfloat params[] = { + (GLfloat) view.w, (GLfloat) view.h, + 0.0f, 0.0f, + }; if (Canvas::current != nullptr) { // gl_FragCoord.y is flipped in Canvases, so we un-flip: // pixcoord.y = gl_FragCoord.y * -1.0 + height. - params[0] = -1.0f; - params[1] = (float) Canvas::current->getHeight(); + params[2] = -1.0f; + params[3] = (GLfloat) view.h; } else { // No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0. - params[0] = 1.0f; - params[1] = 0.0f; + params[2] = 1.0f; + params[3] = 0.0f; } - sendBuiltinFloat(BUILTIN_SCREEN_PARAMS, 2, params, 1); + sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1); + lastCanvas = Canvas::current; + lastViewport = view; +} + +const std::map &Shader::getBoundRetainables() const +{ + return boundRetainables; } std::string Shader::getGLSLVersion() @@ -829,7 +842,7 @@ StringMap::Entry Shader::builti {"ProjectionMatrix", Shader::BUILTIN_PROJECTION_MATRIX}, {"TransformProjectionMatrix", Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX}, {"love_PointSize", Shader::BUILTIN_POINT_SIZE}, - {"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS}, + {"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE}, }; StringMap Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries)); diff --git a/jni/love/src/modules/graphics/opengl/Shader.h b/jni/love/src/modules/graphics/opengl/Shader.h index 9c9a80f1..c5d20ba0 100644 --- a/jni/love/src/modules/graphics/opengl/Shader.h +++ b/jni/love/src/modules/graphics/opengl/Shader.h @@ -67,7 +67,7 @@ public: BUILTIN_PROJECTION_MATRIX, BUILTIN_TRANSFORM_PROJECTION_MATRIX, BUILTIN_POINT_SIZE, - BUILTIN_SCREEN_PARAMS, + BUILTIN_SCREEN_SIZE, BUILTIN_MAX_ENUM }; @@ -152,6 +152,8 @@ public: bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count); void checkSetScreenParams(); + const std::map &getBoundRetainables() const; + static std::string getGLSLVersion(); static bool isSupported(); @@ -227,6 +229,7 @@ private: // Pointer to the active Canvas when the screen params were last checked. Canvas *lastCanvas; + OpenGL::Viewport lastViewport; // Max GPU texture units available for sent images static GLint maxTexUnits; diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp index 7902bdef..ad2c5b3c 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp @@ -263,7 +263,7 @@ int SpriteBatch::getBufferSize() const return size; } -void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t vertex_offset = offsetof(Vertex, x); const size_t texel_offset = offsetof(Vertex, s); diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.h b/jni/love/src/modules/graphics/opengl/SpriteBatch.h index de14bf09..ae79db8d 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.h +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.h @@ -109,7 +109,7 @@ public: int getBufferSize() const; // Implements Drawable. - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); static bool getConstant(const char *in, UsageHint &out); static bool getConstant(UsageHint in, const char *&out); diff --git a/jni/love/src/modules/graphics/opengl/Texture.h b/jni/love/src/modules/graphics/opengl/Texture.h index 6dee5535..3a02decb 100644 --- a/jni/love/src/modules/graphics/opengl/Texture.h +++ b/jni/love/src/modules/graphics/opengl/Texture.h @@ -48,12 +48,12 @@ public: * Any setup the texture might need to do before drawing, e.g. binding * the OpenGL texture for use. **/ - virtual void predraw() const {} + virtual void predraw() {} /** * Any cleanup the texture might need to do directly after drawing. **/ - virtual void postdraw() const {} + virtual void postdraw() {} }; // Texture diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp index 9a466956..cbc9b6ed 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -120,6 +120,13 @@ int w_Canvas_getType(lua_State *L) return 1; } +int w_Canvas_getFSAA(lua_State *L) +{ + Canvas *canvas = luax_checkcanvas(L, 1); + lua_pushinteger(L, canvas->getFSAA()); + return 1; +} + static const luaL_Reg functions[] = { // From wrap_Texture. @@ -136,6 +143,7 @@ static const luaL_Reg functions[] = { "getPixel", w_Canvas_getPixel }, { "clear", w_Canvas_clear }, { "getType", w_Canvas_getType }, + { "getFSAA", w_Canvas_getFSAA }, { 0, 0 } }; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h index 4a6bea87..89c7952f 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h @@ -40,6 +40,7 @@ int w_Canvas_getImageData(lua_State *L); int w_Canvas_getPixel(lua_State * L); int w_Canvas_clear(lua_State *L); int w_Canvas_getType(lua_State *L); +int w_Canvas_getFSAA(lua_State *L); extern "C" int luaopen_canvas(lua_State *L); } // opengl diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp index 8db87269..ca76ef85 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -146,7 +146,7 @@ int w_setInvertedStencil(lua_State *L) int w_getMaxTextureSize(lua_State *L) { - lua_pushinteger(L, instance->getMaxTextureSize()); + lua_pushinteger(L, instance->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE)); return 1; } @@ -323,15 +323,16 @@ int w_newCanvas(lua_State *L) int width = luaL_optint(L, 1, instance->getWidth()); int height = luaL_optint(L, 2, instance->getHeight()); const char *str = luaL_optstring(L, 3, "normal"); + int fsaa = luaL_optint(L, 4, 0); Canvas::TextureType texture_type; if (!Canvas::getConstant(str, texture_type)) return luaL_error(L, "Invalid canvas type: %s", str); - Canvas *canvas = 0; - EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type);) + Canvas *canvas = nullptr; + EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type, fsaa);) - if (canvas == 0) + if (canvas == nullptr) return luaL_error(L, "Canvas not created, but no error thrown. I don't even..."); luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas); @@ -830,7 +831,7 @@ int w_getPointStyle(lua_State *L) int w_getMaxPointSize(lua_State *L) { - lua_pushnumber(L, instance->getMaxPointSize()); + lua_pushnumber(L, instance->getSystemLimit(Graphics::LIMIT_POINT_SIZE)); return 1; } @@ -1025,7 +1026,7 @@ int w_isSupported(lua_State *L) supported = false; break; case Graphics::SUPPORT_INSTANCING: - if (!GLAD_ARB_draw_instanced) + if (!(GLAD_ARB_draw_instanced || (GLAD_ES_VERSION_2_0 && GLAD_EXT_draw_instanced))) supported = false; break; default: @@ -1055,6 +1056,18 @@ int w_getRendererInfo(lua_State *L) return 4; } +int w_getSystemLimit(lua_State *L) +{ + const char *limitstr = luaL_checkstring(L, 1); + Graphics::SystemLimit limittype; + + if (!Graphics::getConstant(limitstr, limittype)) + return luaL_error(L, "Invalid system limit type: %s", limitstr); + + lua_pushnumber(L, instance->getSystemLimit(limittype)); + return 1; +} + int w_draw(lua_State *L) { Drawable *drawable = nullptr; @@ -1392,8 +1405,6 @@ static const luaL_Reg functions[] = { "setPointStyle", w_setPointStyle }, { "getPointSize", w_getPointSize }, { "getPointStyle", w_getPointStyle }, - { "getMaxPointSize", w_getMaxPointSize }, - { "getMaxTextureSize", w_getMaxTextureSize }, { "newScreenshot", w_newScreenshot }, { "setCanvas", w_setCanvas }, { "getCanvas", w_getCanvas }, @@ -1404,6 +1415,7 @@ static const luaL_Reg functions[] = { "isSupported", w_isSupported }, { "getRendererInfo", w_getRendererInfo }, + { "getSystemLimit", w_getSystemLimit }, { "draw", w_draw }, @@ -1439,6 +1451,7 @@ static const luaL_Reg functions[] = // Deprecated since 0.9.1. { "getMaxImageSize", w_getMaxTextureSize }, + { "getMaxPointSize", w_getMaxPointSize }, { 0, 0 } }; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h index 24098b80..5c85c819 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h @@ -93,6 +93,7 @@ int w_getShader(lua_State *L); int w_setDefaultShaderCode(lua_State *L); int w_isSupported(lua_State *L); int w_getRendererInfo(lua_State *L); +int w_getSystemLimit(lua_State *L); int w_draw(lua_State *L); int w_print(lua_State *L); int w_printf(lua_State *L); diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp index 91517d60..6e875b64 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp @@ -127,7 +127,7 @@ int w_ParticleSystem_getInsertMode(lua_State *L) int w_ParticleSystem_setEmissionRate(lua_State *L) { ParticleSystem *t = luax_checkparticlesystem(L, 1); - int arg1 = luaL_checkint(L, 2); + float arg1 = (float) luaL_checknumber(L, 2); EXCEPT_GUARD(t->setEmissionRate(arg1);) return 0; } @@ -135,7 +135,7 @@ int w_ParticleSystem_setEmissionRate(lua_State *L) int w_ParticleSystem_getEmissionRate(lua_State *L) { ParticleSystem *t = luax_checkparticlesystem(L, 1); - lua_pushinteger(L, t->getEmissionRate()); + lua_pushnumber(L, t->getEmissionRate()); return 1; } @@ -191,6 +191,15 @@ int w_ParticleSystem_getPosition(lua_State *L) return 2; } +int w_ParticleSystem_moveTo(lua_State *L) +{ + ParticleSystem *t = luax_checkparticlesystem(L, 1); + float arg1 = (float)luaL_checknumber(L, 2); + float arg2 = (float)luaL_checknumber(L, 3); + t->moveTo(arg1, arg2); + return 0; +} + int w_ParticleSystem_setAreaSpread(lua_State *L) { ParticleSystem *t = luax_checkparticlesystem(L, 1); @@ -649,6 +658,7 @@ static const luaL_Reg functions[] = { "getParticleLifetime", w_ParticleSystem_getParticleLifetime }, { "setPosition", w_ParticleSystem_setPosition }, { "getPosition", w_ParticleSystem_getPosition }, + { "moveTo", w_ParticleSystem_moveTo }, { "setAreaSpread", w_ParticleSystem_setAreaSpread }, { "getAreaSpread", w_ParticleSystem_getAreaSpread }, { "setDirection", w_ParticleSystem_setDirection }, diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h index 84937474..08948c38 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h @@ -48,6 +48,7 @@ int w_ParticleSystem_setParticleLifetime(lua_State *L); int w_ParticleSystem_getParticleLifetime(lua_State *L); int w_ParticleSystem_setPosition(lua_State *L); int w_ParticleSystem_getPosition(lua_State *L); +int w_ParticleSystem_moveTo(lua_State *L); int w_ParticleSystem_setAreaSpread(lua_State *L); int w_ParticleSystem_getAreaSpread(lua_State *L); int w_ParticleSystem_setDirection(lua_State *L); diff --git a/jni/love/src/modules/love/love.cpp b/jni/love/src/modules/love/love.cpp index b27f96ab..4610e638 100644 --- a/jni/love/src/modules/love/love.cpp +++ b/jni/love/src/modules/love/love.cpp @@ -182,6 +182,15 @@ const char *love_codename() return love::VERSION_CODENAME; } +static int w_love_getVersion(lua_State *L) +{ + lua_pushinteger(L, love::VERSION_MAJOR); + lua_pushinteger(L, love::VERSION_MINOR); + lua_pushinteger(L, love::VERSION_REV); + lua_pushstring(L, love::VERSION_CODENAME); + return 4; +} + int luaopen_love(lua_State * L) { love::luax_insistglobal(L, "love"); @@ -215,6 +224,9 @@ int luaopen_love(lua_State * L) lua_setfield(L, -2, "_version_compat"); + lua_pushcfunction(L, w_love_getVersion); + lua_setfield(L, -2, "getVersion"); + #ifdef LOVE_WINDOWS lua_pushstring(L, "Windows"); #elif defined(LOVE_MACOSX) diff --git a/jni/love/src/modules/window/sdl/Window.cpp b/jni/love/src/modules/window/sdl/Window.cpp index b7fde9b0..18fffdba 100644 --- a/jni/love/src/modules/window/sdl/Window.cpp +++ b/jni/love/src/modules/window/sdl/Window.cpp @@ -257,6 +257,18 @@ bool Window::setContext(int fsaa, bool vsync) 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) { displayError("Could not create OpenGL context", SDL_GetError()); @@ -299,6 +311,8 @@ void Window::setWindowGLAttributes(int fsaa) const SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0); + int contextprofile = 0; + bool tryES2 = false; const char *driver = SDL_GetCurrentVideoDriver(); @@ -314,17 +328,30 @@ void Window::setWindowGLAttributes(int fsaa) const if (tryES2) { // Use OpenGL ES 2+. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + contextprofile = SDL_GL_CONTEXT_PROFILE_ES; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); } else { // Use desktop OpenGL. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + contextprofile = 0; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); // default SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); } + + // 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); + if (!tryES2) + contextprofile |= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY; + } + else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, contextprofile); } void Window::updateSettings(const WindowSettings &newsettings) diff --git a/jni/love/src/scripts/boot.lua b/jni/love/src/scripts/boot.lua index 3fed9eb3..9336476c 100644 --- a/jni/love/src/scripts/boot.lua +++ b/jni/love/src/scripts/boot.lua @@ -179,6 +179,9 @@ function love.createhandlers() touchmoved = function (id,x,y,p) if love.touchmoved then return love.touchmoved(id,x,y,p) end end, + touchgestured = function (x,y,theta,dist,num) + if love.touchgestured then return love.touchgestured(x,y,theta,dist,num) end + end, joystickpressed = function (j,b) if love.joystickpressed then return love.joystickpressed(j,b) end end, @@ -354,6 +357,11 @@ function love.init() c.console = true end + -- Console hack + if c.console and love._openConsole then + love._openConsole() + end + -- Gets desired modules. for k,v in ipairs{ "android", @@ -383,11 +391,6 @@ function love.init() love.createhandlers() end - -- Console hack - if c.console and love._openConsole then - love._openConsole() - end - -- Setup window here. if c.window and c.modules.window then assert(love.window.setMode(c.window.width, c.window.height, @@ -488,7 +491,7 @@ function love.run() -- Process events. if love.event then love.event.pump() - for n,a,b,c,d in love.event.poll() do + for n,a,b,c,d,e in love.event.poll() do if n == "quit" then if not love.quit or not love.quit() then if love.audio then @@ -497,7 +500,7 @@ function love.run() return end end - love.handlers[n](a,b,c,d) + love.handlers[n](a,b,c,d,e) end end diff --git a/jni/love/src/scripts/boot.lua.h b/jni/love/src/scripts/boot.lua.h index 750c259e..564bfa5b 100644 --- a/jni/love/src/scripts/boot.lua.h +++ b/jni/love/src/scripts/boot.lua.h @@ -319,6 +319,15 @@ const unsigned char boot_lua[] = 0x76, 0x65, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x28, 0x69, 0x64, 0x2c, 0x78, 0x2c, 0x79, 0x2c, 0x70, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a, + 0x09, 0x09, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x67, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x64, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x74, 0x68, 0x65, 0x74, + 0x61, 0x2c, 0x64, 0x69, 0x73, 0x74, 0x2c, 0x6e, 0x75, 0x6d, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x67, 0x65, + 0x73, 0x74, 0x75, 0x72, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x67, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, + 0x64, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x74, 0x68, 0x65, 0x74, 0x61, 0x2c, 0x64, 0x69, 0x73, 0x74, 0x2c, 0x6e, + 0x75, 0x6d, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x09, 0x09, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, @@ -625,6 +634,13 @@ const unsigned char boot_lua[] = 0x6e, 0x0a, 0x09, 0x09, 0x63, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x2d, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, + 0x65, 0x28, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x2d, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x73, 0x20, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, @@ -658,13 +674,6 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x28, 0x29, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, - 0x09, 0x2d, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x0a, - 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, - 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, - 0x74, 0x68, 0x65, 0x6e, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, - 0x65, 0x28, 0x29, 0x0a, - 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x2d, 0x2d, 0x20, 0x53, 0x65, 0x74, 0x75, 0x70, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, @@ -863,9 +872,9 @@ const unsigned char boot_lua[] = 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28, 0x29, 0x0a, - 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x2c, 0x61, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x20, 0x69, - 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x6f, 0x6c, 0x6c, 0x28, - 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x2c, 0x61, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c, 0x65, + 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x6f, 0x6c, + 0x6c, 0x28, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x71, 0x75, 0x69, 0x74, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x71, @@ -880,7 +889,7 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x5b, - 0x6e, 0x5d, 0x28, 0x61, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x29, 0x0a, + 0x6e, 0x5d, 0x28, 0x61, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c, 0x65, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x64, 0x74, 0x2c, 0x20, 0x61, 0x73, diff --git a/jni/love/src/scripts/graphics.lua b/jni/love/src/scripts/graphics.lua index faafd805..1cf91d50 100644 --- a/jni/love/src/scripts/graphics.lua +++ b/jni/love/src/scripts/graphics.lua @@ -1310,7 +1310,7 @@ do #define TransformProjectionMatrix gl_ModelViewProjectionMatrix #define NormalMatrix gl_NormalMatrix uniform sampler2D _tex0_; -uniform vec2 love_ScreenParams;]] +uniform vec4 love_ScreenSize;]] GLSLES.UNIFORMS = [[ uniform mat4 TransformMatrix; @@ -1319,7 +1319,7 @@ uniform mat4 TransformProjectionMatrix; // uniform mat4 NormalMatrix; uniform float love_PointSize; uniform sampler2D _tex0_; -uniform vec2 love_ScreenParams;]] +uniform vec4 love_ScreenSize;]] GLSL.VERTEX = { HEADER = [[ @@ -1390,7 +1390,7 @@ void main() { float dummy = texture2D(_tex0_, vec2(.5)).r; // See Shader::checkSetScreenParams in Shader.cpp. - vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenParams[0]) + love_ScreenParams[1]); + vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w); gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord); }]], @@ -1401,7 +1401,7 @@ void main() { float dummy = texture2D(_tex0_, vec2(.5)).r; // See Shader::checkSetScreenParams in Shader.cpp. - vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenParams[0]) + love_ScreenParams[1]); + vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w); effects(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord); }]], diff --git a/jni/love/src/scripts/graphics.lua.h b/jni/love/src/scripts/graphics.lua.h index f3f6b445..4aa254f5 100644 --- a/jni/love/src/scripts/graphics.lua.h +++ b/jni/love/src/scripts/graphics.lua.h @@ -6299,8 +6299,8 @@ const unsigned char graphics_lua[] = 0x69, 0x78, 0x20, 0x67, 0x6c, 0x5f, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x0a, - 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, - 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x5d, 0x5d, 0x0a, + 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, + 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x5d, 0x5d, 0x0a, 0x09, 0x47, 0x4c, 0x53, 0x4c, 0x45, 0x53, 0x2e, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x73, @@ -6316,8 +6316,8 @@ const unsigned char graphics_lua[] = 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x0a, - 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, - 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x5d, 0x5d, 0x0a, + 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, + 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x5d, 0x5d, 0x0a, 0x09, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x0a, @@ -6429,9 +6429,9 @@ const unsigned char graphics_lua[] = 0x09, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x2c, 0x20, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, - 0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x5b, 0x30, 0x5d, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, - 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5b, 0x31, 0x5d, 0x29, 0x3b, 0x0a, + 0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, + 0x65, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, + 0x53, 0x69, 0x7a, 0x65, 0x2e, 0x77, 0x29, 0x3b, 0x0a, 0x09, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, @@ -6454,9 +6454,9 @@ const unsigned char graphics_lua[] = 0x09, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x2c, 0x20, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, - 0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x5b, 0x30, 0x5d, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, - 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5b, 0x31, 0x5d, 0x29, 0x3b, 0x0a, + 0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, + 0x65, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, + 0x53, 0x69, 0x7a, 0x65, 0x2e, 0x77, 0x29, 0x3b, 0x0a, 0x09, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x28, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x70, 0x69, 0x78,