Browse Source

Add 'vertexmain' and 'pixelmain' shader entry points.

These are low level 'raw' entry points which don't declare any inputs or outputs themselves. They require GLSL 3 to use.
Alex Szpakowski 4 years ago
parent
commit
2a5a90ccef
3 changed files with 98 additions and 37 deletions
  1. 2 2
      src/modules/graphics/Graphics.cpp
  2. 87 33
      src/modules/graphics/Shader.cpp
  3. 9 2
      src/modules/graphics/Shader.h

+ 2 - 2
src/modules/graphics/Graphics.cpp

@@ -268,7 +268,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
 			if (!validstages[i])
 				continue;
 
-			if (info.isStage[i])
+			if (info.stages[i] != Shader::ENTRYPOINT_NONE)
 			{
 				isanystage = true;
 				stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
@@ -347,7 +347,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
 			if (!validstages[i])
 				continue;
 
-			if (info.isStage[i])
+			if (info.stages[i] != Shader::ENTRYPOINT_NONE)
 			{
 				isanystage = true;
 				std::string glsl = Shader::createShaderStageCode(this, stype, source, info);

+ 87 - 33
src/modules/graphics/Shader.cpp

@@ -38,6 +38,7 @@ namespace graphics
 
 namespace glsl
 {
+
 static const char global_syntax[] = R"(
 #if !defined(GL_ES) && __VERSION__ < 140
 	#define lowp
@@ -275,6 +276,16 @@ void main() {
 }
 )";
 
+static const char vertex_main_raw[] = R"(
+void vertexmain();
+
+void main() {
+	love_initializeBuiltinUniforms();
+	setPointSize();
+	vertexmain();
+}
+)";
+
 static const char pixel_header[] = R"(
 #ifdef GL_ES
 	precision mediump float;
@@ -284,29 +295,10 @@ static const char pixel_header[] = R"(
 
 #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))
@@ -335,6 +327,12 @@ vec4 VideoTexel(vec2 texcoords) {
 )";
 
 static const char pixel_main[] = R"(
+#if __VERSION__ >= 130
+	layout(location = 0) out vec4 love_PixelColor;
+#else
+	#define love_PixelColor gl_FragColor
+#endif
+
 uniform sampler2D MainTex;
 varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
 varying mediump vec4 VaryingColor;
@@ -348,6 +346,30 @@ void main() {
 )";
 
 static const char pixel_main_custom[] = R"(
+#if __VERSION__ >= 130
+	// 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_Canvases love_RenderTargets
+#ifdef LOVE_MULTI_RENDER_TARGETS
+#define LOVE_MULTI_CANVASES 1
+#endif
+
 varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
 varying mediump vec4 VaryingColor;
 
@@ -359,6 +381,15 @@ void main() {
 }
 )";
 
+static const char pixel_main_raw[] = R"(
+void pixelmain();
+
+void main() {
+	love_initializeBuiltinUniforms();
+	pixelmain();
+}
+)";
+
 struct StageInfo
 {
 	const char *name;
@@ -366,12 +397,13 @@ struct StageInfo
 	const char *functions;
 	const char *main;
 	const char *main_custom;
+	const char *main_raw;
 };
 
 static const StageInfo stageInfo[] =
 {
-	{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main },
-	{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom },
+	{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main, vertex_main_raw },
+	{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom, pixel_main_raw },
 };
 
 static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
@@ -400,30 +432,38 @@ static Shader::Language getTargetLanguage(const std::string &src)
 	return lang;
 }
 
-static bool isVertexCode(const std::string &src)
+static Shader::EntryPoint getVertexEntryPoint(const std::string &src)
 {
-	std::regex r("vec4\\s+position\\s*\\(");
 	std::smatch m;
-	return std::regex_search(src, m, r);
+
+	if (std::regex_search(src, m, std::regex("void\\s+vertexmain\\s*\\(")))
+		return Shader::ENTRYPOINT_RAW;
+
+	if (std::regex_search(src, m, std::regex("vec4\\s+position\\s*\\(")))
+		return Shader::ENTRYPOINT_HIGHLEVEL;
+
+	return Shader::ENTRYPOINT_NONE;
 }
 
-static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt)
+static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt)
 {
-	custompixel = false;
 	mrt = false;
 	std::smatch m;
+
+	if (std::regex_search(src, m, std::regex("void\\s+pixelmain\\s*\\(")))
+		return Shader::ENTRYPOINT_RAW;
+
 	if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\(")))
-		return true;
+		return Shader::ENTRYPOINT_HIGHLEVEL;
 
 	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 Shader::ENTRYPOINT_CUSTOM;
 	}
 
-	return false;
+	return Shader::ENTRYPOINT_NONE;
 }
 
 } // glsl
@@ -439,8 +479,8 @@ 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);
+	info.stages[ShaderStage::STAGE_VERTEX] = glsl::getVertexEntryPoint(src);
+	info.stages[ShaderStage::STAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT);
 	return info;
 }
 
@@ -449,6 +489,12 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
 	if (info.language == Shader::LANGUAGE_MAX_ENUM)
 		throw love::Exception("Invalid shader language");
 
+	if (info.stages[stage] == ENTRYPOINT_NONE)
+		throw love::Exception("Cannot find entry point for shader stage.");
+
+	if (info.stages[stage] == ENTRYPOINT_RAW && info.language == LANGUAGE_GLSL1)
+		throw love::Exception("Shaders using a raw entry point must use GLSL 3 or greater.");
+
 	const auto &features = gfx->getCapabilities().features;
 
 	if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
@@ -481,7 +527,15 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
 	ss << glsl::global_uniforms;
 	ss << glsl::global_functions;
 	ss << stageinfo.functions;
-	ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main);
+
+	if (info.stages[stage] == ENTRYPOINT_HIGHLEVEL)
+		ss << stageinfo.main;
+	else if (info.stages[stage] == ENTRYPOINT_CUSTOM)
+		ss << stageinfo.main_custom;
+	else if (info.stages[stage] == ENTRYPOINT_RAW)
+		ss << stageinfo.main_raw;
+	else
+		throw love::Exception("Unknown shader entry point %d", info.stages[stage]);
 	ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n");
 	ss << code;
 

+ 9 - 2
src/modules/graphics/Shader.h

@@ -90,11 +90,18 @@ public:
 		STANDARD_MAX_ENUM
 	};
 
+	enum EntryPoint
+	{
+		ENTRYPOINT_NONE,
+		ENTRYPOINT_HIGHLEVEL,
+		ENTRYPOINT_CUSTOM,
+		ENTRYPOINT_RAW,
+	};
+
 	struct SourceInfo
 	{
 		Language language;
-		bool isStage[ShaderStage::STAGE_MAX_ENUM];
-		bool customPixelFunction;
+		EntryPoint stages[ShaderStage::STAGE_MAX_ENUM];
 		bool usesMRT;
 	};