From 2a5a90ccef1286a8018f082831cfcdcdd8cf0504 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 27 Dec 2020 19:17:47 -0400 Subject: [PATCH] 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. --- src/modules/graphics/Graphics.cpp | 4 +- src/modules/graphics/Shader.cpp | 120 ++++++++++++++++++++++-------- src/modules/graphics/Shader.h | 11 ++- 3 files changed, 98 insertions(+), 37 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index fe2ae7339..f0c2cdfd7 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -268,7 +268,7 @@ Shader *Graphics::newShader(const std::vector &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 &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); diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index c45689e9c..c64770640 100644 --- a/src/modules/graphics/Shader.cpp +++ b/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; diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 2c4717626..d75a6100c 100644 --- a/src/modules/graphics/Shader.h +++ b/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; };