Browse Source

Merge branch '12.0-development' into GraphicsBuffer

Alex Szpakowski 5 years ago
parent
commit
2c4bfab1de

+ 21 - 1
.github/workflows/main.yml

@@ -3,7 +3,7 @@ on: [push, pull_request]
 
 
 jobs:
 jobs:
   linux-os:
   linux-os:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-16.04
     steps:
     steps:
     - name: Update APT
     - name: Update APT
       run: sudo apt-get update
       run: sudo apt-get update
@@ -20,6 +20,26 @@ jobs:
       run: mkdir build && cd build && ../configure
       run: mkdir build && cd build && ../configure
     - name: Build
     - name: Build
       run: cd build && make -j2
       run: cd build && make -j2
+    - name: Prepare appimagetool
+      run: |
+        cd build &&
+        wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool &&
+        chmod +x appimagetool &&
+        sudo apt install -y appstream
+    - name: Clone love-appimages
+      uses: actions/checkout@v2
+      with:
+        path: build/love-appimages
+        repository: pfirsich/love-appimages
+    - name: Build AppImage
+      run: |
+        cd build &&
+        python3 love-appimages/build.py .. AppDir --builddir build --appimage love.AppImage
+    - name: Artifact
+      uses: actions/upload-artifact@v2-preview
+      with:
+        name: love.AppImage
+        path: build/love.AppImage
   windows-os:
   windows-os:
     runs-on: windows-latest
     runs-on: windows-latest
     strategy:
     strategy:

+ 0 - 2
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -1873,7 +1873,6 @@
 		FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Texture.cpp; sourceTree = "<group>"; };
 		FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Texture.cpp; sourceTree = "<group>"; };
 		FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = "<group>"; };
 		FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = "<group>"; };
 		FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = "<group>"; };
 		FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = "<group>"; };
-		FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_GraphicsShader.lua; sourceTree = "<group>"; };
 		FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = "<group>"; };
 		FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = "<group>"; };
 		FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = "<group>"; };
 		FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = "<group>"; };
 		FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = "<group>"; };
 		FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = "<group>"; };
@@ -2862,7 +2861,6 @@
 				FADF54391E3DAFF700012CC0 /* wrap_Graphics.cpp */,
 				FADF54391E3DAFF700012CC0 /* wrap_Graphics.cpp */,
 				FADF543A1E3DAFF700012CC0 /* wrap_Graphics.h */,
 				FADF543A1E3DAFF700012CC0 /* wrap_Graphics.h */,
 				FADF54371E3DAFBA00012CC0 /* wrap_Graphics.lua */,
 				FADF54371E3DAFBA00012CC0 /* wrap_Graphics.lua */,
-				FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */,
 				FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */,
 				FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */,
 				FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */,
 				FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */,
 				FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */,
 				FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */,

+ 73 - 36
src/modules/graphics/Graphics.cpp

@@ -104,8 +104,6 @@ bool isDebugEnabled()
 
 
 love::Type Graphics::type("graphics", &Module::type);
 love::Type Graphics::type("graphics", &Module::type);
 
 
-Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
-
 Graphics::Graphics()
 Graphics::Graphics()
 	: width(0)
 	: width(0)
 	, height(0)
 	, height(0)
@@ -216,13 +214,8 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
 	return new ParticleSystem(texture, size);
 	return new ParticleSystem(texture, size);
 }
 }
 
 
-ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource)
+ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info)
 {
 {
-	if (stage == ShaderStage::STAGE_MAX_ENUM)
-		throw love::Exception("Invalid shader stage.");
-
-	const std::string &source = optsource.empty() ? getCurrentDefaultShaderCode().source[stage] : optsource;
-
 	ShaderStage *s = nullptr;
 	ShaderStage *s = nullptr;
 	std::string cachekey;
 	std::string cachekey;
 
 
@@ -243,7 +236,8 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
 
 
 	if (s == nullptr)
 	if (s == nullptr)
 	{
 	{
-		s = newShaderStageInternal(stage, cachekey, source, getRenderer() == RENDERER_OPENGLES);
+		std::string glsl = Shader::createShaderStageCode(this, stage, source, info);
+		s = newShaderStageInternal(stage, cachekey, glsl, getRenderer() == RENDERER_OPENGLES);
 		if (!cachekey.empty())
 		if (!cachekey.empty())
 			cachedShaderStages[stage][cachekey] = s;
 			cachedShaderStages[stage][cachekey] = s;
 	}
 	}
@@ -251,15 +245,48 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
 	return s;
 	return s;
 }
 }
 
 
-Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
+Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
 {
 {
-	if (vertex.empty() && pixel.empty())
-		throw love::Exception("Error creating shader: no source code!");
+	StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
+
+	bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
+	validstages[ShaderStage::STAGE_VERTEX] = true;
+	validstages[ShaderStage::STAGE_PIXEL] = true;
+
+	for (const std::string &source : stagessource)
+	{
+		Shader::SourceInfo info = Shader::getSourceInfo(source);
+		bool isanystage = false;
+
+		for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
+		{
+			if (!validstages[i])
+				continue;
+
+			if (info.isStage[i])
+			{
+				isanystage = true;
+				stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
+			}
+		}
+
+		if (!isanystage)
+			throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)");
+	}
+
+	for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
+	{
+		auto stype = (ShaderStage::StageType) i;
+		if (validstages[i] && stages[i].get() == nullptr)
+		{
+			const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype);
+			Shader::SourceInfo info = Shader::getSourceInfo(source);
+			stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN);
+		}
 
 
-	StrongRef<ShaderStage> vertexstage(newShaderStage(ShaderStage::STAGE_VERTEX, vertex), Acquire::NORETAIN);
-	StrongRef<ShaderStage> pixelstage(newShaderStage(ShaderStage::STAGE_PIXEL, pixel), Acquire::NORETAIN);
+	}
 
 
-	return newShaderInternal(vertexstage.get(), pixelstage.get());
+	return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]);
 }
 }
 
 
 Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength)
 Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength)
@@ -293,26 +320,44 @@ void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::
 	cachedShaderStages[type].erase(hashkey);
 	cachedShaderStages[type].erase(hashkey);
 }
 }
 
 
-bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err)
+bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
 {
 {
-	if (vertex.empty() && pixel.empty())
-	{
-		err = "Error validating shader: no source code!";
-		return false;
-	}
+	StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
 
 
-	StrongRef<ShaderStage> vertexstage;
-	StrongRef<ShaderStage> pixelstage;
+	bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
+	validstages[ShaderStage::STAGE_VERTEX] = true;
+	validstages[ShaderStage::STAGE_PIXEL] = true;
 
 
 	// Don't use cached shader stages, since the gles flag may not match the
 	// Don't use cached shader stages, since the gles flag may not match the
 	// current renderer.
 	// current renderer.
-	if (!vertex.empty())
-		vertexstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_VERTEX, vertex, gles), Acquire::NORETAIN);
+	for (const std::string &source : stagessource)
+	{
+		Shader::SourceInfo info = Shader::getSourceInfo(source);
+		bool isanystage = false;
+
+		for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
+		{
+			auto stype = (ShaderStage::StageType) i;
+
+			if (!validstages[i])
+				continue;
+
+			if (info.isStage[i])
+			{
+				isanystage = true;
+				std::string glsl = Shader::createShaderStageCode(this, stype, source, info);
+				stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN);
+			}
+		}
 
 
-	if (!pixel.empty())
-		pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN);
+		if (!isanystage)
+		{
+			err = "Could not parse shader code (missing 'position' or 'effect' function?)";
+			return false;
+		}
+	}
 
 
-	return Shader::validate(vertexstage.get(), pixelstage.get(), err);
+	return Shader::validate(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL], err);
 }
 }
 
 
 int Graphics::getWidth() const
 int Graphics::getWidth() const
@@ -1742,14 +1787,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
 	return p;
 	return p;
 }
 }
 
 
-const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const
-{
-	int languageindex = (int) getShaderLanguageTarget();
-	int gammaindex = isGammaCorrect() ? 1 : 0;
-
-	return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex];
-}
-
 /**
 /**
  * Constants.
  * Constants.
  **/
  **/

+ 3 - 14
src/modules/graphics/Graphics.h

@@ -415,11 +415,6 @@ public:
 		}
 		}
 	};
 	};
 
 
-	struct DefaultShaderCode
-	{
-		std::string source[ShaderStage::STAGE_MAX_ENUM];
-	};
-
 	Graphics();
 	Graphics();
 	virtual ~Graphics();
 	virtual ~Graphics();
 
 
@@ -436,8 +431,7 @@ public:
 	SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage);
 	SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage);
 	ParticleSystem *newParticleSystem(Texture *texture, int size);
 	ParticleSystem *newParticleSystem(Texture *texture, int size);
 
 
-	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
-	Shader *newShader(const std::string &vertex, const std::string &pixel);
+	Shader *newShader(const std::vector<std::string> &stagessource);
 
 
 	virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
 	virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
 	virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
 	virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
@@ -448,7 +442,7 @@ public:
 
 
 	Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
 	Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
 
 
-	bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err);
+	bool validateShader(bool gles, const std::vector<std::string> &stages, std::string &err);
 
 
 	/**
 	/**
 	 * Resets the current color, background color, line style, and so forth.
 	 * Resets the current color, background color, line style, and so forth.
@@ -827,9 +821,6 @@ public:
 
 
 	static void flushBatchedDrawsGlobal();
 	static void flushBatchedDrawsGlobal();
 
 
-	virtual Shader::Language getShaderLanguageTarget() const = 0;
-	const DefaultShaderCode &getCurrentDefaultShaderCode() const;
-
 	void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
 	void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
 
 
 	template <typename T>
 	template <typename T>
@@ -869,9 +860,6 @@ public:
 	static bool getConstant(StackType in, const char *&out);
 	static bool getConstant(StackType in, const char *&out);
 	static std::vector<std::string> getConstants(StackType);
 	static std::vector<std::string> getConstants(StackType);
 
 
-	// Default shader code (a shader is always required internally.)
-	static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
-
 protected:
 protected:
 
 
 	struct DisplayState
 	struct DisplayState
@@ -945,6 +933,7 @@ protected:
 		{}
 		{}
 	};
 	};
 
 
+	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info);
 	virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
 	virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
 	virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
 	virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
 	virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
 	virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;

+ 511 - 22
src/modules/graphics/Shader.cpp

@@ -28,12 +28,402 @@
 
 
 // C++
 // C++
 #include <string>
 #include <string>
+#include <regex>
+#include <sstream>
 
 
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
 {
 {
 
 
+namespace glsl
+{
+static const char global_syntax[] = R"(
+#if !defined(GL_ES) && __VERSION__ < 140
+	#define lowp
+	#define mediump
+	#define highp
+#endif
+#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
+	#define LOVE_HIGHP_OR_MEDIUMP highp
+#else
+	#define LOVE_HIGHP_OR_MEDIUMP mediump
+#endif
+#define number float
+#define Image sampler2D
+#define ArrayImage sampler2DArray
+#define CubeImage samplerCube
+#define VolumeImage sampler3D
+#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
+	#define DepthImage sampler2DShadow
+	#define DepthArrayImage sampler2DArrayShadow
+	#define DepthCubeImage samplerCubeShadow
+#endif
+#define extern uniform
+#ifdef GL_EXT_texture_array
+#extension GL_EXT_texture_array : enable
+#endif
+#ifdef GL_OES_texture_3D
+#extension GL_OES_texture_3D : enable
+#endif
+#ifdef GL_OES_standard_derivatives
+#extension GL_OES_standard_derivatives : enable
+#endif
+)";
+
+static const char global_uniforms[] = R"(
+// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
+// but we can't guarantee that highp is always supported in fragment shaders...
+// We *really* don't want to use mediump for these in vertex shaders though.
+uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
+
+// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
+// do it as an initializer.
+LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
+LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
+LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
+
+LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
+LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
+
+#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
+
+// Alternate names
+#define ViewSpaceFromLocal TransformMatrix
+#define ClipSpaceFromView ProjectionMatrix
+#define ClipSpaceFromLocal TransformProjectionMatrix
+#define ViewNormalFromLocal NormalMatrix
+
+void love_initializeBuiltinUniforms() {
+	TransformMatrix = mat4(
+	   love_UniformsPerDraw[0],
+	   love_UniformsPerDraw[1],
+	   love_UniformsPerDraw[2],
+	   love_UniformsPerDraw[3]
+	);
+
+	ProjectionMatrix = mat4(
+	   love_UniformsPerDraw[4],
+	   love_UniformsPerDraw[5],
+	   love_UniformsPerDraw[6],
+	   love_UniformsPerDraw[7]
+	);
+
+	NormalMatrix = mat3(
+	   love_UniformsPerDraw[8].xyz,
+	   love_UniformsPerDraw[9].xyz,
+	   love_UniformsPerDraw[10].xyz
+	);
+
+	love_ScreenSize = love_UniformsPerDraw[11];
+	ConstantColor = love_UniformsPerDraw[12];
+}
+)";
+
+static const char global_functions[] = R"(
+#ifdef GL_ES
+	#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
+		precision lowp sampler2DArray;
+	#endif
+	#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
+		precision lowp sampler3D;
+	#endif
+	#if __VERSION__ >= 300
+		precision lowp sampler2DShadow;
+		precision lowp samplerCubeShadow;
+		precision lowp sampler2DArrayShadow;
+	#endif
+#endif
+
+#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
+	#define Texel texture
+#else
+	#if __VERSION__ >= 130
+		#define texture2D Texel
+		#define texture3D Texel
+		#define textureCube Texel
+		#define texture2DArray Texel
+		#define love_texture2D texture
+		#define love_texture3D texture
+		#define love_textureCube texture
+		#define love_texture2DArray texture
+	#else
+		#define love_texture2D texture2D
+		#define love_texture3D texture3D
+		#define love_textureCube textureCube
+		#define love_texture2DArray texture2DArray
+	#endif
+	vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
+	vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); }
+	#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
+		vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
+	#endif
+	#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
+		vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
+	#endif
+	#ifdef PIXEL
+		vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
+		vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); }
+		#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
+			vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
+		#endif
+		#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
+			vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
+		#endif
+	#endif
+	#define texture love_texture
+#endif
+
+float gammaToLinearPrecise(float c) {
+	return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
+}
+vec3 gammaToLinearPrecise(vec3 c) {
+	bvec3 leq = lessThanEqual(c, vec3(0.04045));
+	c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
+	c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
+	c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
+	return c;
+}
+vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
+float linearToGammaPrecise(float c) {
+	return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+}
+vec3 linearToGammaPrecise(vec3 c) {
+	bvec3 lt = lessThanEqual(c, vec3(0.0031308));
+	c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
+	c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
+	c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
+	return c;
+}
+vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
+
+// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
+
+mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
+mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
+mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
+
+mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
+mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
+mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
+
+#define gammaToLinear gammaToLinearFast
+#define linearToGamma linearToGammaFast
+
+#ifdef LOVE_GAMMA_CORRECT
+	#define gammaCorrectColor gammaToLinear
+	#define unGammaCorrectColor linearToGamma
+	#define gammaCorrectColorPrecise gammaToLinearPrecise
+	#define unGammaCorrectColorPrecise linearToGammaPrecise
+	#define gammaCorrectColorFast gammaToLinearFast
+	#define unGammaCorrectColorFast linearToGammaFast
+#else
+	#define gammaCorrectColor
+	#define unGammaCorrectColor
+	#define gammaCorrectColorPrecise
+	#define unGammaCorrectColorPrecise
+	#define gammaCorrectColorFast
+	#define unGammaCorrectColorFast
+#endif
+)";
+
+static const char vertex_header[] = R"(
+#define love_Position gl_Position
+
+#if __VERSION__ >= 130
+	#define attribute in
+	#define varying out
+	#ifndef LOVE_GLSL1_ON_GLSL3
+		#define love_VertexID gl_VertexID
+		#define love_InstanceID gl_InstanceID
+	#endif
+#endif
+
+#ifdef GL_ES
+	uniform mediump float love_PointSize;
+#endif
+)";
+
+static const char vertex_functions[] = R"(
+void setPointSize() {
+#ifdef GL_ES
+	gl_PointSize = love_PointSize;
+#endif
+}
+)";
+
+static const char vertex_main[] = R"(
+attribute vec4 VertexPosition;
+attribute vec4 VertexTexCoord;
+attribute vec4 VertexColor;
+
+varying vec4 VaryingTexCoord;
+varying vec4 VaryingColor;
+
+vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
+
+void main() {
+	love_initializeBuiltinUniforms();
+	VaryingTexCoord = VertexTexCoord;
+	VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
+	setPointSize();
+	love_Position = position(ClipSpaceFromLocal, VertexPosition);
+}
+)";
+
+static const char pixel_header[] = R"(
+#ifdef GL_ES
+	precision mediump float;
+#endif
+
+#define love_MaxRenderTargets gl_MaxDrawBuffers
+
+#if __VERSION__ >= 130
+	#define varying in
+	// Some drivers seem to make the pixel shader do more work when multiple
+	// pixel shader outputs are defined, even when only one is actually used.
+	// TODO: We should use reflection or something instead of this, to determine
+	// how many outputs are actually used in the shader code.
+	#ifdef LOVE_MULTI_RENDER_TARGETS
+		layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets];
+		#define love_PixelColor love_RenderTargets[0]
+	#else
+		layout(location = 0) out vec4 love_PixelColor;
+	#endif
+#else
+	#ifdef LOVE_MULTI_RENDER_TARGETS
+		#define love_RenderTargets gl_FragData
+	#endif
+	#define love_PixelColor gl_FragColor
+#endif
+
+// Legacy
+#define love_MaxCanvases love_MaxRenderTargets
+#define love_Canvases love_RenderTargets
+#ifdef LOVE_MULTI_RENDER_TARGETS
+#define LOVE_MULTI_CANVASES 1
+#endif
+
+// See Shader::updateScreenParams in Shader.cpp.
+#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))
+)";
+
+static const char pixel_functions[] = R"(
+uniform sampler2D love_VideoYChannel;
+uniform sampler2D love_VideoCbChannel;
+uniform sampler2D love_VideoCrChannel;
+
+vec4 VideoTexel(vec2 texcoords) {
+	vec3 yuv;
+	yuv[0] = Texel(love_VideoYChannel, texcoords).r;
+	yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
+	yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
+	yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
+
+	vec4 color;
+	color.r = dot(yuv, vec3(1.164,  0.000,  1.596));
+	color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
+	color.b = dot(yuv, vec3(1.164,  2.018,  0.000));
+	color.a = 1.0;
+
+	return gammaCorrectColor(color);
+}
+)";
+
+static const char pixel_main[] = R"(
+uniform sampler2D MainTex;
+varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
+varying mediump vec4 VaryingColor;
+
+vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
+
+void main() {
+	love_initializeBuiltinUniforms();
+	love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
+}
+)";
+
+static const char pixel_main_custom[] = R"(
+varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
+varying mediump vec4 VaryingColor;
+
+void effect();
+
+void main() {
+	love_initializeBuiltinUniforms();
+	effect();
+}
+)";
+
+struct StageInfo
+{
+	const char *name;
+	const char *header;
+	const char *functions;
+	const char *main;
+	const char *main_custom;
+};
+
+static const StageInfo stageInfo[] =
+{
+	{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main },
+	{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom },
+};
+
+static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
+
+struct Version
+{
+	std::string glsl;
+	std::string glsles;
+};
+
+// Indexed by Shader::Version
+static const Version versions[] =
+{
+	{ "#version 120", "#version 100" },
+	{ "#version 330 core", "#version 300 es" },
+	{ "#version 430 core", "#version 310 es" },
+};
+
+static Shader::Language getTargetLanguage(const std::string &src)
+{
+	std::regex r("^\\s*#pragma language (\\w+)");
+	std::smatch m;
+	std::string langstr = std::regex_search(src, m, r) ? m[0] : std::string("glsl1");
+	Shader::Language lang = Shader::LANGUAGE_MAX_ENUM;
+	Shader::getConstant(langstr.c_str(), lang);
+	return lang;
+}
+
+static bool isVertexCode(const std::string &src)
+{
+	std::regex r("vec4\\s+position\\s*\\(");
+	std::smatch m;
+	return std::regex_search(src, m, r);
+}
+
+static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt)
+{
+	custompixel = false;
+	mrt = false;
+	std::smatch m;
+	if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\(")))
+		return true;
+
+	if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\(")))
+	{
+		custompixel = true;
+		if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos)
+			mrt = true;
+		return true;
+	}
+
+	return false;
+}
+
+} // glsl
+
 static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
 static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
 
 
 love::Type Shader::type("Shader", &Object::type);
 love::Type Shader::type("Shader", &Object::type);
@@ -41,6 +431,59 @@ love::Type Shader::type("Shader", &Object::type);
 Shader *Shader::current = nullptr;
 Shader *Shader::current = nullptr;
 Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
 Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
 
 
+Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
+{
+	SourceInfo info = {};
+	info.language = glsl::getTargetLanguage(src);
+	info.isStage[ShaderStage::STAGE_VERTEX] = glsl::isVertexCode(src);
+	info.isStage[ShaderStage::STAGE_PIXEL] = glsl::isPixelCode(src, info.customPixelFunction, info.usesMRT);
+	return info;
+}
+
+std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const Shader::SourceInfo &info)
+{
+	if (info.language == Shader::LANGUAGE_MAX_ENUM)
+		throw love::Exception("Invalid shader language");
+
+	const auto &features = gfx->getCapabilities().features;
+
+	if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
+		throw love::Exception("GLSL 3 shaders are not supported on this system.");
+
+	if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4])
+		throw love::Exception("GLSL 4 shaders are not supported on this system.");
+
+	bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES;
+	bool glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3];
+
+	Language lang = info.language;
+	if (glsl1on3)
+		lang = LANGUAGE_GLSL3;
+
+	glsl::StageInfo stageinfo = glsl::stageInfo[stage];
+
+	std::stringstream ss;
+
+	ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n";
+	ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
+	if (glsl1on3)
+		ss << "#define LOVE_GLSL1_ON_GLSL3 1\n";
+	if (isGammaCorrect())
+		ss << "#define LOVE_GAMMA_CORRECT 1\n";
+	if (info.usesMRT)
+		ss << "#define LOVE_MULTI_RENDER_TARGETS 1";
+	ss << glsl::global_syntax;
+	ss << stageinfo.header;
+	ss << glsl::global_uniforms;
+	ss << glsl::global_functions;
+	ss << stageinfo.functions;
+	ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main);
+	ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n");
+	ss << code;
+
+	return ss.str();
+}
+
 Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
 Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
 	: stages()
 	: stages()
 {
 {
@@ -157,47 +600,93 @@ void Shader::deinitialize()
 	glslang::FinalizeProcess();
 	glslang::FinalizeProcess();
 }
 }
 
 
-bool Shader::getConstant(const char *in, Language &out)
+static const std::string defaultVertex = R"(
+vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition)
 {
 {
-	return languages.find(in, out);
+	return clipSpaceFromLocal * localPosition;
 }
 }
+)";
 
 
-bool Shader::getConstant(Language in, const char *&out)
+static const std::string defaultStandardPixel = R"(
+vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord)
 {
 {
-	return languages.find(in, out);
+	return Texel(tex, texcoord) * vcolor;
 }
 }
+)";
 
 
-bool Shader::getConstant(const char *in, BuiltinUniform &out)
+static const std::string defaultVideoPixel = R"(
+void effect()
 {
 {
-	return builtinNames.find(in, out);
+	love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
 }
 }
+)";
 
 
-bool Shader::getConstant(BuiltinUniform in, const char *&out)
+static const std::string defaultArrayPixel = R"(
+uniform ArrayImage MainTex;
+void effect()
 {
 {
-	return builtinNames.find(in, out);
+	love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
+}
+)";
+
+const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStage::StageType stage)
+{
+	if (stage == ShaderStage::STAGE_VERTEX)
+		return defaultVertex;
+
+	static std::string nocode = "";
+
+	switch (shader)
+	{
+		case STANDARD_DEFAULT: return defaultStandardPixel;
+		case STANDARD_VIDEO: return defaultVideoPixel;
+		case STANDARD_ARRAY: return defaultArrayPixel;
+		case STANDARD_MAX_ENUM: return nocode;
+	}
+
+	return nocode;
 }
 }
 
 
-StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEntries[] =
+static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry languageEntries[] =
 {
 {
-	{ "glsl1", LANGUAGE_GLSL1 },
-	{ "essl1", LANGUAGE_ESSL1 },
-	{ "glsl3", LANGUAGE_GLSL3 },
-	{ "essl3", LANGUAGE_ESSL3 },
+	{ "glsl1", Shader::LANGUAGE_GLSL1 },
+	{ "glsl3", Shader::LANGUAGE_GLSL3 },
+	{ "glsl4", Shader::LANGUAGE_GLSL4 },
 };
 };
 
 
-StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries));
+static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> languages(languageEntries, sizeof(languageEntries));
 
 
-StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
+static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry builtinNameEntries[] =
 {
 {
-	{ "MainTex",              BUILTIN_TEXTURE_MAIN      },
-	{ "love_VideoYChannel",   BUILTIN_TEXTURE_VIDEO_Y   },
-	{ "love_VideoCbChannel",  BUILTIN_TEXTURE_VIDEO_CB  },
-	{ "love_VideoCrChannel",  BUILTIN_TEXTURE_VIDEO_CR  },
-	{ "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW },
-	{ "love_PointSize",       BUILTIN_POINT_SIZE        },
+	{ "MainTex",              Shader::BUILTIN_TEXTURE_MAIN      },
+	{ "love_VideoYChannel",   Shader::BUILTIN_TEXTURE_VIDEO_Y   },
+	{ "love_VideoCbChannel",  Shader::BUILTIN_TEXTURE_VIDEO_CB  },
+	{ "love_VideoCrChannel",  Shader::BUILTIN_TEXTURE_VIDEO_CR  },
+	{ "love_UniformsPerDraw", Shader::BUILTIN_UNIFORMS_PER_DRAW },
+	{ "love_PointSize",       Shader::BUILTIN_POINT_SIZE        },
 };
 };
 
 
-StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
+static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> builtinNames(builtinNameEntries, sizeof(builtinNameEntries));
+
+bool Shader::getConstant(const char *in, Language &out)
+{
+	return languages.find(in, out);
+}
+
+bool Shader::getConstant(Language in, const char *&out)
+{
+	return languages.find(in, out);
+}
+
+bool Shader::getConstant(const char *in, BuiltinUniform &out)
+{
+	return builtinNames.find(in, out);
+}
+
+bool Shader::getConstant(BuiltinUniform in, const char *&out)
+{
+	return builtinNames.find(in, out);
+}
 
 
 } // graphics
 } // graphics
 } // love
 } // love

+ 14 - 11
src/modules/graphics/Shader.h

@@ -50,9 +50,8 @@ public:
 	enum Language
 	enum Language
 	{
 	{
 		LANGUAGE_GLSL1,
 		LANGUAGE_GLSL1,
-		LANGUAGE_ESSL1,
 		LANGUAGE_GLSL3,
 		LANGUAGE_GLSL3,
-		LANGUAGE_ESSL3,
+		LANGUAGE_GLSL4,
 		LANGUAGE_MAX_ENUM
 		LANGUAGE_MAX_ENUM
 	};
 	};
 
 
@@ -89,6 +88,14 @@ public:
 		STANDARD_MAX_ENUM
 		STANDARD_MAX_ENUM
 	};
 	};
 
 
+	struct SourceInfo
+	{
+		Language language;
+		bool isStage[ShaderStage::STAGE_MAX_ENUM];
+		bool customPixelFunction;
+		bool usesMRT;
+	};
+
 	struct MatrixSize
 	struct MatrixSize
 	{
 	{
 		short columns;
 		short columns;
@@ -187,11 +194,16 @@ public:
 	void checkMainTextureType(TextureType textype, bool isDepthSampler) const;
 	void checkMainTextureType(TextureType textype, bool isDepthSampler) const;
 	void checkMainTexture(Texture *texture) const;
 	void checkMainTexture(Texture *texture) const;
 
 
+	static SourceInfo getSourceInfo(const std::string &src);
+	static std::string createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const SourceInfo &info);
+
 	static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
 	static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
 
 
 	static bool initialize();
 	static bool initialize();
 	static void deinitialize();
 	static void deinitialize();
 
 
+	static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage);
+
 	static bool getConstant(const char *in, Language &out);
 	static bool getConstant(const char *in, Language &out);
 	static bool getConstant(Language in, const char *&out);
 	static bool getConstant(Language in, const char *&out);
 
 
@@ -202,15 +214,6 @@ protected:
 
 
 	StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
 	StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
 
 
-private:
-
-	static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];
-	static StringMap<Language, LANGUAGE_MAX_ENUM> languages;
-	
-	// Names for the built-in uniform variables.
-	static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
-	static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM> builtinNames;
-
 }; // Shader
 }; // Shader
 
 
 } // graphics
 } // graphics

+ 7 - 0
src/modules/graphics/ShaderStage.cpp

@@ -202,6 +202,13 @@ bool ShaderStage::getConstant(StageType in, const char *&out)
 	return stageNames.find(in, out);
 	return stageNames.find(in, out);
 }
 }
 
 
+const char *ShaderStage::getConstant(StageType in)
+{
+	const char *name = nullptr;
+	getConstant(in, name);
+	return name;
+}
+
 StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
 StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
 {
 {
 	{ "vertex", STAGE_VERTEX },
 	{ "vertex", STAGE_VERTEX },

+ 2 - 0
src/modules/graphics/ShaderStage.h

@@ -44,6 +44,7 @@ class ShaderStage : public love::Object, public Volatile, public Resource
 {
 {
 public:
 public:
 
 
+	// Order is used for stages array in ShaderStage.cpp
 	enum StageType
 	enum StageType
 	{
 	{
 		STAGE_VERTEX,
 		STAGE_VERTEX,
@@ -61,6 +62,7 @@ public:
 
 
 	static bool getConstant(const char *in, StageType &out);
 	static bool getConstant(const char *in, StageType &out);
 	static bool getConstant(StageType in, const char *&out);
 	static bool getConstant(StageType in, const char *&out);
+	static const char *getConstant(StageType in);
 
 
 protected:
 protected:
 
 

+ 2 - 0
src/modules/graphics/Texture.cpp

@@ -194,6 +194,8 @@ Texture::Texture(const Settings &settings, const Slices *slices)
 		love::image::ImageDataBase *slice = slices->get(0, 0);
 		love::image::ImageDataBase *slice = slices->get(0, 0);
 
 
 		format = slice->getFormat();
 		format = slice->getFormat();
+		if (sRGB)
+			format = getSRGBPixelFormat(format);
 
 
 		pixelWidth = slice->getWidth();
 		pixelWidth = slice->getWidth();
 		pixelHeight = slice->getHeight();
 		pixelHeight = slice->getHeight();

+ 6 - 17
src/modules/graphics/opengl/Graphics.cpp

@@ -252,12 +252,11 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 	// Restore the graphics state.
 	// Restore the graphics state.
 	restoreState(states.back());
 	restoreState(states.back());
 
 
-	int gammacorrect = isGammaCorrect() ? 1 : 0;
-	Shader::Language target = getShaderLanguageTarget();
-
 	// We always need a default shader.
 	// We always need a default shader.
 	for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
 	for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
 	{
 	{
+		auto stype = (Shader::StandardShader) i;
+
 		if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
 		if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
 			continue;
 			continue;
 
 
@@ -267,8 +266,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 		{
 		{
 			if (!Shader::standardShaders[i])
 			if (!Shader::standardShaders[i])
 			{
 			{
-				const auto &code = defaultShaderCode[i][target][gammacorrect];
-				Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
+				std::vector<std::string> stages;
+				stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX));
+				stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL));
+				Shader::standardShaders[i] = newShader(stages);
 			}
 			}
 		}
 		}
 		catch (love::Exception &)
 		catch (love::Exception &)
@@ -1501,18 +1502,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
 	return supported.value;
 	return supported.value;
 }
 }
 
 
-Shader::Language Graphics::getShaderLanguageTarget() const
-{
-	if (gl.isCoreProfile())
-		return Shader::LANGUAGE_GLSL3;
-	else if (GLAD_ES_VERSION_3_0)
-		return Shader::LANGUAGE_ESSL3;
-	else if (GLAD_ES_VERSION_2_0)
-		return Shader::LANGUAGE_ESSL1;
-	else
-		return Shader::LANGUAGE_GLSL1;
-}
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 0 - 2
src/modules/graphics/opengl/Graphics.h

@@ -106,8 +106,6 @@ public:
 	Renderer getRenderer() const override;
 	Renderer getRenderer() const override;
 	RendererInfo getRendererInfo() const override;
 	RendererInfo getRendererInfo() const override;
 
 
-	Shader::Language getShaderLanguageTarget() const override;
-
 	// Internal use.
 	// Internal use.
 	void cleanupRenderTexture(love::graphics::Texture *texture);
 	void cleanupRenderTexture(love::graphics::Texture *texture);
 
 

+ 8 - 3
src/modules/graphics/opengl/OpenGL.cpp

@@ -1061,7 +1061,7 @@ void OpenGL::setTextureUnit(int textureunit)
 	state.curTextureUnit = textureunit;
 	state.curTextureUnit = textureunit;
 }
 }
 
 
-void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev)
+void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
 {
 {
 	if (texture != state.boundTextures[target][textureunit])
 	if (texture != state.boundTextures[target][textureunit])
 	{
 	{
@@ -1077,9 +1077,14 @@ void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureun
 		else
 		else
 			state.curTextureUnit = textureunit;
 			state.curTextureUnit = textureunit;
 	}
 	}
+	else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit)
+	{
+		glActiveTexture(GL_TEXTURE0 + textureunit);
+		state.curTextureUnit = textureunit;
+	}
 }
 }
 
 
-void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
+void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
 {
 {
 	TextureType textype = TEXTURE_2D;
 	TextureType textype = TEXTURE_2D;
 	GLuint handle = 0;
 	GLuint handle = 0;
@@ -1101,7 +1106,7 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
 		handle = getDefaultTexture(textype);
 		handle = getDefaultTexture(textype);
 	}
 	}
 
 
-	bindTextureToUnit(textype, handle, textureunit, restoreprev);
+	bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
 }
 }
 
 
 void OpenGL::deleteTexture(GLuint texture)
 void OpenGL::deleteTexture(GLuint texture)

+ 3 - 2
src/modules/graphics/opengl/OpenGL.h

@@ -318,9 +318,10 @@ public:
 	 *
 	 *
 	 * @param textureunit Index in the range of [0, maxtextureunits-1]
 	 * @param textureunit Index in the range of [0, maxtextureunits-1]
 	 * @param restoreprev Restore previously bound texture unit when done.
 	 * @param restoreprev Restore previously bound texture unit when done.
+	 * @param bindforedit If false, the active texture unit may be left alone.
 	 **/
 	 **/
-	void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev);
-	void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
+	void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit = true);
+	void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit = true);
 
 
 	/**
 	/**
 	 * Helper for deleting an OpenGL texture.
 	 * Helper for deleting an OpenGL texture.

+ 2 - 2
src/modules/graphics/opengl/Shader.cpp

@@ -440,7 +440,7 @@ void Shader::attach()
 		{
 		{
 			const TextureUnit &unit = textureUnits[i];
 			const TextureUnit &unit = textureUnits[i];
 			if (unit.active)
 			if (unit.active)
-				gl.bindTextureToUnit(unit.type, unit.texture, i, false);
+				gl.bindTextureToUnit(unit.type, unit.texture, i, false, false);
 		}
 		}
 
 
 		// send any pending uniforms to the shader program.
 		// send any pending uniforms to the shader program.
@@ -637,7 +637,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 		int texunit = info->ints[i];
 		int texunit = info->ints[i];
 
 
 		if (shaderactive)
 		if (shaderactive)
-			gl.bindTextureToUnit(info->textureType, gltex, texunit, false);
+			gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
 
 
 		// Store texture id so it can be re-bound to the texture unit later.
 		// Store texture id so it can be re-bound to the texture unit later.
 		textureUnits[texunit].texture = gltex;
 		textureUnits[texunit].texture = gltex;

+ 12 - 101
src/modules/graphics/wrap_Graphics.cpp

@@ -45,11 +45,6 @@ static const char graphics_lua[] =
 #include "wrap_Graphics.lua"
 #include "wrap_Graphics.lua"
 ;
 ;
 
 
-// This is in a separate file because VS2013 has a 16KB limit for raw strings..
-static const char graphics_shader_lua[] =
-#include "wrap_GraphicsShader.lua"
-;
-
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
@@ -748,15 +743,15 @@ static int w__pushNewTexture(lua_State *L, Texture::Slices *slices, const Textur
 static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool checkType, bool checkDimensions, OptionalBool forceRenderTarget, Texture::Settings &s, bool &setdpiscale)
 static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool checkType, bool checkDimensions, OptionalBool forceRenderTarget, Texture::Settings &s, bool &setdpiscale)
 {
 {
 	setdpiscale = false;
 	setdpiscale = false;
+	if (forceRenderTarget.hasValue)
+		s.renderTarget = forceRenderTarget.value;
 
 
 	if (opt && lua_isnoneornil(L, idx))
 	if (opt && lua_isnoneornil(L, idx))
 		return;
 		return;
 
 
 	luax_checktablefields<Texture::SettingType>(L, idx, "texture setting name", Texture::getConstant);
 	luax_checktablefields<Texture::SettingType>(L, idx, "texture setting name", Texture::getConstant);
 
 
-	if (forceRenderTarget.hasValue)
-		s.renderTarget = forceRenderTarget.value;
-	else
+	if (!forceRenderTarget.hasValue)
 		s.renderTarget = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_RENDER_TARGET), s.renderTarget);
 		s.renderTarget = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_RENDER_TARGET), s.renderTarget);
 
 
 	lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_FORMAT));
 	lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_FORMAT));
@@ -1366,7 +1361,7 @@ int w_newParticleSystem(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
+static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages)
 {
 {
 	using namespace love::filesystem;
 	using namespace love::filesystem;
 
 
@@ -1426,61 +1421,23 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string
 	if (!(has_arg1 || has_arg2))
 	if (!(has_arg1 || has_arg2))
 		luaL_checkstring(L, startidx);
 		luaL_checkstring(L, startidx);
 
 
-	luax_getfunction(L, "graphics", "_shaderCodeToGLSL");
-
-	// push vertexcode and pixelcode strings to the top of the stack
-	lua_pushboolean(L, gles);
-
 	if (has_arg1)
 	if (has_arg1)
-		lua_pushvalue(L, startidx + 0);
-	else
-		lua_pushnil(L);
-
+		stages.push_back(luax_checkstring(L, startidx + 0));
 	if (has_arg2)
 	if (has_arg2)
-		lua_pushvalue(L, startidx + 1);
-	else
-		lua_pushnil(L);
-
-	// call effectCodeToGLSL, returned values will be at the top of the stack
-	if (lua_pcall(L, 3, 2, 0) != 0)
-		return luaL_error(L, "%s", lua_tostring(L, -1));
-
-	// vertex shader code
-	if (lua_isstring(L, -2))
-		vertexsource = luax_checkstring(L, -2);
-	else if (has_arg1 && has_arg2)
-		return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)");
-
-	// pixel shader code
-	if (lua_isstring(L, -1))
-		pixelsource = luax_checkstring(L, -1);
-	else if (has_arg1 && has_arg2)
-		return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)");
-
-	if (vertexsource.empty() && pixelsource.empty())
-	{
-		// Original args had source code, but effectCodeToGLSL couldn't translate it
-		for (int i = startidx; i < startidx + 2; i++)
-		{
-			if (lua_isstring(L, i))
-				return luaL_argerror(L, i, "missing 'position' or 'effect' function?");
-		}
-	}
+		stages.push_back(luax_checkstring(L, startidx + 1));
 
 
 	return 0;
 	return 0;
 }
 }
 
 
 int w_newShader(lua_State *L)
 int w_newShader(lua_State *L)
 {
 {
-	bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
-
-	std::string vertexsource, pixelsource;
-	w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
+	std::vector<std::string> stages;
+	w_getShaderSource(L, 1, stages);
 
 
 	bool should_error = false;
 	bool should_error = false;
 	try
 	try
 	{
 	{
-		Shader *shader = instance()->newShader(vertexsource, pixelsource);
+		Shader *shader = instance()->newShader(stages);
 		luax_pushtype(L, shader);
 		luax_pushtype(L, shader);
 		shader->release();
 		shader->release();
 	}
 	}
@@ -1504,14 +1461,14 @@ int w_validateShader(lua_State *L)
 {
 {
 	bool gles = luax_checkboolean(L, 1);
 	bool gles = luax_checkboolean(L, 1);
 
 
-	std::string vertexsource, pixelsource;
-	w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
+	std::vector<std::string> stages;
+	w_getShaderSource(L, 2, stages);
 
 
 	bool success = true;
 	bool success = true;
 	std::string err;
 	std::string err;
 	try
 	try
 	{
 	{
-		success = instance()->validateShader(gles, vertexsource, pixelsource, err);
+		success = instance()->validateShader(gles, stages, err);
 	}
 	}
 	catch (love::Exception &e)
 	catch (love::Exception &e)
 	{
 	{
@@ -2312,46 +2269,6 @@ int w_getShader(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_setDefaultShaderCode(lua_State *L)
-{
-	for (int i = 0; i < 2; i++)
-	{
-		luaL_checktype(L, i + 1, LUA_TTABLE);
-
-		for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++)
-		{
-			const char *langname;
-			if (!Shader::getConstant((Shader::Language) lang, langname))
-				continue;
-
-			lua_getfield(L, i + 1, langname);
-
-			lua_getfield(L, -1, "vertex");
-			lua_getfield(L, -2, "pixel");
-			lua_getfield(L, -3, "videopixel");
-			lua_getfield(L, -4, "arraypixel");
-
-			std::string vertex = luax_checkstring(L, -4);
-			std::string pixel = luax_checkstring(L, -3);
-			std::string videopixel = luax_checkstring(L, -2);
-			std::string arraypixel = luax_checkstring(L, -1);
-
-			lua_pop(L, 5);
-
-			Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
-			Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
-
-			Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
-			Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel;
-
-			Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
-			Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
-		}
-	}
-
-	return 0;
-}
-
 int w_getSupported(lua_State *L)
 int w_getSupported(lua_State *L)
 {
 {
 	const Graphics::Capabilities &caps = instance()->getCapabilities();
 	const Graphics::Capabilities &caps = instance()->getCapabilities();
@@ -3226,7 +3143,6 @@ static const luaL_Reg functions[] =
 
 
 	{ "setShader", w_setShader },
 	{ "setShader", w_setShader },
 	{ "getShader", w_getShader },
 	{ "getShader", w_getShader },
-	{ "_setDefaultShaderCode", w_setDefaultShaderCode },
 
 
 	{ "getSupported", w_getSupported },
 	{ "getSupported", w_getSupported },
 	{ "getTextureFormats", w_getTextureFormats },
 	{ "getTextureFormats", w_getTextureFormats },
@@ -3343,11 +3259,6 @@ extern "C" int luaopen_love_graphics(lua_State *L)
 	else
 	else
 		lua_error(L);
 		lua_error(L);
 
 
-	if (luaL_loadbuffer(L, (const char *)graphics_shader_lua, sizeof(graphics_shader_lua), "wrap_GraphicsShader.lua") == 0)
-		lua_call(L, 0, 0);
-	else
-		lua_error(L);
-
 	return n;
 	return n;
 }
 }
 
 

+ 0 - 503
src/modules/graphics/wrap_GraphicsShader.lua

@@ -1,503 +0,0 @@
-R"luastring"--(
--- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
--- There is a matching delimiter at the bottom of the file.
-
---[[
-Copyright (c) 2006-2020 LOVE Development Team
-
-This software is provided 'as-is', without any express or implied
-warranty.  In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
-claim that you wrote the original software. If you use this software
-in a product, an acknowledgment in the product documentation would be
-appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
-misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
---]]
-
-local table_concat, table_insert = table.concat, table.insert
-local ipairs = ipairs
-
-local GLSL = {}
-
-GLSL.VERSION = { -- index using [target][gles]
-	glsl1 = {[false]="#version 120",      [true]="#version 100"},
-	glsl3 = {[false]="#version 330 core", [true]="#version 300 es"},
-	glsl4 = {[false]="#version 430 core", [true]="#version 310 es"},
-}
-
-GLSL.SYNTAX = [[
-#if !defined(GL_ES) && __VERSION__ < 140
-	#define lowp
-	#define mediump
-	#define highp
-#endif
-#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
-	#define LOVE_HIGHP_OR_MEDIUMP highp
-#else
-	#define LOVE_HIGHP_OR_MEDIUMP mediump
-#endif
-#define number float
-#define Image sampler2D
-#define ArrayImage sampler2DArray
-#define CubeImage samplerCube
-#define VolumeImage sampler3D
-#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
-	#define DepthImage sampler2DShadow
-	#define DepthArrayImage sampler2DArrayShadow
-	#define DepthCubeImage samplerCubeShadow
-#endif
-#define extern uniform
-#ifdef GL_EXT_texture_array
-#extension GL_EXT_texture_array : enable
-#endif
-#ifdef GL_OES_texture_3D
-#extension GL_OES_texture_3D : enable
-#endif
-#ifdef GL_OES_standard_derivatives
-#extension GL_OES_standard_derivatives : enable
-#endif
-]]
-
--- Uniforms shared by the vertex and pixel shader stages.
-GLSL.UNIFORMS = [[
-// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
-// but we can't guarantee that highp is always supported in fragment shaders...
-// We *really* don't want to use mediump for these in vertex shaders though.
-uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
-
-// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
-// do it as an initializer.
-LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
-LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
-LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
-
-LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
-LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
-
-#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
-
-// Alternate names
-#define ViewSpaceFromLocal TransformMatrix
-#define ClipSpaceFromView ProjectionMatrix
-#define ClipSpaceFromLocal TransformProjectionMatrix
-#define ViewNormalFromLocal NormalMatrix
-
-void love_initializeBuiltinUniforms() {
-	TransformMatrix = mat4(
-	   love_UniformsPerDraw[0],
-	   love_UniformsPerDraw[1],
-	   love_UniformsPerDraw[2],
-	   love_UniformsPerDraw[3]
-	);
-
-	ProjectionMatrix = mat4(
-	   love_UniformsPerDraw[4],
-	   love_UniformsPerDraw[5],
-	   love_UniformsPerDraw[6],
-	   love_UniformsPerDraw[7]
-	);
-
-	NormalMatrix = mat3(
-	   love_UniformsPerDraw[8].xyz,
-	   love_UniformsPerDraw[9].xyz,
-	   love_UniformsPerDraw[10].xyz
-	);
-
-	love_ScreenSize = love_UniformsPerDraw[11];
-	ConstantColor = love_UniformsPerDraw[12];
-}
-]]
-
-GLSL.FUNCTIONS = [[
-#ifdef GL_ES
-	#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
-		precision lowp sampler2DArray;
-	#endif
-	#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
-		precision lowp sampler3D;
-	#endif
-	#if __VERSION__ >= 300
-		precision lowp sampler2DShadow;
-		precision lowp samplerCubeShadow;
-		precision lowp sampler2DArrayShadow;
-	#endif
-#endif
-
-#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
-	#define Texel texture
-#else
-	#if __VERSION__ >= 130
-		#define texture2D Texel
-		#define texture3D Texel
-		#define textureCube Texel
-		#define texture2DArray Texel
-		#define love_texture2D texture
-		#define love_texture3D texture
-		#define love_textureCube texture
-		#define love_texture2DArray texture
-	#else
-		#define love_texture2D texture2D
-		#define love_texture3D texture3D
-		#define love_textureCube textureCube
-		#define love_texture2DArray texture2DArray
-	#endif
-	vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
-	vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); }
-	#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
-		vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
-	#endif
-	#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
-		vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
-	#endif
-	#ifdef PIXEL
-		vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
-		vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); }
-		#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
-			vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
-		#endif
-		#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
-			vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
-		#endif
-	#endif
-	#define texture love_texture
-#endif
-
-float gammaToLinearPrecise(float c) {
-	return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
-}
-vec3 gammaToLinearPrecise(vec3 c) {
-	bvec3 leq = lessThanEqual(c, vec3(0.04045));
-	c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
-	c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
-	c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
-	return c;
-}
-vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
-float linearToGammaPrecise(float c) {
-	return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
-}
-vec3 linearToGammaPrecise(vec3 c) {
-	bvec3 lt = lessThanEqual(c, vec3(0.0031308));
-	c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
-	c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
-	c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
-	return c;
-}
-vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
-
-// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
-
-mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
-mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
-mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
-
-mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
-mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
-mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
-
-#define gammaToLinear gammaToLinearFast
-#define linearToGamma linearToGammaFast
-
-#ifdef LOVE_GAMMA_CORRECT
-	#define gammaCorrectColor gammaToLinear
-	#define unGammaCorrectColor linearToGamma
-	#define gammaCorrectColorPrecise gammaToLinearPrecise
-	#define unGammaCorrectColorPrecise linearToGammaPrecise
-	#define gammaCorrectColorFast gammaToLinearFast
-	#define unGammaCorrectColorFast linearToGammaFast
-#else
-	#define gammaCorrectColor
-	#define unGammaCorrectColor
-	#define gammaCorrectColorPrecise
-	#define unGammaCorrectColorPrecise
-	#define gammaCorrectColorFast
-	#define unGammaCorrectColorFast
-#endif]]
-
-GLSL.VERTEX = {
-	HEADER = [[
-#define love_Position gl_Position
-
-#if __VERSION__ >= 130
-	#define attribute in
-	#define varying out
-	#ifndef LOVE_GLSL1_ON_GLSL3
-		#define love_VertexID gl_VertexID
-		#define love_InstanceID gl_InstanceID
-	#endif
-#endif
-
-#ifdef GL_ES
-	uniform mediump float love_PointSize;
-#endif]],
-
-	FUNCTIONS = [[
-void setPointSize() {
-#ifdef GL_ES
-	gl_PointSize = love_PointSize;
-#endif
-}]],
-
-	MAIN = [[
-attribute vec4 VertexPosition;
-attribute vec4 VertexTexCoord;
-attribute vec4 VertexColor;
-
-varying vec4 VaryingTexCoord;
-varying vec4 VaryingColor;
-
-vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
-
-void main() {
-	love_initializeBuiltinUniforms();
-	VaryingTexCoord = VertexTexCoord;
-	VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
-	setPointSize();
-	love_Position = position(ClipSpaceFromLocal, VertexPosition);
-}]],
-}
-
-GLSL.PIXEL = {
-	HEADER = [[
-#ifdef GL_ES
-	precision mediump float;
-#endif
-
-#define love_MaxRenderTargets gl_MaxDrawBuffers
-
-#if __VERSION__ >= 130
-	#define varying in
-	// Some drivers seem to make the pixel shader do more work when multiple
-	// pixel shader outputs are defined, even when only one is actually used.
-	// TODO: We should use reflection or something instead of this, to determine
-	// how many outputs are actually used in the shader code.
-	#ifdef LOVE_MULTI_RENDER_TARGETS
-		layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets];
-		#define love_PixelColor love_RenderTargets[0]
-	#else
-		layout(location = 0) out vec4 love_PixelColor;
-	#endif
-#else
-	#ifdef LOVE_MULTI_RENDER_TARGETS
-		#define love_RenderTargets gl_FragData
-	#endif
-	#define love_PixelColor gl_FragColor
-#endif
-
-// Legacy
-#define love_MaxCanvases love_MaxRenderTargets
-#define love_Canvases love_RenderTargets
-#ifdef LOVE_MULTI_RENDER_TARGETS
-#define LOVE_MULTI_CANVASES 1
-#endif
-
-// See Shader::updateScreenParams in Shader.cpp.
-#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))]],
-
-	FUNCTIONS = [[
-uniform sampler2D love_VideoYChannel;
-uniform sampler2D love_VideoCbChannel;
-uniform sampler2D love_VideoCrChannel;
-
-vec4 VideoTexel(vec2 texcoords) {
-	vec3 yuv;
-	yuv[0] = Texel(love_VideoYChannel, texcoords).r;
-	yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
-	yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
-	yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
-
-	vec4 color;
-	color.r = dot(yuv, vec3(1.164,  0.000,  1.596));
-	color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
-	color.b = dot(yuv, vec3(1.164,  2.018,  0.000));
-	color.a = 1.0;
-
-	return gammaCorrectColor(color);
-}]],
-
-	MAIN = [[
-uniform sampler2D MainTex;
-varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
-varying mediump vec4 VaryingColor;
-
-vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
-
-void main() {
-	love_initializeBuiltinUniforms();
-	love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
-}]],
-
-	MAIN_CUSTOM = [[
-varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
-varying mediump vec4 VaryingColor;
-
-void effect();
-
-void main() {
-	love_initializeBuiltinUniforms();
-	effect();
-}]],
-}
-
-local function getLanguageTarget(code)
-	if not code then return nil end
-	return (code:match("^%s*#pragma language (%w+)")) or "glsl1"
-end
-
-local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacorrect, custom, multirendertarget)
-	stage = stage:upper()
-	local lines = {
-		GLSL.VERSION[lang][gles],
-		"#define " ..stage .. " " .. stage,
-		glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
-		gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
-		multirendertarget and "#define LOVE_MULTI_RENDER_TARGETS 1" or "",
-		GLSL.SYNTAX,
-		GLSL[stage].HEADER,
-		GLSL.UNIFORMS,
-		GLSL.FUNCTIONS,
-		GLSL[stage].FUNCTIONS,
-		custom and GLSL[stage].MAIN_CUSTOM or GLSL[stage].MAIN,
-		((lang == "glsl1" or glsl1on3) and not gles) and "#line 0" or "#line 1",
-		code,
-	}
-	return table_concat(lines, "\n")
-end
-
-local function isVertexCode(code)
-	return code:match("vec4%s+position%s*%(") ~= nil
-end
-
-local function isPixelCode(code)
-	if code:match("vec4%s+effect%s*%(") then
-		return true
-	elseif code:match("void%s+effect%s*%(") then -- custom effect function
-		local multirendertargets = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil)
-		return true, true, multirendertargets
-	else
-		return false
-	end
-end
-
-function love.graphics._shaderCodeToGLSL(gles, arg1, arg2)
-	local vertexcode, pixelcode
-	local is_custompixel = false -- whether pixel code has "effects" function instead of "effect"
-	local is_multicanvas = false
-
-	if arg1 then
-		if isVertexCode(arg1) then
-			vertexcode = arg1 -- first arg contains vertex shader code
-		end
-
-		local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg1)
-		if ispixel then
-			pixelcode = arg1 -- first arg contains pixel shader code
-			is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
-		end
-	end
-	
-	if arg2 then
-		if isVertexCode(arg2) then
-			vertexcode = arg2 -- second arg contains vertex shader code
-		end
-
-		local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg2)
-		if ispixel then
-			pixelcode = arg2 -- second arg contains pixel shader code
-			is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
-		end
-	end
-
-	local graphicsfeatures = love.graphics.getSupported()
-	local supportsGLSL3 = graphicsfeatures.glsl3
-	local supportsGLSL4 = graphicsfeatures.glsl4
-	local gammacorrect = love.graphics.isGammaCorrect()
-
-	local targetlang = getLanguageTarget(pixelcode or vertexcode)
-	if getLanguageTarget(vertexcode or pixelcode) ~= targetlang then
-		error("vertex and pixel shader languages must match", 2)
-	end
-
-	if targetlang == "glsl3" and not supportsGLSL3 then
-		error("GLSL 3 shaders are not supported on this system.", 2)
-	end
-
-	if targetlang == "glsl4" and not supportsGLSL4 then
-		error("GLSL 4 shaders are not supported on this system.", 2)
-	end
-
-	if targetlang ~= nil and not GLSL.VERSION[targetlang] then
-		error("Invalid shader language: " .. targetlang, 2)
-	end
-
-	local lang = targetlang or "glsl1"
-	local glsl1on3 = false
-	if lang == "glsl1" and supportsGLSL3 then
-		lang = "glsl3"
-		glsl1on3 = true
-	end
-
-	if vertexcode then
-		vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gles, glsl1on3, gammacorrect)
-	end
-	if pixelcode then
-		pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gles, glsl1on3, gammacorrect, is_custompixel, is_multicanvas)
-	end
-
-	return vertexcode, pixelcode
-end
-
-local defaultcode = {
-	vertex = [[
-vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) {
-	return clipSpaceFromLocal * localPosition;
-}]],
-	pixel = [[
-vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
-	return Texel(tex, texcoord) * vcolor;
-}]],
-	videopixel = [[
-void effect() {
-	love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
-}]],
-	arraypixel = [[
-uniform ArrayImage MainTex;
-void effect() {
-	love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
-}]],
-}
-
-local defaults = {}
-local defaults_gammacorrect = {}
-
-local langs = {
-	glsl1 = {target="glsl1", gles=false},
-	essl1 = {target="glsl1", gles=true},
-	glsl3 = {target="glsl3", gles=false},
-	essl3 = {target="glsl3", gles=true},
-}
-
-for lang, info in pairs(langs) do
-	for _, gammacorrect in ipairs{false, true} do
-		local t = gammacorrect and defaults_gammacorrect or defaults
-		t[lang] = {
-			vertex = createShaderStageCode("VERTEX", defaultcode.vertex, info.target, info.gles, false, gammacorrect),
-			pixel = createShaderStageCode("PIXEL", defaultcode.pixel, info.target, info.gles, false, gammacorrect, false),
-			videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, info.target, info.gles, false, gammacorrect, true),
-			arraypixel = createShaderStageCode("PIXEL", defaultcode.arraypixel, info.target, info.gles, false, gammacorrect, true),
-		}
-	end
-end
-
-love.graphics._setDefaultShaderCode(defaults, defaults_gammacorrect)
-
--- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
---)luastring"--"

+ 2 - 1
src/modules/image/magpie/STBHandler.cpp

@@ -30,12 +30,13 @@ static void loveSTBIAssert(bool test, const char *teststr)
 }
 }
 
 
 // stb_image
 // stb_image
- #define STBI_ONLY_JPEG
+#define STBI_ONLY_JPEG
 // #define STBI_ONLY_PNG
 // #define STBI_ONLY_PNG
 #define STBI_ONLY_BMP
 #define STBI_ONLY_BMP
 #define STBI_ONLY_TGA
 #define STBI_ONLY_TGA
 #define STBI_ONLY_HDR
 #define STBI_ONLY_HDR
 #define STBI_NO_STDIO
 #define STBI_NO_STDIO
+#define STB_IMAGE_STATIC
 #define STB_IMAGE_IMPLEMENTATION
 #define STB_IMAGE_IMPLEMENTATION
 #define STBI_ASSERT(A) loveSTBIAssert((A), #A)
 #define STBI_ASSERT(A) loveSTBIAssert((A), #A)
 #include "libraries/stb/stb_image.h"
 #include "libraries/stb/stb_image.h"

+ 68 - 37
src/modules/timer/Timer.cpp

@@ -24,6 +24,7 @@
 #include "common/delay.h"
 #include "common/delay.h"
 #include "Timer.h"
 #include "Timer.h"
 
 
+#include <iostream>
 #if defined(LOVE_WINDOWS)
 #if defined(LOVE_WINDOWS)
 #include <windows.h>
 #include <windows.h>
 #elif defined(LOVE_MACOS) || defined(LOVE_IOS)
 #elif defined(LOVE_MACOS) || defined(LOVE_IOS)
@@ -35,15 +36,6 @@
 #include <sys/time.h>
 #include <sys/time.h>
 #endif
 #endif
 
 
-#if defined(LOVE_LINUX)
-static inline double getTimeOfDay()
-{
-	timeval t;
-	gettimeofday(&t, NULL);
-	return (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
-}
-#endif
-
 namespace love
 namespace love
 {
 {
 namespace timer
 namespace timer
@@ -109,52 +101,91 @@ double Timer::getAverageDelta() const
 	return averageDelta;
 	return averageDelta;
 }
 }
 
 
-double Timer::getTimerPeriod()
+#if defined(LOVE_LINUX)
+
+static inline timespec getTimeOfDay()
 {
 {
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
-	mach_timebase_info_data_t info;
-	mach_timebase_info(&info);
-	return (double) info.numer / (double) info.denom / 1000000000.0;
-#elif defined(LOVE_WINDOWS)
-	LARGE_INTEGER temp;
-	if (QueryPerformanceFrequency(&temp) != 0 && temp.QuadPart != 0)
-		return 1.0 / (double) temp.QuadPart;
-#endif
-	return 0;
+	timeval t;
+	gettimeofday(&t, NULL);
+	return timespec { t.tv_sec, t.tv_usec * 1000 };
 }
 }
 
 
-double Timer::getTime()
+static timespec getTimeAbsolute()
 {
 {
-	// The timer period (reciprocal of the frequency.)
-	static const double timerPeriod = getTimerPeriod();
-
-#if defined(LOVE_LINUX)
-	(void) timerPeriod; // Unused on linux
-
-	double mt;
 	// Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback.
 	// Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback.
 #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) \
 #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) \
 && (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
 && (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
-	timespec t;
+
 #ifdef CLOCK_MONOTONIC_RAW
 #ifdef CLOCK_MONOTONIC_RAW
 	clockid_t clk_id = CLOCK_MONOTONIC_RAW;
 	clockid_t clk_id = CLOCK_MONOTONIC_RAW;
 #else
 #else
 	clockid_t clk_id = CLOCK_MONOTONIC;
 	clockid_t clk_id = CLOCK_MONOTONIC;
 #endif
 #endif
+
+	timespec t;
 	if (clock_gettime(clk_id, &t) == 0)
 	if (clock_gettime(clk_id, &t) == 0)
-		mt = (double) t.tv_sec + (double) t.tv_nsec / 1000000000.0;
+		return t;
 	else
 	else
+		return getTimeOfDay();
 #endif
 #endif
-		mt = getTimeOfDay();
-	return mt;
+	return getTimeOfDay();
+}
+
+double Timer::getTime()
+{
+	static const timespec start = getTimeAbsolute();
+	const timespec now = getTimeAbsolute();
+	// tv_sec and tv_nsec should be signed on POSIX, so we are fine in just subtracting here.
+	const long sec = now.tv_sec - start.tv_sec;
+	const long nsec = now.tv_nsec - start.tv_nsec;
+	return (double) sec + (double) nsec / 1.0e9;
+}
+
 #elif defined(LOVE_MACOS) || defined(LOVE_IOS)
 #elif defined(LOVE_MACOS) || defined(LOVE_IOS)
-	return (double) mach_absolute_time() * timerPeriod;
+
+static mach_timebase_info_data_t getTimebaseInfo()
+{
+	mach_timebase_info_data_t info;
+	mach_timebase_info(&info);
+	return info;
+}
+
+double Timer::getTime()
+{
+	static const mach_timebase_info_data_t info = getTimebaseInfo();
+	static const uint64_t start = mach_absolute_time();
+	const uint64_t rel = mach_absolute_time() - start;
+	return ((double) rel * 1.0e-9) * (double) info.numer / (double) info.denom;
+}
+
 #elif defined(LOVE_WINDOWS)
 #elif defined(LOVE_WINDOWS)
-	LARGE_INTEGER microTime;
-	QueryPerformanceCounter(&microTime);
-	return (double) microTime.QuadPart * timerPeriod;
-#endif
+
+static LARGE_INTEGER getTimeAbsolute()
+{
+	LARGE_INTEGER t;
+	QueryPerformanceCounter(&t);
+	return t;
+}
+
+static LARGE_INTEGER getFrequency()
+{
+	LARGE_INTEGER freq;
+	// "On systems that run Windows XP or later, the function will always succeed and will thus never return zero."
+	QueryPerformanceFrequency(&freq);
+	return freq;
 }
 }
 
 
+double Timer::getTime()
+{
+	static const LARGE_INTEGER freq = getFrequency();
+	static const LARGE_INTEGER start = getTimeAbsolute();
+	const LARGE_INTEGER now = getTimeAbsolute();
+	LARGE_INTEGER rel;
+	rel.QuadPart = now.QuadPart - start.QuadPart;
+	return (double) rel.QuadPart / (double) freq.QuadPart;
+}
+
+#endif
+
 } // timer
 } // timer
 } // love
 } // love

+ 5 - 6
src/modules/timer/Timer.h

@@ -72,9 +72,11 @@ public:
 	double getAverageDelta() const;
 	double getAverageDelta() const;
 
 
 	/**
 	/**
-	 * Gets the amount of time passed since an unspecified time. Useful for
-	 * profiling code or measuring intervals. The time is microsecond-precise,
-	 * and increases monotonically.
+	 * Gets the amount of time in seconds passed since its first invocation
+	 * (which happens as part of the Timer constructor,
+	 * which is called when the module is first opened).
+	 * Useful for profiling code or measuring intervals.
+	 * The time is microsecond-precise, and increases monotonically.
 	 * @return The time (in seconds)
 	 * @return The time (in seconds)
 	 **/
 	 **/
 	static double getTime();
 	static double getTime();
@@ -99,9 +101,6 @@ private:
 	// The current timestep.
 	// The current timestep.
 	double dt;
 	double dt;
 
 
-	// Returns the timer period on some platforms.
-	static double getTimerPeriod();
-
 }; // Timer
 }; // Timer
 
 
 } // timer
 } // timer

+ 1 - 1
src/scripts/boot.lua

@@ -532,6 +532,7 @@ function love.init()
 
 
 	-- Setup window here.
 	-- Setup window here.
 	if c.window and c.modules.window then
 	if c.window and c.modules.window then
+		love.window.setTitle(c.window.title or c.title)
 		assert(love.window.setMode(c.window.width, c.window.height,
 		assert(love.window.setMode(c.window.width, c.window.height,
 		{
 		{
 			fullscreen = c.window.fullscreen,
 			fullscreen = c.window.fullscreen,
@@ -551,7 +552,6 @@ function love.init()
 			x = c.window.x,
 			x = c.window.x,
 			y = c.window.y,
 			y = c.window.y,
 		}), "Could not set window mode")
 		}), "Could not set window mode")
-		love.window.setTitle(c.window.title or c.title)
 		if c.window.icon then
 		if c.window.icon then
 			assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
 			assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
 			love.window.setIcon(love.image.newImageData(c.window.icon))
 			love.window.setIcon(love.image.newImageData(c.window.icon))

+ 3 - 3
src/scripts/boot.lua.h

@@ -1055,6 +1055,9 @@ const unsigned char boot_lua[] =
 	0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 
 	0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 
 	0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68, 
 	0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68, 
 	0x65, 0x6e, 0x0a,
 	0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54, 
+	0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, 
+	0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a,
 	0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 
 	0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 
 	0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 
 	0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 
 	0x77, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 
 	0x77, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 
@@ -1094,9 +1097,6 @@ const unsigned char boot_lua[] =
 	0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a,
 	0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 
 	0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 
 	0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
 	0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
-	0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54, 
-	0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, 
-	0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a,
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, 0x63, 0x6f, 0x6e, 
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, 0x63, 0x6f, 0x6e, 
 	0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61, 
 	0x09, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61,