Browse Source

updated to latest mobile-common branch of Löve

fysx 12 years ago
parent
commit
33d979caf6
49 changed files with 824 additions and 199 deletions
  1. 3 3
      jni/love/src/common/version.h
  2. 1 1
      jni/love/src/modules/audio/Audio.h
  3. 1 1
      jni/love/src/modules/audio/Source.h
  4. 2 5
      jni/love/src/modules/audio/null/Audio.cpp
  5. 1 2
      jni/love/src/modules/audio/null/Audio.h
  6. 2 1
      jni/love/src/modules/audio/null/Source.cpp
  7. 1 1
      jni/love/src/modules/audio/null/Source.h
  8. 28 14
      jni/love/src/modules/audio/openal/Audio.cpp
  9. 1 2
      jni/love/src/modules/audio/openal/Audio.h
  10. 31 13
      jni/love/src/modules/audio/openal/Pool.cpp
  11. 8 3
      jni/love/src/modules/audio/openal/Pool.h
  12. 13 3
      jni/love/src/modules/audio/openal/Source.cpp
  13. 2 2
      jni/love/src/modules/audio/openal/Source.h
  14. 2 2
      jni/love/src/modules/audio/wrap_Audio.cpp
  15. 2 2
      jni/love/src/modules/audio/wrap_Source.cpp
  16. 8 0
      jni/love/src/modules/event/sdl/Event.cpp
  17. 1 1
      jni/love/src/modules/graphics/Drawable.h
  18. 20 0
      jni/love/src/modules/graphics/Graphics.cpp
  19. 15 0
      jni/love/src/modules/graphics/Graphics.h
  20. 1 1
      jni/love/src/modules/graphics/Texture.h
  21. 245 33
      jni/love/src/modules/graphics/opengl/Canvas.cpp
  22. 20 5
      jni/love/src/modules/graphics/opengl/Canvas.h
  23. 106 14
      jni/love/src/modules/graphics/opengl/Graphics.cpp
  24. 8 12
      jni/love/src/modules/graphics/opengl/Graphics.h
  25. 5 5
      jni/love/src/modules/graphics/opengl/Image.cpp
  26. 5 5
      jni/love/src/modules/graphics/opengl/Image.h
  27. 1 1
      jni/love/src/modules/graphics/opengl/Mesh.cpp
  28. 1 1
      jni/love/src/modules/graphics/opengl/Mesh.h
  29. 92 0
      jni/love/src/modules/graphics/opengl/OpenGL.cpp
  30. 16 0
      jni/love/src/modules/graphics/opengl/OpenGL.h
  31. 10 4
      jni/love/src/modules/graphics/opengl/ParticleSystem.cpp
  32. 13 4
      jni/love/src/modules/graphics/opengl/ParticleSystem.h
  33. 22 9
      jni/love/src/modules/graphics/opengl/Shader.cpp
  34. 4 1
      jni/love/src/modules/graphics/opengl/Shader.h
  35. 1 1
      jni/love/src/modules/graphics/opengl/SpriteBatch.cpp
  36. 1 1
      jni/love/src/modules/graphics/opengl/SpriteBatch.h
  37. 2 2
      jni/love/src/modules/graphics/opengl/Texture.h
  38. 8 0
      jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp
  39. 1 0
      jni/love/src/modules/graphics/opengl/wrap_Canvas.h
  40. 21 8
      jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp
  41. 1 0
      jni/love/src/modules/graphics/opengl/wrap_Graphics.h
  42. 12 2
      jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp
  43. 1 0
      jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h
  44. 12 0
      jni/love/src/modules/love/love.cpp
  45. 29 2
      jni/love/src/modules/window/sdl/Window.cpp
  46. 10 7
      jni/love/src/scripts/boot.lua
  47. 20 11
      jni/love/src/scripts/boot.lua.h
  48. 4 4
      jni/love/src/scripts/graphics.lua
  49. 10 10
      jni/love/src/scripts/graphics.lua.h

+ 3 - 3
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

+ 1 - 1
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.

+ 1 - 1
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;

+ 2 - 5
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 *)

+ 1 - 2
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);

+ 2 - 1
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()

+ 1 - 1
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();

+ 28 - 14
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)

+ 1 - 2
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);

+ 31 - 13
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
 		{

+ 8 - 3
jni/love/src/modules/audio/openal/Pool.h

@@ -36,6 +36,7 @@
 #ifdef LOVE_MACOSX_USE_FRAMEWORKS
 #include <OpenAL-Soft/alc.h>
 #include <OpenAL-Soft/al.h>
+#include <OpenAL-Soft/alext.h>
 #else
 #include <AL/alc.h>
 #include <AL/al.h>
@@ -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<ALuint> available;

+ 13 - 3
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()

+ 2 - 2
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();

+ 2 - 2
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)

+ 2 - 2
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)

+ 8 - 0
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:

+ 1 - 1
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

+ 20 - 0
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<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
 {
 	{ "line", Graphics::DRAW_LINE },
@@ -170,5 +180,15 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
 
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
 
+StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::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::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
+
 } // graphics
 } // love

+ 15 - 0
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<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
@@ -178,6 +190,9 @@ private:
 	static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
 	static StringMap<Support, SUPPORT_MAX_ENUM> support;
 
+	static StringMap<SystemLimit, LIMIT_MAX_ENUM>::Entry systemLimitEntries[];
+	static StringMap<SystemLimit, LIMIT_MAX_ENUM> systemLimits;
+
 }; // Graphics
 
 } // graphics

+ 1 - 1
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;

+ 245 - 33
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 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)
+	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<Canvas *> &canvases)
@@ -554,6 +714,9 @@ void Canvas::startGrab(const std::vector<Canvas *> &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<Canvas *> &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()

+ 20 - 5
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<Canvas *> 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<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
 	static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;

+ 106 - 14
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)

+ 8 - 12
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);

+ 5 - 5
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();
 

+ 5 - 5
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.

+ 1 - 1
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);

+ 1 - 1
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);

+ 92 - 0
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<std::string, Object *> &r = shader->getBoundRetainables();
+		for (auto it = r.begin(); it != r.end(); ++it)
+		{
+			// Even bigger hack! D:
+			Canvas *canvas = dynamic_cast<Canvas *>(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;
 

+ 16 - 0
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;
 

+ 10 - 4
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)

+ 13 - 4
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;

+ 22 - 9
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<std::string, Object *> &Shader::getBoundRetainables() const
+{
+	return boundRetainables;
 }
 
 std::string Shader::getGLSLVersion()
@@ -829,7 +842,7 @@ StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::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::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));

+ 4 - 1
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<std::string, Object *> &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;

+ 1 - 1
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);

+ 1 - 1
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);

+ 2 - 2
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

+ 8 - 0
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 }
 };
 

+ 1 - 0
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

+ 21 - 8
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 }
 };

+ 1 - 0
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);

+ 12 - 2
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 },

+ 1 - 0
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);

+ 12 - 0
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)

+ 29 - 2
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)

+ 10 - 7
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
 

+ 20 - 11
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, 

+ 4 - 4
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);
 }]],

+ 10 - 10
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,