From 184ef184209d42c06f04a5a3162bd76a1a184be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Fri, 9 Jun 2017 19:19:43 +0200 Subject: [PATCH] Add optimizing AT_LIGHT_PASS builtin to canvas shaders This one allows for complex shaders paired with a simple lighting shader to skip code that would otherwise be pointlessly (and wastefully) run during the light pass. You can use `if (AT_LIGHT_PASS) , negated or not, and that will be converted to a preprocessed #if when the shader is compiled. Depending on your game (number of items and lights), this can be a *significant* performance gain, or at least avoids relying on the driver's optimizing abilities. --- drivers/gles2/shader_compiler_gles2.cpp | 58 ++++++++++++++++++++++--- drivers/gles2/shader_compiler_gles2.h | 2 + drivers/gles2/shaders/canvas.glsl | 6 +++ servers/visual/shader_language.cpp | 2 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 56e1777ef82..eeb373af09f 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -502,16 +502,31 @@ String ShaderCompilerGLES2::dump_node_code(SL::Node *p_node, int p_level, bool p SL::ControlFlowNode *cfnode = (SL::ControlFlowNode *)p_node; if (cfnode->flow_op == SL::FLOW_OP_IF) { - code += "if (" + dump_node_code(cfnode->statements[0], p_level) + ") {" ENDL; - code += dump_node_code(cfnode->statements[1], p_level + 1); - if (cfnode->statements.size() == 3) { + // Help lazy drivers + if (!_is_condition_preprocessable(cfnode->statements[0])) { - code += "} else {" ENDL; - code += dump_node_code(cfnode->statements[2], p_level + 1); + code += "if (" + dump_node_code(cfnode->statements[0], p_level) + ") {" ENDL; + code += dump_node_code(cfnode->statements[1], p_level + 1); + if (cfnode->statements.size() == 3) { + + code += "} else {" ENDL; + code += dump_node_code(cfnode->statements[2], p_level + 1); + } + + code += "}" ENDL; + } else { + + code += "#if (" + dump_node_code(cfnode->statements[0], p_level) + ")" ENDL; + code += dump_node_code(cfnode->statements[1], p_level); + if (cfnode->statements.size() == 3) { + + code += "#else" ENDL; + code += dump_node_code(cfnode->statements[2], p_level); + } + + code += "#endif" ENDL; } - code += "}" ENDL; - } else if (cfnode->flow_op == SL::FLOW_OP_RETURN) { if (cfnode->statements.size()) { @@ -649,6 +664,33 @@ String ShaderCompilerGLES2::replace_string(const StringName &p_string) { return "_" + p_string.operator String(); } +bool ShaderCompilerGLES2::_is_condition_preprocessable(ShaderLanguage::Node *p_condition) const { + + // Check if this is a "(!)variable" expression + + SL::Node *maybe_variable; + if (p_condition->type == SL::Node::TYPE_OPERATOR) { + SL::OperatorNode *op = (SL::OperatorNode *)p_condition; + if (op->op != SL::OP_NOT) + return false; + maybe_variable = op->arguments[0]; + } else { + maybe_variable = p_condition; + } + + if (maybe_variable->type != SL::Node::TYPE_VARIABLE) + return false; + + SL::VariableNode *variable = (SL::VariableNode *)maybe_variable; + // Hardcoding since it's currently the only; + // if more were added, they would be better characterized + // in the various ShaderLanguage::BuiltinsDef + if (variable->name != String("AT_LIGHT_PASS")) + return false; + + return true; +} + Error ShaderCompilerGLES2::compile(const String &p_code, ShaderLanguage::ShaderType p_type, String &r_code_line, String &r_globals_line, Flags &r_flags, Map *r_uniforms) { uses_texscreen = false; @@ -873,6 +915,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["PROJECTION_MATRIX"] = "projection_matrix"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["EXTRA_MATRIX"] = "extra_matrix"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["TIME"] = "time"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["AT_LIGHT_PASS"] = "at_light_pass"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["POSITION"] = "(gl_FragCoord.xy)"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["NORMAL"] = "normal"; @@ -888,6 +931,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["SCREEN_UV"] = "screen_uv"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["POINT_COORD"] = "gl_PointCoord"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["TIME"] = "time"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["AT_LIGHT_PASS"] = "at_light_pass"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["POSITION"] = "(gl_FragCoord.xy)"; mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["NORMAL"] = "normal"; diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h index 02f9e58978e..b71eb1fb5d0 100644 --- a/drivers/gles2/shader_compiler_gles2.h +++ b/drivers/gles2/shader_compiler_gles2.h @@ -44,6 +44,8 @@ private: Error compile_node(ShaderLanguage::ProgramNode *p_program); static Error create_glsl_120_code(void *p_str, ShaderLanguage::ProgramNode *p_program); + bool _is_condition_preprocessable(ShaderLanguage::Node *p_condition) const; + bool uses_light; bool uses_texscreen; bool uses_texpos; diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 5f4767940d4..3ffa2537995 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -39,6 +39,9 @@ uniform vec2 normal_flip; varying highp vec2 pos; #endif +#define at_light_pass 1 +#else +#define at_light_pass 0 #endif #if defined(ENABLE_VAR1_INTERP) @@ -176,6 +179,9 @@ uniform float shadow_esm_multiplier; #endif +#define at_light_pass 1 +#else +#define at_light_pass 0 #endif #if defined(USE_TEXPIXEL_SIZE) diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 6fe071f43a5..6f6a14dc765 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -1124,6 +1124,7 @@ const ShaderLanguage::BuiltinsDef ShaderLanguage::ci_vertex_builtins_defs[] = { { "PROJECTION_MATRIX", TYPE_MAT4 }, { "EXTRA_MATRIX", TYPE_MAT4 }, { "TIME", TYPE_FLOAT }, + { "AT_LIGHT_PASS", TYPE_BOOL }, { NULL, TYPE_VOID }, }; const ShaderLanguage::BuiltinsDef ShaderLanguage::ci_fragment_builtins_defs[] = { @@ -1145,6 +1146,7 @@ const ShaderLanguage::BuiltinsDef ShaderLanguage::ci_fragment_builtins_defs[] = // { "SCREEN_POS", TYPE_VEC2}, // { "SCREEN_TEXEL_SIZE", TYPE_VEC2}, { "TIME", TYPE_FLOAT }, + { "AT_LIGHT_PASS", TYPE_BOOL }, { NULL, TYPE_VOID } };