Browse Source

Restructured some Shader code. Added runtime temporary caching and sharing of shader stages. Resolves issue #1235.

--HG--
branch : minor
Alex Szpakowski 7 years ago
parent
commit
e5b0d96b27

+ 4 - 0
CMakeLists.txt

@@ -529,6 +529,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Quad.h
 	src/modules/graphics/Shader.cpp
 	src/modules/graphics/Shader.h
+	src/modules/graphics/ShaderStage.cpp
+	src/modules/graphics/ShaderStage.h
 	src/modules/graphics/SpriteBatch.cpp
 	src/modules/graphics/SpriteBatch.h
 	src/modules/graphics/StreamBuffer.cpp
@@ -588,6 +590,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
 	src/modules/graphics/opengl/ParticleSystem.h
 	src/modules/graphics/opengl/Shader.cpp
 	src/modules/graphics/opengl/Shader.h
+	src/modules/graphics/opengl/ShaderStage.cpp
+	src/modules/graphics/opengl/ShaderStage.h
 	src/modules/graphics/opengl/SpriteBatch.cpp
 	src/modules/graphics/opengl/SpriteBatch.h
 	src/modules/graphics/opengl/StreamBuffer.cpp

+ 36 - 10
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -821,9 +821,6 @@
 		FA27B39D1B498151008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B38A1B498151008A9DCE /* Video.cpp */; };
 		FA27B39E1B498151008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B38A1B498151008A9DCE /* Video.cpp */; };
 		FA27B39F1B498151008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B38B1B498151008A9DCE /* Video.h */; };
-		FA27B3A01B498151008A9DCE /* VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B38C1B498151008A9DCE /* VideoStream.cpp */; };
-		FA27B3A11B498151008A9DCE /* VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B38C1B498151008A9DCE /* VideoStream.cpp */; };
-		FA27B3A21B498151008A9DCE /* VideoStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B38D1B498151008A9DCE /* VideoStream.h */; };
 		FA27B3A91B498151008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3931B498151008A9DCE /* Video.h */; };
 		FA27B3AA1B498151008A9DCE /* VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3941B498151008A9DCE /* VideoStream.cpp */; };
 		FA27B3AB1B498151008A9DCE /* VideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3941B498151008A9DCE /* VideoStream.cpp */; };
@@ -843,6 +840,12 @@
 		FA2AF6741DAD64970032B62C /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA2AF6731DAD64970032B62C /* vertex.cpp */; };
 		FA2AF6751DAD64970032B62C /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA2AF6731DAD64970032B62C /* vertex.cpp */; };
 		FA317EBA18F28B6D00B0BCD7 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA317EB918F28B6D00B0BCD7 /* libz.dylib */; };
+		FA3C5E421F8C368C0003C579 /* ShaderStage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */; };
+		FA3C5E431F8C368C0003C579 /* ShaderStage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */; };
+		FA3C5E441F8C368C0003C579 /* ShaderStage.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3C5E411F8C368C0003C579 /* ShaderStage.h */; };
+		FA3C5E471F8D80CA0003C579 /* ShaderStage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */; };
+		FA3C5E481F8D80CA0003C579 /* ShaderStage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */; };
+		FA3C5E491F8D80CA0003C579 /* ShaderStage.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3C5E461F8D80CA0003C579 /* ShaderStage.h */; };
 		FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
 		FA41A3C91C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; };
 		FA41A3CA1C0A1F950084430C /* ASTCHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA41A3C71C0A1F950084430C /* ASTCHandler.h */; };
@@ -963,6 +966,10 @@
 		FAA3A9AE1B7D465A00CED060 /* android.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA3A9AC1B7D465A00CED060 /* android.cpp */; };
 		FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA3A9AC1B7D465A00CED060 /* android.cpp */; };
 		FAA3A9B01B7D465A00CED060 /* android.h in Headers */ = {isa = PBXBuildFile; fileRef = FAA3A9AD1B7D465A00CED060 /* android.h */; };
+		FAA54ACA1F91660400A8FA7B /* OggDemuxer.h in Headers */ = {isa = PBXBuildFile; fileRef = FAA54AC61F91660400A8FA7B /* OggDemuxer.h */; };
+		FAA54ACB1F91660400A8FA7B /* TheoraVideoStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FAA54AC71F91660400A8FA7B /* TheoraVideoStream.h */; };
+		FAA54ACC1F91660400A8FA7B /* TheoraVideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA54AC81F91660400A8FA7B /* TheoraVideoStream.cpp */; };
+		FAA54ACD1F91660400A8FA7B /* OggDemuxer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA54AC91F91660400A8FA7B /* OggDemuxer.cpp */; };
 		FAA627CE18E7E1560080752D /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAA627CD18E7E1560080752D /* CoreServices.framework */; };
 		FAAA3FD81F64B3AD00F89E99 /* lprefix.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAA3FD31F64B3AD00F89E99 /* lprefix.h */; };
 		FAAA3FD91F64B3AD00F89E99 /* lstrlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FAAA3FD41F64B3AD00F89E99 /* lstrlib.c */; };
@@ -1737,8 +1744,6 @@
 		FA1E88821DF363DB00E808AA /* Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Filter.h; sourceTree = "<group>"; };
 		FA27B38A1B498151008A9DCE /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
 		FA27B38B1B498151008A9DCE /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; };
-		FA27B38C1B498151008A9DCE /* VideoStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VideoStream.cpp; sourceTree = "<group>"; };
-		FA27B38D1B498151008A9DCE /* VideoStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoStream.h; sourceTree = "<group>"; };
 		FA27B3931B498151008A9DCE /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = "<group>"; };
 		FA27B3941B498151008A9DCE /* VideoStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VideoStream.cpp; sourceTree = "<group>"; };
 		FA27B3951B498151008A9DCE /* VideoStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoStream.h; sourceTree = "<group>"; };
@@ -1757,6 +1762,10 @@
 		FA2AF6731DAD64970032B62C /* vertex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vertex.cpp; sourceTree = "<group>"; };
 		FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_RandomGenerator.lua; sourceTree = "<group>"; };
 		FA317EB918F28B6D00B0BCD7 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+		FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = "<group>"; };
+		FA3C5E411F8C368C0003C579 /* ShaderStage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderStage.h; sourceTree = "<group>"; };
+		FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderStage.cpp; sourceTree = "<group>"; };
+		FA3C5E461F8D80CA0003C579 /* ShaderStage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderStage.h; sourceTree = "<group>"; };
 		FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = ASTCHandler.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
 		FA41A3C71C0A1F950084430C /* ASTCHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASTCHandler.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
 		FA4B66C81ABBCF1900558F15 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = "<group>"; };
@@ -1835,6 +1844,10 @@
 		FA9D8DDF1DEF843D002CD881 /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = "<group>"; };
 		FAA3A9AC1B7D465A00CED060 /* android.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = android.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
 		FAA3A9AD1B7D465A00CED060 /* android.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = android.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+		FAA54AC61F91660400A8FA7B /* OggDemuxer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OggDemuxer.h; sourceTree = "<group>"; };
+		FAA54AC71F91660400A8FA7B /* TheoraVideoStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TheoraVideoStream.h; sourceTree = "<group>"; };
+		FAA54AC81F91660400A8FA7B /* TheoraVideoStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TheoraVideoStream.cpp; sourceTree = "<group>"; };
+		FAA54AC91F91660400A8FA7B /* OggDemuxer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OggDemuxer.cpp; sourceTree = "<group>"; };
 		FAA627CD18E7E1560080752D /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
 		FAAA3FD31F64B3AD00F89E99 /* lprefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lprefix.h; sourceTree = "<group>"; };
 		FAAA3FD41F64B3AD00F89E99 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lstrlib.c; sourceTree = "<group>"; };
@@ -2705,6 +2718,8 @@
 				FA0B7BBD1A95902C000E1D17 /* Quad.h */,
 				FA1BA0AF1E16FD0800AA2803 /* Shader.cpp */,
 				FA1BA0B01E16FD0800AA2803 /* Shader.h */,
+				FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */,
+				FA3C5E411F8C368C0003C579 /* ShaderStage.h */,
 				FADF542D1E3DABF600012CC0 /* SpriteBatch.cpp */,
 				FADF542E1E3DABF600012CC0 /* SpriteBatch.h */,
 				FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */,
@@ -2770,6 +2785,8 @@
 				FA0B7B9A1A95902C000E1D17 /* ParticleSystem.h */,
 				FA0B7B9D1A95902C000E1D17 /* Shader.cpp */,
 				FA0B7B9E1A95902C000E1D17 /* Shader.h */,
+				FA3C5E451F8D80CA0003C579 /* ShaderStage.cpp */,
+				FA3C5E461F8D80CA0003C579 /* ShaderStage.h */,
 				FA0B7B9F1A95902C000E1D17 /* SpriteBatch.cpp */,
 				FA0B7BA01A95902C000E1D17 /* SpriteBatch.h */,
 				FA7634481E28722A0066EF9E /* StreamBuffer.cpp */,
@@ -3244,10 +3261,12 @@
 		FA27B3891B498151008A9DCE /* theora */ = {
 			isa = PBXGroup;
 			children = (
+				FAA54AC91F91660400A8FA7B /* OggDemuxer.cpp */,
+				FAA54AC61F91660400A8FA7B /* OggDemuxer.h */,
+				FAA54AC81F91660400A8FA7B /* TheoraVideoStream.cpp */,
+				FAA54AC71F91660400A8FA7B /* TheoraVideoStream.h */,
 				FA27B38A1B498151008A9DCE /* Video.cpp */,
 				FA27B38B1B498151008A9DCE /* Video.h */,
-				FA27B38C1B498151008A9DCE /* VideoStream.cpp */,
-				FA27B38D1B498151008A9DCE /* VideoStream.h */,
 			);
 			path = theora;
 			sourceTree = "<group>";
@@ -3622,6 +3641,7 @@
 				FADF54361E3DAE6E00012CC0 /* wrap_SpriteBatch.h in Headers */,
 				FA0B7DB01A95902C000E1D17 /* wrap_CompressedImageData.h in Headers */,
 				FA0B7AC11A958EA3000E1D17 /* callbacks.h in Headers */,
+				FA3C5E491F8D80CA0003C579 /* ShaderStage.h in Headers */,
 				FA0B7D8F1A95902C000E1D17 /* ddsHandler.h in Headers */,
 				FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */,
 				FAF140C41E20934C00F898D2 /* ShaderLang.h in Headers */,
@@ -3658,6 +3678,7 @@
 				FA0B7CE41A95902C000E1D17 /* wrap_Audio.h in Headers */,
 				FA0B7A7F1A958EA3000E1D17 /* b2ContactSolver.h in Headers */,
 				FADF540F1E3D7CDD00012CC0 /* wrap_Video.h in Headers */,
+				FAA54ACA1F91660400A8FA7B /* OggDemuxer.h in Headers */,
 				FA0B79491A958E3B000E1D17 /* version.h in Headers */,
 				FAF140581E20934C00F898D2 /* BaseTypes.h in Headers */,
 				FA0B7B2B1A958EA3000E1D17 /* stb_image.h in Headers */,
@@ -3783,9 +3804,9 @@
 				FA0B7E961A95902C000E1D17 /* Mpg123Decoder.h in Headers */,
 				FADF54091E3D78F700012CC0 /* Video.h in Headers */,
 				FA0B7D501A95902C000E1D17 /* SpriteBatch.h in Headers */,
+				FAA54ACB1F91660400A8FA7B /* TheoraVideoStream.h in Headers */,
 				FA0B7DD51A95902C000E1D17 /* BezierCurve.h in Headers */,
 				FA0B79271A958E3B000E1D17 /* int.h in Headers */,
-				FA27B3A21B498151008A9DCE /* VideoStream.h in Headers */,
 				FA0B7E531A95902C000E1D17 /* wrap_FrictionJoint.h in Headers */,
 				FA0B7EB71A95902C000E1D17 /* wrap_System.h in Headers */,
 				FA27B3A91B498151008A9DCE /* Video.h in Headers */,
@@ -3878,6 +3899,7 @@
 				217DFBF81D9F6D490055D849 /* pierror.h in Headers */,
 				217DFC021D9F6D490055D849 /* tcp.h in Headers */,
 				FA0B7A3D1A958EA3000E1D17 /* b2TimeOfImpact.h in Headers */,
+				FA3C5E441F8C368C0003C579 /* ShaderStage.h in Headers */,
 				FA0B79261A958E3B000E1D17 /* Exception.h in Headers */,
 				FA0B7D4D1A95902C000E1D17 /* Shader.h in Headers */,
 				FA0B793D1A958E3B000E1D17 /* runtime.h in Headers */,
@@ -4093,6 +4115,7 @@
 				FA4F2C031DE936C200CA37D7 /* auxiliar.c in Sources */,
 				FA0B7E731A95902C000E1D17 /* wrap_Shape.cpp in Sources */,
 				FA0B7CFE1A95902C000E1D17 /* File.cpp in Sources */,
+				FA3C5E481F8D80CA0003C579 /* ShaderStage.cpp in Sources */,
 				FA27B39E1B498151008A9DCE /* Video.cpp in Sources */,
 				FA0B7A751A958EA3000E1D17 /* b2ChainAndPolygonContact.cpp in Sources */,
 				FA0B7E6A1A95902C000E1D17 /* wrap_PulleyJoint.cpp in Sources */,
@@ -4128,6 +4151,7 @@
 				FAF140AA1E20934C00F898D2 /* SymbolTable.cpp in Sources */,
 				FA0B7A8D1A958EA3000E1D17 /* b2DistanceJoint.cpp in Sources */,
 				FA0B7A841A958EA3000E1D17 /* b2EdgeAndPolygonContact.cpp in Sources */,
+				FA3C5E431F8C368C0003C579 /* ShaderStage.cpp in Sources */,
 				FA0B7E191A95902C000E1D17 /* MotorJoint.cpp in Sources */,
 				FAF1406F1E20934C00F898D2 /* Initialize.cpp in Sources */,
 				FA0B7E4F1A95902C000E1D17 /* wrap_Fixture.cpp in Sources */,
@@ -4252,7 +4276,6 @@
 				FA0B791C1A958E3B000E1D17 /* b64.cpp in Sources */,
 				FA1E88851DF363E100E808AA /* Filter.cpp in Sources */,
 				FA0B7DA01A95902C000E1D17 /* KTXHandler.cpp in Sources */,
-				FA27B3A11B498151008A9DCE /* VideoStream.cpp in Sources */,
 				FA0B7A5C1A958EA3000E1D17 /* b2Timer.cpp in Sources */,
 				FA0B7CEC1A95902C000E1D17 /* Event.cpp in Sources */,
 				FA27B3AB1B498151008A9DCE /* VideoStream.cpp in Sources */,
@@ -4428,6 +4451,7 @@
 				FA0B7D2B1A95902C000E1D17 /* wrap_Rasterizer.cpp in Sources */,
 				FA0B7E8E1A95902C000E1D17 /* GmeDecoder.cpp in Sources */,
 				FA0B7CD61A95902C000E1D17 /* Audio.cpp in Sources */,
+				FA3C5E421F8C368C0003C579 /* ShaderStage.cpp in Sources */,
 				FA0B7A9E1A958EA3000E1D17 /* b2PrismaticJoint.cpp in Sources */,
 				FA0B7EAF1A95902C000E1D17 /* System.cpp in Sources */,
 				FA0B7EE21A95902D000E1D17 /* Window.cpp in Sources */,
@@ -4617,7 +4641,6 @@
 				FA0B7D9F1A95902C000E1D17 /* KTXHandler.cpp in Sources */,
 				FA1E88831DF363DB00E808AA /* Filter.cpp in Sources */,
 				FA0B7A2C1A958EA3000E1D17 /* b2CollideCircle.cpp in Sources */,
-				FA27B3A01B498151008A9DCE /* VideoStream.cpp in Sources */,
 				FA0B7CEB1A95902C000E1D17 /* Event.cpp in Sources */,
 				FA1557C31CE90BD200AFF582 /* EXRHandler.cpp in Sources */,
 				FA27B3AA1B498151008A9DCE /* VideoStream.cpp in Sources */,
@@ -4700,6 +4723,7 @@
 				FA0B7DCD1A95902C000E1D17 /* wrap_Keyboard.cpp in Sources */,
 				FA0B7EE51A95902D000E1D17 /* Window.cpp in Sources */,
 				FA6A2B6A1F5F7F560074C308 /* DataView.cpp in Sources */,
+				FA3C5E471F8D80CA0003C579 /* ShaderStage.cpp in Sources */,
 				FA0B7E391A95902C000E1D17 /* World.cpp in Sources */,
 				FA0B7ABD1A958EA3000E1D17 /* compress.c in Sources */,
 				FA0B7ACB1A958EA3000E1D17 /* list.c in Sources */,
@@ -4764,6 +4788,7 @@
 				FAF140881E20934C00F898D2 /* PoolAlloc.cpp in Sources */,
 				FA0B7AAD1A958EA3000E1D17 /* b2WheelJoint.cpp in Sources */,
 				FA0B7DEE1A95902C000E1D17 /* Mouse.cpp in Sources */,
+				FAA54ACC1F91660400A8FA7B /* TheoraVideoStream.cpp in Sources */,
 				FA1E887E1DF363CD00E808AA /* Filter.cpp in Sources */,
 				FA0B7D281A95902C000E1D17 /* wrap_GlyphData.cpp in Sources */,
 				FA0B7DE21A95902C000E1D17 /* wrap_RandomGenerator.cpp in Sources */,
@@ -4773,6 +4798,7 @@
 				FA0B7D0C1A95902C000E1D17 /* wrap_Filesystem.cpp in Sources */,
 				FA0B7AD91A958EA3000E1D17 /* glad.cpp in Sources */,
 				217DFBF61D9F6D490055D849 /* options.c in Sources */,
+				FAA54ACD1F91660400A8FA7B /* OggDemuxer.cpp in Sources */,
 				217DFBD91D9F6D490055D849 /* auxiliar.c in Sources */,
 				217DFBDB1D9F6D490055D849 /* buffer.c in Sources */,
 				FA0B7DB41A95902C000E1D17 /* wrap_ImageData.cpp in Sources */,

+ 81 - 5
src/modules/graphics/Graphics.cpp

@@ -22,6 +22,7 @@
 #include "Graphics.h"
 #include "Buffer.h"
 #include "math/MathModule.h"
+#include "data/DataModule.h"
 #include "Polyline.h"
 #include "font/Font.h"
 #include "window/Window.h"
@@ -100,7 +101,7 @@ bool isDebugEnabled()
 
 love::Type Graphics::type("graphics", &Module::type);
 
-Shader::ShaderSource Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
+Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
 
 Graphics::Graphics()
 	: width(0)
@@ -115,6 +116,7 @@ Graphics::Graphics()
 	, canvasSwitchCount(0)
 	, drawCallsBatched(0)
 	, capabilities()
+	, cachedShaderStages()
 {
 	transformStack.reserve(16);
 	transformStack.push_back(Matrix4());
@@ -149,6 +151,9 @@ Graphics::~Graphics()
 	delete streamBufferState.vb[1];
 	delete streamBufferState.indexBuffer;
 
+	for (int i = 0; i < (int) ShaderStage::STAGE_MAX_ENUM; i++)
+		cachedShaderStages[i].clear();
+
 	Shader::deinitialize();
 }
 
@@ -177,9 +182,77 @@ Video *Graphics::newVideo(love::video::VideoStream *stream, float dpiscale)
 	return new Video(this, stream, dpiscale);
 }
 
-bool Graphics::validateShader(bool gles, const Shader::ShaderSource &source, std::string &err)
+ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource)
+{
+	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;
+
+	if (!source.empty())
+	{
+		data::HashFunction::Value hashvalue;
+		data::hash(data::HashFunction::FUNCTION_SHA1, source.c_str(), source.size(), hashvalue);
+
+		cachekey = std::string(hashvalue.data, hashvalue.size);
+
+		auto it = cachedShaderStages[stage].find(cachekey);
+		if (it != cachedShaderStages[stage].end())
+		{
+			s = it->second;
+			s->retain();
+		}
+	}
+
+	if (s == nullptr)
+	{
+		s = newShaderStageInternal(stage, cachekey, source, getRenderer() == RENDERER_OPENGLES);
+		if (!cachekey.empty())
+			cachedShaderStages[stage][cachekey] = s;
+	}
+
+	return s;
+}
+
+void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &hashkey)
+{
+	cachedShaderStages[type].erase(hashkey);
+}
+
+Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
+{
+	if (vertex.empty() && pixel.empty())
+		throw love::Exception("Error creating shader: no source code!");
+
+	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());
+}
+
+bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err)
 {
-	return Shader::validate(this, gles, source, true, err);
+	if (vertex.empty() && pixel.empty())
+	{
+		err = "Error validating shader: no source code!";
+		return false;
+	}
+
+	StrongRef<ShaderStage> vertexstage;
+	StrongRef<ShaderStage> pixelstage;
+
+	// 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);
+
+	if (!pixel.empty())
+		pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN);
+
+	return Shader::validate(vertexstage.get(), pixelstage.get(), err);
 }
 
 int Graphics::getWidth() const
@@ -1425,9 +1498,12 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
 	return p;
 }
 
-const Shader::ShaderSource &Graphics::getCurrentDefaultShaderCode() const
+const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const
 {
-	return defaultShaderCode[Shader::STANDARD_DEFAULT][getShaderLanguageTarget()][isGammaCorrect() ? 1 : 0];
+	int languageindex = (int) getShaderLanguageTarget();
+	int gammaindex = isGammaCorrect() ? 1 : 0;
+
+	return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex];
 }
 
 /**

+ 19 - 4
src/modules/graphics/Graphics.h

@@ -34,6 +34,7 @@
 #include "Texture.h"
 #include "Canvas.h"
 #include "Font.h"
+#include "ShaderStage.h"
 #include "Shader.h"
 #include "Quad.h"
 #include "Mesh.h"
@@ -44,6 +45,7 @@
 #include "font/Rasterizer.h"
 #include "font/Font.h"
 #include "video/VideoStream.h"
+#include "data/HashFunction.h"
 
 // C++
 #include <string>
@@ -359,6 +361,11 @@ public:
 		{}
 	};
 
+	struct DefaultShaderCode
+	{
+		std::string source[ShaderStage::STAGE_MAX_ENUM];
+	};
+
 	Graphics();
 	virtual ~Graphics();
 
@@ -378,7 +385,9 @@ public:
 	virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
 
 	virtual Canvas *newCanvas(const Canvas::Settings &settings) = 0;
-	virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
+
+	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
+	Shader *newShader(const std::string &vertex, const std::string &pixel);
 
 	virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
 
@@ -390,7 +399,7 @@ public:
 
 	virtual Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {}) = 0;
 
-	bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
+	bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err);
 
 	/**
 	 * Resets the current color, background color, line style, and so forth.
@@ -758,7 +767,9 @@ public:
 	static void flushStreamDrawsGlobal();
 
 	virtual Shader::Language getShaderLanguageTarget() const = 0;
-	const Shader::ShaderSource &getCurrentDefaultShaderCode() const;
+	const DefaultShaderCode &getCurrentDefaultShaderCode() const;
+
+	void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
 
 	template <typename T>
 	T *getScratchBuffer(size_t count)
@@ -806,7 +817,7 @@ public:
 	static std::vector<std::string> getConstants(StackType);
 
 	// Default shader code (a shader is always required internally.)
-	static Shader::ShaderSource defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
+	static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
 
 protected:
 
@@ -868,6 +879,8 @@ protected:
 		}
 	};
 
+	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;
 
 	virtual void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) = 0;
@@ -926,6 +939,8 @@ private:
 
 	std::vector<uint8> scratchBuffer;
 
+	std::unordered_map<std::string, ShaderStage *> cachedShaderStages[ShaderStage::STAGE_MAX_ENUM];
+
 	static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
 	static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
 

+ 11 - 175
src/modules/graphics/Shader.cpp

@@ -29,103 +29,6 @@
 // C++
 #include <string>
 
-// TODO: Use love.graphics to determine actual limits?
-static const TBuiltInResource defaultTBuiltInResource = {
-	/* .MaxLights = */ 32,
-	/* .MaxClipPlanes = */ 6,
-	/* .MaxTextureUnits = */ 32,
-	/* .MaxTextureCoords = */ 32,
-	/* .MaxVertexAttribs = */ 64,
-	/* .MaxVertexUniformComponents = */ 16384,
-	/* .MaxVaryingFloats = */ 128,
-	/* .MaxVertexTextureImageUnits = */ 32,
-	/* .MaxCombinedTextureImageUnits = */ 80,
-	/* .MaxTextureImageUnits = */ 32,
-	/* .MaxFragmentUniformComponents = */ 16384,
-	/* .MaxDrawBuffers = */ 8,
-	/* .MaxVertexUniformVectors = */ 4096,
-	/* .MaxVaryingVectors = */ 32,
-	/* .MaxFragmentUniformVectors = */ 4096,
-	/* .MaxVertexOutputVectors = */ 32,
-	/* .MaxFragmentInputVectors = */ 31,
-	/* .MinProgramTexelOffset = */ -8,
-	/* .MaxProgramTexelOffset = */ 7,
-	/* .MaxClipDistances = */ 8,
-	/* .MaxComputeWorkGroupCountX = */ 65535,
-	/* .MaxComputeWorkGroupCountY = */ 65535,
-	/* .MaxComputeWorkGroupCountZ = */ 65535,
-	/* .MaxComputeWorkGroupSizeX = */ 1024,
-	/* .MaxComputeWorkGroupSizeY = */ 1024,
-	/* .MaxComputeWorkGroupSizeZ = */ 64,
-	/* .MaxComputeUniformComponents = */ 1024,
-	/* .MaxComputeTextureImageUnits = */ 32,
-	/* .MaxComputeImageUniforms = */ 16,
-	/* .MaxComputeAtomicCounters = */ 4096,
-	/* .MaxComputeAtomicCounterBuffers = */ 8,
-	/* .MaxVaryingComponents = */ 128,
-	/* .MaxVertexOutputComponents = */ 128,
-	/* .MaxGeometryInputComponents = */ 128,
-	/* .MaxGeometryOutputComponents = */ 128,
-	/* .MaxFragmentInputComponents = */ 128,
-	/* .MaxImageUnits = */ 192,
-	/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
-	/* .MaxCombinedShaderOutputResources = */ 144,
-	/* .MaxImageSamples = */ 32,
-	/* .MaxVertexImageUniforms = */ 16,
-	/* .MaxTessControlImageUniforms = */ 16,
-	/* .MaxTessEvaluationImageUniforms = */ 16,
-	/* .MaxGeometryImageUniforms = */ 16,
-	/* .MaxFragmentImageUniforms = */ 16,
-	/* .MaxCombinedImageUniforms = */ 80,
-	/* .MaxGeometryTextureImageUnits = */ 16,
-	/* .MaxGeometryOutputVertices = */ 256,
-	/* .MaxGeometryTotalOutputComponents = */ 1024,
-	/* .MaxGeometryUniformComponents = */ 1024,
-	/* .MaxGeometryVaryingComponents = */ 64,
-	/* .MaxTessControlInputComponents = */ 128,
-	/* .MaxTessControlOutputComponents = */ 128,
-	/* .MaxTessControlTextureImageUnits = */ 16,
-	/* .MaxTessControlUniformComponents = */ 1024,
-	/* .MaxTessControlTotalOutputComponents = */ 4096,
-	/* .MaxTessEvaluationInputComponents = */ 128,
-	/* .MaxTessEvaluationOutputComponents = */ 128,
-	/* .MaxTessEvaluationTextureImageUnits = */ 16,
-	/* .MaxTessEvaluationUniformComponents = */ 1024,
-	/* .MaxTessPatchComponents = */ 120,
-	/* .MaxPatchVertices = */ 32,
-	/* .MaxTessGenLevel = */ 64,
-	/* .MaxViewports = */ 16,
-	/* .MaxVertexAtomicCounters = */ 4096,
-	/* .MaxTessControlAtomicCounters = */ 4096,
-	/* .MaxTessEvaluationAtomicCounters = */ 4096,
-	/* .MaxGeometryAtomicCounters = */ 4096,
-	/* .MaxFragmentAtomicCounters = */ 4096,
-	/* .MaxCombinedAtomicCounters = */ 4096,
-	/* .MaxAtomicCounterBindings = */ 8,
-	/* .MaxVertexAtomicCounterBuffers = */ 8,
-	/* .MaxTessControlAtomicCounterBuffers = */ 8,
-	/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
-	/* .MaxGeometryAtomicCounterBuffers = */ 8,
-	/* .MaxFragmentAtomicCounterBuffers = */ 8,
-	/* .MaxCombinedAtomicCounterBuffers = */ 8,
-	/* .MaxAtomicCounterBufferSize = */ 16384,
-	/* .MaxTransformFeedbackBuffers = */ 4,
-	/* .MaxTransformFeedbackInterleavedComponents = */ 64,
-	/* .MaxCullDistances = */ 8,
-	/* .MaxCombinedClipAndCullDistances = */ 8,
-	/* .MaxSamples = */ 32,
-	/* .limits = */ {
-		/* .nonInductiveForLoops = */ 1,
-		/* .whileLoops = */ 1,
-		/* .doWhileLoops = */ 1,
-		/* .generalUniformIndexing = */ 1,
-		/* .generalAttributeMatrixVectorIndexing = */ 1,
-		/* .generalVaryingIndexing = */ 1,
-		/* .generalSamplerIndexing = */ 1,
-		/* .generalVariableIndexing = */ 1,
-		/* .generalConstantMatrixVectorIndexing = */ 1,
-	}};
-
 namespace love
 {
 namespace graphics
@@ -136,17 +39,15 @@ love::Type Shader::type("Shader", &Object::type);
 Shader *Shader::current = nullptr;
 Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
 
-Shader::Shader(const ShaderSource &source)
-	: shaderSource(source)
+Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
+	: stages()
 {
-	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-	if (gfx == nullptr)
-		throw love::Exception("love.graphics must be initialized to create a Shader.");
-
-	bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES;
 	std::string err;
-	if (!validate(gfx, gles, source, false, err))
+	if (!validate(vertex, pixel, err))
 		throw love::Exception("%s", err.c_str());
+
+	stages[ShaderStage::STAGE_VERTEX] = vertex;
+	stages[ShaderStage::STAGE_PIXEL] = pixel;
 }
 
 Shader::~Shader()
@@ -225,62 +126,15 @@ void Shader::checkMainTexture(Texture *tex) const
 	checkMainTextureType(tex->getTextureType(), tex->getDepthSampleMode().hasValue);
 }
 
-bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
+bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err)
 {
-	if (source.vertex.empty() && source.pixel.empty())
-	{
-		err = "Error validating shader: no source code!";
-		return false;
-	}
-
-	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
-
-	int defaultversion = gles ? 100 : 120;
-	EProfile defaultprofile = ENoProfile;
-
-	glslang::TShader vshader(EShLangVertex);
-	glslang::TShader pshader(EShLangFragment);
-
-	// TProgram must be destroyed before TShader.
 	glslang::TProgram program;
 
-	auto addshader = [&](glslang::TShader &s, const std::string &src, ShaderStage stage) -> bool
-	{
-		if (src.empty())
-			return true;
-
-		const char *csrc = src.c_str();
-		int srclen = (int) src.length();
-		s.setStringsWithLengths(&csrc, &srclen, 1);
-
-		bool forcedefault = false;
-		if (src.find("#define LOVE_GLSL1_ON_GLSL3") != std::string::npos)
-			forcedefault = true;
-
-		bool forwardcompat = supportsGLSL3 && !forcedefault;
-
-		if (!s.parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
-		{
-			const char *stagename = "unknown";
-			getConstant(stage, stagename);
-			err = "Error validating " + std::string(stagename) + " shader:\n\n"
-				+ std::string(s.getInfoLog()) + "\n" + std::string(s.getInfoDebugLog());
-			return false;
-		}
+	if (vertex != nullptr)
+		program.addShader(vertex->getGLSLangShader());
 
-		program.addShader(&s);
-		return true;
-	};
-
-	const ShaderSource &defaults = gfx->getCurrentDefaultShaderCode();
-	const std::string &vertcode = (checkWithDefaults && source.vertex.empty()) ? defaults.vertex : source.vertex;
-	const std::string &pixcode = (checkWithDefaults && source.pixel.empty()) ? defaults.pixel : source.pixel;
-
-	if (!addshader(vshader, vertcode, STAGE_VERTEX))
-		return false;
-
-	if (!addshader(pshader, pixcode, STAGE_PIXEL))
-		return false;
+	if (pixel != nullptr)
+		program.addShader(pixel->getGLSLangShader());
 
 	if (!program.link(EShMsgDefault))
 	{
@@ -311,16 +165,6 @@ bool Shader::getConstant(Language in, const char *&out)
 	return languages.find(in, out);
 }
 
-bool Shader::getConstant(const char *in, ShaderStage &out)
-{
-	return stageNames.find(in, out);
-}
-
-bool Shader::getConstant(ShaderStage in, const char *&out)
-{
-	return stageNames.find(in, out);
-}
-
 bool Shader::getConstant(const char *in, BuiltinUniform &out)
 {
 	return builtinNames.find(in, out);
@@ -341,14 +185,6 @@ StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEn
 
 StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries));
 
-StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM>::Entry Shader::stageNameEntries[] =
-{
-	{ "vertex", STAGE_VERTEX },
-	{ "pixel",  STAGE_PIXEL  },
-};
-
-StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM> Shader::stageNames(Shader::stageNameEntries, sizeof(Shader::stageNameEntries));
-
 StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
 {
 	{ "MainTex",             BUILTIN_TEXTURE_MAIN                  },

+ 4 - 33
src/modules/graphics/Shader.h

@@ -24,6 +24,7 @@
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "Texture.h"
+#include "ShaderStage.h"
 
 // STL
 #include <string>
@@ -59,13 +60,6 @@ public:
 		LANGUAGE_MAX_ENUM
 	};
 
-	enum ShaderStage
-	{
-		STAGE_VERTEX,
-		STAGE_PIXEL,
-		STAGE_MAX_ENUM
-	};
-
 	// Built-in uniform variables.
 	enum BuiltinUniform
 	{
@@ -103,12 +97,6 @@ public:
 		STANDARD_MAX_ENUM
 	};
 
-	struct ShaderSource
-	{
-		std::string vertex;
-		std::string pixel;
-	};
-
 	struct MatrixSize
 	{
 		short columns;
@@ -150,7 +138,7 @@ public:
 	// Pointer to the default Shader.
 	static Shader *standardShaders[STANDARD_MAX_ENUM];
 
-	Shader(const ShaderSource &source);
+	Shader(ShaderStage *vertex, ShaderStage *pixel);
 	virtual ~Shader();
 
 	/**
@@ -197,7 +185,7 @@ public:
 
 	virtual ptrdiff_t getHandle() const = 0;
 
-	static bool validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err);
+	static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
 
 	static bool initialize();
 	static void deinitialize();
@@ -205,34 +193,17 @@ public:
 	static bool getConstant(const char *in, Language &out);
 	static bool getConstant(Language in, const char *&out);
 
-	static bool getConstant(const char *in, ShaderStage &out);
-	static bool getConstant(ShaderStage in, const char *&out);
-
 	static bool getConstant(const char *in, BuiltinUniform &out);
 	static bool getConstant(BuiltinUniform in, const char *&out);
 
 protected:
 
-	struct CachedShaderStage
-	{
-		int referenceCount;
-		ShaderStage stage;
-		glslang::TShader *glslangShader;
-		ptrdiff_t handle;
-	};
-
-	// Source code used for this Shader.
-	ShaderSource shaderSource;
-
-	static std::map<std::string, CachedShaderStage> cachedShaders;
+	StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
 
 private:
 
 	static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];
 	static StringMap<Language, LANGUAGE_MAX_ENUM> languages;
-
-	static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[];
-	static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames;
 	
 	// Names for the built-in uniform variables.
 	static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];

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

@@ -0,0 +1,205 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#include "ShaderStage.h"
+#include "common/Exception.h"
+#include "Graphics.h"
+
+#include "libraries/glslang/glslang/Public/ShaderLang.h"
+
+// TODO: Use love.graphics to determine actual limits?
+static const TBuiltInResource defaultTBuiltInResource = {
+	/* .MaxLights = */ 32,
+	/* .MaxClipPlanes = */ 6,
+	/* .MaxTextureUnits = */ 32,
+	/* .MaxTextureCoords = */ 32,
+	/* .MaxVertexAttribs = */ 64,
+	/* .MaxVertexUniformComponents = */ 16384,
+	/* .MaxVaryingFloats = */ 128,
+	/* .MaxVertexTextureImageUnits = */ 32,
+	/* .MaxCombinedTextureImageUnits = */ 80,
+	/* .MaxTextureImageUnits = */ 32,
+	/* .MaxFragmentUniformComponents = */ 16384,
+	/* .MaxDrawBuffers = */ 8,
+	/* .MaxVertexUniformVectors = */ 4096,
+	/* .MaxVaryingVectors = */ 32,
+	/* .MaxFragmentUniformVectors = */ 4096,
+	/* .MaxVertexOutputVectors = */ 32,
+	/* .MaxFragmentInputVectors = */ 31,
+	/* .MinProgramTexelOffset = */ -8,
+	/* .MaxProgramTexelOffset = */ 7,
+	/* .MaxClipDistances = */ 8,
+	/* .MaxComputeWorkGroupCountX = */ 65535,
+	/* .MaxComputeWorkGroupCountY = */ 65535,
+	/* .MaxComputeWorkGroupCountZ = */ 65535,
+	/* .MaxComputeWorkGroupSizeX = */ 1024,
+	/* .MaxComputeWorkGroupSizeY = */ 1024,
+	/* .MaxComputeWorkGroupSizeZ = */ 64,
+	/* .MaxComputeUniformComponents = */ 1024,
+	/* .MaxComputeTextureImageUnits = */ 32,
+	/* .MaxComputeImageUniforms = */ 16,
+	/* .MaxComputeAtomicCounters = */ 4096,
+	/* .MaxComputeAtomicCounterBuffers = */ 8,
+	/* .MaxVaryingComponents = */ 128,
+	/* .MaxVertexOutputComponents = */ 128,
+	/* .MaxGeometryInputComponents = */ 128,
+	/* .MaxGeometryOutputComponents = */ 128,
+	/* .MaxFragmentInputComponents = */ 128,
+	/* .MaxImageUnits = */ 192,
+	/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
+	/* .MaxCombinedShaderOutputResources = */ 144,
+	/* .MaxImageSamples = */ 32,
+	/* .MaxVertexImageUniforms = */ 16,
+	/* .MaxTessControlImageUniforms = */ 16,
+	/* .MaxTessEvaluationImageUniforms = */ 16,
+	/* .MaxGeometryImageUniforms = */ 16,
+	/* .MaxFragmentImageUniforms = */ 16,
+	/* .MaxCombinedImageUniforms = */ 80,
+	/* .MaxGeometryTextureImageUnits = */ 16,
+	/* .MaxGeometryOutputVertices = */ 256,
+	/* .MaxGeometryTotalOutputComponents = */ 1024,
+	/* .MaxGeometryUniformComponents = */ 1024,
+	/* .MaxGeometryVaryingComponents = */ 64,
+	/* .MaxTessControlInputComponents = */ 128,
+	/* .MaxTessControlOutputComponents = */ 128,
+	/* .MaxTessControlTextureImageUnits = */ 16,
+	/* .MaxTessControlUniformComponents = */ 1024,
+	/* .MaxTessControlTotalOutputComponents = */ 4096,
+	/* .MaxTessEvaluationInputComponents = */ 128,
+	/* .MaxTessEvaluationOutputComponents = */ 128,
+	/* .MaxTessEvaluationTextureImageUnits = */ 16,
+	/* .MaxTessEvaluationUniformComponents = */ 1024,
+	/* .MaxTessPatchComponents = */ 120,
+	/* .MaxPatchVertices = */ 32,
+	/* .MaxTessGenLevel = */ 64,
+	/* .MaxViewports = */ 16,
+	/* .MaxVertexAtomicCounters = */ 4096,
+	/* .MaxTessControlAtomicCounters = */ 4096,
+	/* .MaxTessEvaluationAtomicCounters = */ 4096,
+	/* .MaxGeometryAtomicCounters = */ 4096,
+	/* .MaxFragmentAtomicCounters = */ 4096,
+	/* .MaxCombinedAtomicCounters = */ 4096,
+	/* .MaxAtomicCounterBindings = */ 8,
+	/* .MaxVertexAtomicCounterBuffers = */ 8,
+	/* .MaxTessControlAtomicCounterBuffers = */ 8,
+	/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
+	/* .MaxGeometryAtomicCounterBuffers = */ 8,
+	/* .MaxFragmentAtomicCounterBuffers = */ 8,
+	/* .MaxCombinedAtomicCounterBuffers = */ 8,
+	/* .MaxAtomicCounterBufferSize = */ 16384,
+	/* .MaxTransformFeedbackBuffers = */ 4,
+	/* .MaxTransformFeedbackInterleavedComponents = */ 64,
+	/* .MaxCullDistances = */ 8,
+	/* .MaxCombinedClipAndCullDistances = */ 8,
+	/* .MaxSamples = */ 32,
+	/* .limits = */ {
+		/* .nonInductiveForLoops = */ 1,
+		/* .whileLoops = */ 1,
+		/* .doWhileLoops = */ 1,
+		/* .generalUniformIndexing = */ 1,
+		/* .generalAttributeMatrixVectorIndexing = */ 1,
+		/* .generalVaryingIndexing = */ 1,
+		/* .generalSamplerIndexing = */ 1,
+		/* .generalVariableIndexing = */ 1,
+		/* .generalConstantMatrixVectorIndexing = */ 1,
+	}
+};
+
+namespace love
+{
+namespace graphics
+{
+
+ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey)
+	: stageType(stage)
+	, source(glsl)
+	, cacheKey(cachekey)
+	, glslangShader(nullptr)
+{
+	EShLanguage glslangStage = EShLangCount;
+	if (stage == STAGE_VERTEX)
+		glslangStage = EShLangVertex;
+	else if (stage == STAGE_PIXEL)
+		glslangStage = EShLangFragment;
+	else
+		throw love::Exception("Cannot compile shader stage: unknown stage type.");
+
+	glslangShader = new glslang::TShader(glslangStage);
+
+	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
+	int defaultversion = gles ? 100 : 120;
+	EProfile defaultprofile = ENoProfile;
+
+	const char *csrc = glsl.c_str();
+	int srclen = (int) glsl.length();
+	glslangShader->setStringsWithLengths(&csrc, &srclen, 1);
+
+	bool forcedefault = false;
+	if (source.find("#define LOVE_GLSL1_ON_GLSL3") != std::string::npos)
+		forcedefault = true;
+
+	bool forwardcompat = supportsGLSL3 && !forcedefault;
+
+	if (!glslangShader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
+	{
+		const char *stagename = "unknown";
+		getConstant(stage, stagename);
+
+		std::string err = "Error validating " + std::string(stagename) + " shader:\n\n"
+			+ std::string(glslangShader->getInfoLog()) + "\n"
+			+ std::string(glslangShader->getInfoDebugLog());
+
+		delete glslangShader;
+		throw love::Exception("%s", err.c_str());
+	}
+}
+
+ShaderStage::~ShaderStage()
+{
+	if (!cacheKey.empty())
+	{
+		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+		if (gfx != nullptr)
+			gfx->cleanupCachedShaderStage(stageType, cacheKey);
+	}
+
+	delete glslangShader;
+}
+
+bool ShaderStage::getConstant(const char *in, StageType &out)
+{
+	return stageNames.find(in, out);
+}
+
+bool ShaderStage::getConstant(StageType in, const char *&out)
+{
+	return stageNames.find(in, out);
+}
+
+StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
+{
+	{ "vertex", STAGE_VERTEX },
+	{ "pixel",  STAGE_PIXEL  },
+};
+
+StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM> ShaderStage::stageNames(ShaderStage::stageNameEntries, sizeof(ShaderStage::stageNameEntries));
+
+} // graphics
+} // love

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

@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#pragma once
+
+#include "common/Object.h"
+#include "common/StringMap.h"
+#include "Volatile.h"
+
+#include <stddef.h>
+#include <string>
+
+namespace glslang
+{
+class TShader;
+}
+
+namespace love
+{
+namespace graphics
+{
+
+class Graphics;
+
+class ShaderStage : public love::Object, public Volatile
+{
+public:
+
+	enum StageType
+	{
+		STAGE_VERTEX,
+		STAGE_PIXEL,
+		STAGE_MAX_ENUM
+	};
+
+	ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
+	virtual ~ShaderStage();
+
+	StageType getStageType() const { return stageType; }
+	const std::string &getSource() const { return source; }
+	const std::string &getWarnings() const { return warnings; }
+	glslang::TShader *getGLSLangShader() const { return glslangShader; }
+	
+	virtual ptrdiff_t getHandle() const = 0;
+
+	static bool getConstant(const char *in, StageType &out);
+	static bool getConstant(StageType in, const char *&out);
+
+protected:
+
+	std::string warnings;
+
+private:
+
+	StageType stageType;
+	std::string source;
+	std::string cacheKey;
+	glslang::TShader *glslangShader;
+
+	static StringMap<StageType, STAGE_MAX_ENUM>::Entry stageNameEntries[];
+	static StringMap<StageType, STAGE_MAX_ENUM> stageNames;
+
+}; // ShaderStage
+
+class ShaderStageForValidation final : public ShaderStage
+{
+public:
+
+	ShaderStageForValidation(Graphics *gfx, StageType stage, const std::string &glsl, bool gles)
+		: ShaderStage(gfx, stage, glsl, gles, "")
+	{}
+
+	virtual ~ShaderStageForValidation() {}
+
+	ptrdiff_t getHandle() const override { return 0; }
+	bool loadVolatile() override { return true; }
+	void unloadVolatile() override { }
+
+}; // ShaderStageForValidation
+
+} // graphics
+} // love

+ 0 - 8
src/modules/graphics/Texture.h

@@ -52,14 +52,6 @@ enum TextureType
 	TEXTURE_MAX_ENUM
 };
 
-class TextureTooLargeException : public love::Exception
-{
-public:
-	TextureTooLargeException(const char *dimname, int pix)
-		: Exception("Cannot create texture: %s of %d pixels is too large for this system.", dimname, pix)
-	{}
-};
-
 /**
  * Base class for 2D textures. All textures can be drawn with Quads, have a
  * width and height, and have filter and wrap modes.

+ 12 - 3
src/modules/graphics/opengl/Graphics.cpp

@@ -30,6 +30,7 @@
 #include "window/Window.h"
 #include "Buffer.h"
 #include "Text.h"
+#include "ShaderStage.h"
 
 #include "libraries/xxHash/xxhash.h"
 
@@ -126,9 +127,14 @@ love::graphics::Canvas *Graphics::newCanvas(const Canvas::Settings &settings)
 	return new Canvas(settings);
 }
 
-love::graphics::Shader *Graphics::newShader(const Shader::ShaderSource &source)
+love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles)
 {
-	return new Shader(source);
+	return new ShaderStage(this, stage, source, gles, cachekey);
+}
+
+love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
+{
+	return new Shader(vertex, pixel);
 }
 
 love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
@@ -281,7 +287,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 		try
 		{
 			if (!Shader::standardShaders[i])
-				Shader::standardShaders[i] = newShader(defaultShaderCode[i][target][gammacorrect]);
+			{
+				const auto &code = defaultShaderCode[i][target][gammacorrect];
+				Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
+			}
 		}
 		catch (love::Exception &)
 		{

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

@@ -68,7 +68,6 @@ public:
 	love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
 
 	love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
-	love::graphics::Shader *newShader(const Shader::ShaderSource &source) override;
 
 	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
 
@@ -126,6 +125,8 @@ public:
 
 private:
 
+	love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
+	love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
 	love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
 	void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
 	void initCapabilities() override;

+ 18 - 101
src/modules/graphics/opengl/Shader.cpp

@@ -36,8 +36,8 @@ namespace graphics
 namespace opengl
 {
 
-Shader::Shader(const ShaderSource &source)
-	: love::graphics::Shader(source)
+Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
+	: love::graphics::Shader(vertex, pixel)
 	, program(0)
 	, builtinUniforms()
 	, builtinUniformInfo()
@@ -73,71 +73,6 @@ Shader::~Shader()
 	}
 }
 
-GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
-{
-	GLenum glstage;
-	const char *typestr;
-
-	if (!getConstant(stage, typestr))
-		typestr = "";
-
-	switch (stage)
-	{
-	case STAGE_VERTEX:
-		glstage = GL_VERTEX_SHADER;
-		break;
-	case STAGE_PIXEL:
-		glstage = GL_FRAGMENT_SHADER;
-		break;
-	default:
-		throw love::Exception("Cannot create shader object: unknown shader type.");
-		break;
-	}
-
-	GLuint shaderid = glCreateShader(glstage);
-
-	if (shaderid == 0)
-	{
-		if (glGetError() == GL_INVALID_ENUM)
-			throw love::Exception("Cannot create %s shader object: %s shaders not supported.", typestr, typestr);
-		else
-			throw love::Exception("Cannot create %s shader object.", typestr);
-	}
-
-	const char *src = code.c_str();
-	GLint srclen = (GLint) code.length();
-	glShaderSource(shaderid, 1, (const GLchar **)&src, &srclen);
-
-	glCompileShader(shaderid);
-
-	GLint infologlen;
-	glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
-
-	// Get any warnings the shader compiler may have produced.
-	if (infologlen > 0)
-	{
-		GLchar *infolog = new GLchar[infologlen];
-		glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
-
-		// Save any warnings for later querying.
-		shaderWarnings[stage] = infolog;
-
-		delete[] infolog;
-	}
-
-	GLint status;
-	glGetShaderiv(shaderid, GL_COMPILE_STATUS, &status);
-
-	if (status == GL_FALSE)
-	{
-		glDeleteShader(shaderid);
-		throw love::Exception("Cannot compile %s shader code:\n%s",
-		                      typestr, shaderWarnings[stage].c_str());
-	}
-
-	return shaderid;
-}
-
 void Shader::mapActiveUniforms()
 {
 	// Built-in uniform locations default to -1 (nonexistent.)
@@ -376,38 +311,22 @@ bool Shader::loadVolatile()
 	textureUnits.clear();
 	textureUnits.push_back(TextureUnit());
 
-	std::vector<GLuint> shaderids;
-
-	auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
-	const ShaderSource &defaults = gfx->getCurrentDefaultShaderCode();
-
-	// The shader program must have both vertex and pixel shader stages.
-	const std::string &vertexcode = shaderSource.vertex.empty() ? defaults.vertex : shaderSource.vertex;
-	const std::string &pixelcode = shaderSource.pixel.empty() ? defaults.pixel : shaderSource.pixel;
-
-	try
+	for (const auto &stage : stages)
 	{
-		shaderids.push_back(compileCode(STAGE_VERTEX, vertexcode));
-		shaderids.push_back(compileCode(STAGE_PIXEL, pixelcode));
-	}
-	catch (love::Exception &)
-	{
-		for (GLuint id : shaderids)
-			glDeleteShader(id);
-		throw;
+		if (stage.get() != nullptr)
+			stage->loadVolatile();
 	}
 
 	program = glCreateProgram();
 
 	if (program == 0)
-	{
-		for (GLuint id : shaderids)
-			glDeleteShader(id);
 		throw love::Exception("Cannot create shader program object.");
-	}
 
-	for (GLuint id : shaderids)
-		glAttachShader(program, id);
+	for (const auto &stage : stages)
+	{
+		if (stage.get() != nullptr)
+			glAttachShader(program, (GLuint) stage->getHandle());
+	}
 
 	// Bind generic vertex attribute indices to names in the shader.
 	for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
@@ -419,10 +338,6 @@ bool Shader::loadVolatile()
 
 	glLinkProgram(program);
 
-	// Flag shaders for auto-deletion when the program object is deleted.
-	for (GLuint id : shaderids)
-		glDeleteShader(id);
-
 	GLint status;
 	glGetProgramiv(program, GL_LINK_STATUS, &status);
 
@@ -477,8 +392,6 @@ void Shader::unloadVolatile()
 	// And the locations of any built-in uniform variables.
 	for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
 		builtinUniforms[i] = -1;
-
-	shaderWarnings.clear();
 }
 
 std::string Shader::getProgramWarnings() const
@@ -506,11 +419,15 @@ std::string Shader::getWarnings() const
 	std::string warnings;
 	const char *stagestr;
 
-	// Get the individual shader stage warnings
-	for (const auto &warning : shaderWarnings)
+	for (const auto &stage : stages)
 	{
-		if (getConstant(warning.first, stagestr))
-			warnings += std::string(stagestr) + std::string(" shader:\n") + warning.second;
+		if (stage.get() == nullptr)
+			continue;
+
+		const std::string &stagewarnings = stage->getWarnings();
+
+		if (ShaderStage::getConstant(stage->getStageType(), stagestr))
+			warnings += std::string(stagestr) + std::string(" shader:\n") + stagewarnings;
 	}
 
 	warnings += getProgramWarnings();

+ 1 - 7
src/modules/graphics/opengl/Shader.h

@@ -47,8 +47,7 @@ public:
 	 * Creates a new Shader using a list of source codes.
 	 * Source must contain either vertex or pixel shader code, or both.
 	 **/
-	Shader(const ShaderSource &source);
-
+	Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel);
 	virtual ~Shader();
 
 	// Implements Volatile
@@ -96,16 +95,11 @@ private:
 	TextureType getUniformTextureType(GLenum type) const;
 	bool isDepthTextureType(GLenum type) const;
 
-	GLuint compileCode(ShaderStage stage, const std::string &code);
-
 	void flushStreamDraws() const;
 
 	// Get any warnings or errors generated only by the shader program object.
 	std::string getProgramWarnings() const;
 
-	// Shader compiler warning strings for individual shader stages.
-	std::map<ShaderStage, std::string> shaderWarnings;
-
 	// volatile
 	GLuint program;
 

+ 105 - 0
src/modules/graphics/opengl/ShaderStage.cpp

@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#include "ShaderStage.h"
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+
+ShaderStage::ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey)
+	: love::graphics::ShaderStage(gfx, stage, source, gles, cachekey)
+	, glShader(0)
+{
+	loadVolatile();
+}
+
+ShaderStage::~ShaderStage()
+{
+	unloadVolatile();
+}
+
+bool ShaderStage::loadVolatile()
+{
+	if (glShader != 0)
+		return true;
+
+	StageType stage = getStageType();
+	const char *typestr = "unknown";
+	getConstant(stage, typestr);
+
+	GLenum glstage = 0;
+	if (stage == STAGE_VERTEX)
+		glstage = GL_VERTEX_SHADER;
+	else if (stage == STAGE_PIXEL)
+		glstage = GL_FRAGMENT_SHADER;
+	else
+		throw love::Exception("%s shader stage is not handled in OpenGL backend code.", typestr);
+
+	glShader = glCreateShader(glstage);
+
+	if (glShader == 0)
+		throw love::Exception("Cannot create OpenGL %s shader object.", typestr);
+
+	const std::string &sourcestring = getSource();
+	const char *src = sourcestring.c_str();
+	GLint srclen = (GLint) sourcestring.length();
+
+	glShaderSource(glShader, 1, (const GLchar **)&src, &srclen);
+	glCompileShader(glShader);
+
+	GLint infologlen;
+	glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &infologlen);
+
+	if (infologlen > 0)
+	{
+		GLchar *infolog = new GLchar[infologlen];
+		glGetShaderInfoLog(glShader, infologlen, nullptr, infolog);
+
+		warnings = infolog;
+		delete[] infolog;
+	}
+
+	GLint status = GL_FALSE;
+	glGetShaderiv(glShader, GL_COMPILE_STATUS, &status);
+
+	if (glShader == GL_FALSE)
+	{
+		glDeleteShader(glShader);
+		throw love::Exception("Cannot compile %s shader code:\n%s", typestr, warnings.c_str());
+	}
+
+	return true;
+}
+
+void ShaderStage::unloadVolatile()
+{
+	if (glShader != 0)
+		glDeleteShader(glShader);
+
+	glShader = 0;
+}
+
+} // opengl
+} // graphics
+} // love

+ 55 - 0
src/modules/graphics/opengl/ShaderStage.h

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#pragma once
+
+#include "graphics/ShaderStage.h"
+#include "graphics/Volatile.h"
+#include "OpenGL.h"
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+
+class ShaderStage final : public love::graphics::ShaderStage
+{
+public:
+
+	ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey);
+	virtual ~ShaderStage();
+
+	ptrdiff_t getHandle() const override { return glShader; }
+
+	// Implements Volatile.
+	bool loadVolatile() override;
+	void unloadVolatile() override;
+
+private:
+
+	GLuint glShader;
+
+}; // ShaderStage
+
+} // opengl
+} // graphics
+} // love

+ 30 - 23
src/modules/graphics/wrap_Graphics.cpp

@@ -1234,7 +1234,7 @@ int w_newCanvas(lua_State *L)
 	return 1;
 }
 
-static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::ShaderSource &source)
+static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
 {
 	using namespace love::filesystem;
 
@@ -1315,17 +1315,17 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::Shad
 
 	// vertex shader code
 	if (lua_isstring(L, -2))
-		source.vertex = luax_checkstring(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))
-		source.pixel = luax_checkstring(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 (source.vertex.empty() && source.pixel.empty())
+	if (vertexsource.empty() && pixelsource.empty())
 	{
 		// Original args had source code, but effectCodeToGLSL couldn't translate it
 		for (int i = startidx; i < startidx + 2; i++)
@@ -1342,13 +1342,13 @@ int w_newShader(lua_State *L)
 {
 	bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
 
-	Shader::ShaderSource source;
-	w_getShaderSource(L, 1, gles, source);
+	std::string vertexsource, pixelsource;
+	w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
 
 	bool should_error = false;
 	try
 	{
-		Shader *shader = instance()->newShader(source);
+		Shader *shader = instance()->newShader(vertexsource, pixelsource);
 		luax_pushtype(L, shader);
 		shader->release();
 	}
@@ -1372,11 +1372,20 @@ int w_validateShader(lua_State *L)
 {
 	bool gles = luax_checkboolean(L, 1);
 
-	Shader::ShaderSource source;
-	w_getShaderSource(L, 2, gles, source);
+	std::string vertexsource, pixelsource;
+	w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
 
+	bool success = true;
 	std::string err;
-	bool success = instance()->validateShader(gles, source, err);
+	try
+	{
+		success = instance()->validateShader(gles, vertexsource, pixelsource, err);
+	}
+	catch (love::Exception &e)
+	{
+		success = false;
+		err = e.what();
+	}
 
 	luax_pushboolean(L, success);
 
@@ -2007,23 +2016,21 @@ int w_setDefaultShaderCode(lua_State *L)
 			lua_getfield(L, -3, "videopixel");
 			lua_getfield(L, -4, "arraypixel");
 
-			Shader::ShaderSource code;
-			code.vertex = luax_checkstring(L, -4);
-			code.pixel = luax_checkstring(L, -3);
+			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);
 
-			Shader::ShaderSource videocode;
-			videocode.vertex = luax_checkstring(L, -4);
-			videocode.pixel = luax_checkstring(L, -2);
+			lua_pop(L, 5);
 
-			Shader::ShaderSource arraycode;
-			arraycode.vertex = luax_checkstring(L, -4);
-			arraycode.pixel = luax_checkstring(L, -1);
+			Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
+			Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
 
-			lua_pop(L, 5);
+			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_DEFAULT][lang][i] = code;
-			Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i] = videocode;
-			Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i] = arraycode;
+			Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
+			Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
 		}
 	}