Browse Source

Move shader parsing code from Lua to C++.

Alex Szpakowski 5 years ago
parent
commit
2cff535eb1

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

@@ -1868,7 +1868,6 @@
 		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>"; };
 		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>"; };
 		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>"; };
@@ -2855,7 +2854,6 @@
 				FADF54391E3DAFF700012CC0 /* wrap_Graphics.cpp */,
 				FADF543A1E3DAFF700012CC0 /* wrap_Graphics.h */,
 				FADF54371E3DAFBA00012CC0 /* wrap_Graphics.lua */,
-				FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */,
 				FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */,
 				FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */,
 				FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */,

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

@@ -104,8 +104,6 @@ bool isDebugEnabled()
 
 love::Type Graphics::type("graphics", &Module::type);
 
-Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
-
 Graphics::Graphics()
 	: width(0)
 	, height(0)
@@ -214,13 +212,8 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
 	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;
 	std::string cachekey;
 
@@ -241,7 +234,8 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
 
 	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())
 			cachedShaderStages[stage][cachekey] = s;
 	}
@@ -249,15 +243,48 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::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]);
 }
 
 Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage)
@@ -290,26 +317,44 @@ void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::
 	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
 	// 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
@@ -1743,14 +1788,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
 	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.
  **/

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

@@ -415,11 +415,6 @@ public:
 		}
 	};
 
-	struct DefaultShaderCode
-	{
-		std::string source[ShaderStage::STAGE_MAX_ENUM];
-	};
-
 	Graphics();
 	virtual ~Graphics();
 
@@ -436,8 +431,7 @@ public:
 	SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage);
 	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(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
 
@@ -448,7 +442,7 @@ public:
 
 	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.
@@ -827,9 +821,6 @@ public:
 
 	static void flushBatchedDrawsGlobal();
 
-	virtual Shader::Language getShaderLanguageTarget() const = 0;
-	const DefaultShaderCode &getCurrentDefaultShaderCode() const;
-
 	void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
 
 	template <typename T>
@@ -869,9 +860,6 @@ public:
 	static bool getConstant(StackType in, const char *&out);
 	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:
 
 	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 Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
 	virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;

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

@@ -28,12 +28,402 @@
 
 // C++
 #include <string>
+#include <regex>
+#include <sstream>
 
 namespace love
 {
 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.");
 
 love::Type Shader::type("Shader", &Object::type);
@@ -41,6 +431,59 @@ love::Type Shader::type("Shader", &Object::type);
 Shader *Shader::current = 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)
 	: stages()
 {
@@ -157,47 +600,93 @@ void Shader::deinitialize()
 	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
 } // love

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

@@ -55,9 +55,8 @@ public:
 	enum Language
 	{
 		LANGUAGE_GLSL1,
-		LANGUAGE_ESSL1,
 		LANGUAGE_GLSL3,
-		LANGUAGE_ESSL3,
+		LANGUAGE_GLSL4,
 		LANGUAGE_MAX_ENUM
 	};
 
@@ -94,6 +93,14 @@ public:
 		STANDARD_MAX_ENUM
 	};
 
+	struct SourceInfo
+	{
+		Language language;
+		bool isStage[ShaderStage::STAGE_MAX_ENUM];
+		bool customPixelFunction;
+		bool usesMRT;
+	};
+
 	struct MatrixSize
 	{
 		short columns;
@@ -192,11 +199,16 @@ public:
 	void checkMainTextureType(TextureType textype, bool isDepthSampler) 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 initialize();
 	static void deinitialize();
 
+	static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage);
+
 	static bool getConstant(const char *in, Language &out);
 	static bool getConstant(Language in, const char *&out);
 
@@ -207,15 +219,6 @@ protected:
 
 	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
 
 } // 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);
 }
 
+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[] =
 {
 	{ "vertex", STAGE_VERTEX },

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

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

+ 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.
 	restoreState(states.back());
 
-	int gammacorrect = isGammaCorrect() ? 1 : 0;
-	Shader::Language target = getShaderLanguageTarget();
-
 	// We always need a default shader.
 	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])
 			continue;
 
@@ -267,8 +266,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 		{
 			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 &)
@@ -1501,18 +1502,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
 	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
 } // graphics
 } // love

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

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

+ 9 - 98
src/modules/graphics/wrap_Graphics.cpp

@@ -45,11 +45,6 @@ static const char 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 graphics
@@ -1366,7 +1361,7 @@ int w_newParticleSystem(lua_State *L)
 	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;
 
@@ -1426,61 +1421,23 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string
 	if (!(has_arg1 || has_arg2))
 		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)
-		lua_pushvalue(L, startidx + 0);
-	else
-		lua_pushnil(L);
-
+		stages.push_back(luax_checkstring(L, startidx + 0));
 	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;
 }
 
 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;
 	try
 	{
-		Shader *shader = instance()->newShader(vertexsource, pixelsource);
+		Shader *shader = instance()->newShader(stages);
 		luax_pushtype(L, shader);
 		shader->release();
 	}
@@ -1504,14 +1461,14 @@ int w_validateShader(lua_State *L)
 {
 	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;
 	std::string err;
 	try
 	{
-		success = instance()->validateShader(gles, vertexsource, pixelsource, err);
+		success = instance()->validateShader(gles, stages, err);
 	}
 	catch (love::Exception &e)
 	{
@@ -2289,46 +2246,6 @@ int w_getShader(lua_State *L)
 	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)
 {
 	const Graphics::Capabilities &caps = instance()->getCapabilities();
@@ -3203,7 +3120,6 @@ static const luaL_Reg functions[] =
 
 	{ "setShader", w_setShader },
 	{ "getShader", w_getShader },
-	{ "_setDefaultShaderCode", w_setDefaultShaderCode },
 
 	{ "getSupported", w_getSupported },
 	{ "getTextureFormats", w_getTextureFormats },
@@ -3319,11 +3235,6 @@ extern "C" int luaopen_love_graphics(lua_State *L)
 	else
 		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;
 }
 

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