From 4790c6a06072814b21f5b3f24b53c6936915308d Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 16 Mar 2021 18:51:34 +0200 Subject: [PATCH] Support inlining GLSL functions with parameters --- source/glsl/optimize.cpp | 33 +++++++++++++++++++---- source/glsl/optimize.h | 6 ++--- tests/glsl/function_arguments_inline.glsl | 21 +++++++++++++++ tests/glsl/function_overloading.glsl | 12 +++------ 4 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 tests/glsl/function_arguments_inline.glsl diff --git a/source/glsl/optimize.cpp b/source/glsl/optimize.cpp index 0d428149..7d605f1f 100644 --- a/source/glsl/optimize.cpp +++ b/source/glsl/optimize.cpp @@ -34,8 +34,12 @@ void InlineableFunctionLocator::visit(FunctionCall &call) void InlineableFunctionLocator::visit(FunctionDeclaration &func) { + bool has_out_params = false; + for(NodeArray::const_iterator i=func.parameters.begin(); (!has_out_params && i!=func.parameters.end()); ++i) + has_out_params = ((*i)->interface=="out"); + unsigned &count = refcounts[func.definition]; - if(count<=1 && func.parameters.empty()) + if(count<=1 && !has_out_params) inlineable.insert(func.definition); SetForScope set(current_function, &func); @@ -69,9 +73,9 @@ InlineContentInjector::InlineContentInjector(): pass(DEPENDS) { } -const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionDeclaration &src) +const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &target_func, Block &tgt_blk, const NodeList::iterator &ins_pt, FunctionCall &call) { - source_func = &src; + source_func = call.declaration->definition; // Collect all declarations the inlined function depends on. pass = DEPENDS; @@ -89,7 +93,22 @@ const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &ta staging_block.variables.clear(); remap_prefix = source_func->name; - for(NodeList::iterator i=src.body.body.begin(); i!=src.body.body.end(); ++i) + std::vector > params; + params.reserve(source_func->parameters.size()); + for(NodeArray::iterator i=source_func->parameters.begin(); i!=source_func->parameters.end(); ++i) + { + RefPtr var = (*i)->clone(); + var->interface.clear(); + + SetForScope set_pass(pass, RENAME); + var->visit(*this); + + staging_block.body.push_back(0); + staging_block.body.back() = var; + params.push_back(var); + } + + for(NodeList::iterator i=source_func->body.body.begin(); i!=source_func->body.body.end(); ++i) { r_inlined_statement = 0; (*i)->visit(*this); @@ -117,6 +136,10 @@ const string &InlineContentInjector::apply(Stage &stage, FunctionDeclaration &ta remap_prefix = target_func.name; target_func.visit(*this); + // Put the argument expressions in place after all renaming has been done. + for(unsigned i=0; iparameters.size(); ++i) + params[i]->init_expression = call.arguments[i]->clone(); + tgt_blk.body.splice(ins_pt, staging_block.body); NodeReorderer().apply(stage, target_func, dependencies); @@ -258,7 +281,7 @@ void FunctionInliner::visit(FunctionCall &call) if(def && inlineable.count(def)) { - string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, *def); + string result_name = InlineContentInjector().apply(*stage, *current_function, *current_block, insert_point, call); // This will later get removed by UnusedVariableRemover. if(result_name.empty()) diff --git a/source/glsl/optimize.h b/source/glsl/optimize.h index f6604743..6d4b1bcb 100644 --- a/source/glsl/optimize.h +++ b/source/glsl/optimize.h @@ -10,8 +10,8 @@ namespace GL { namespace SL { /** Finds functions which are candidates for inlining. Currently this means -functions which have no parameters, contain no more than one return statement, -and are only called once. */ +functions which have no flow control statements, no more than one return +statement, and are only called once. */ class InlineableFunctionLocator: private TraversingVisitor { private: @@ -59,7 +59,7 @@ private: public: InlineContentInjector(); - const std::string &apply(Stage &, FunctionDeclaration &, Block &, const NodeList::iterator &, FunctionDeclaration &); + const std::string &apply(Stage &, FunctionDeclaration &, Block &, const NodeList::iterator &, FunctionCall &); private: virtual void visit(VariableReference &); diff --git a/tests/glsl/function_arguments_inline.glsl b/tests/glsl/function_arguments_inline.glsl new file mode 100644 index 00000000..f9946c9c --- /dev/null +++ b/tests/glsl/function_arguments_inline.glsl @@ -0,0 +1,21 @@ +uniform mat4 mvp; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +vec4 transform_position(vec4 p) +{ + return mvp*p; +} +void main() +{ + gl_Position = transform_position(position); +} + +/* Expected output: vertex +uniform mat4 mvp; +layout(location=0) in vec4 position; +void main() +{ + gl_Position = mvp*position; +} +*/ diff --git a/tests/glsl/function_overloading.glsl b/tests/glsl/function_overloading.glsl index 636db511..f74a793b 100644 --- a/tests/glsl/function_overloading.glsl +++ b/tests/glsl/function_overloading.glsl @@ -43,18 +43,12 @@ void main() /* Expected output: fragment uniform sampler2D tex; -vec3 srgb_to_linear(vec3 color) -{ - return mix(color.rgb/12.92, pow((color.rgb+0.055)/1.055, vec3(2.4)), lessThan(color, vec3(0.04045))); -} -vec4 srgb_to_linear(vec4 color) -{ - return vec4(srgb_to_linear(color.rgb), color.a); -} layout(location=0) out vec4 frag_color; in vec2 _vs_out_texcoord; void main() { - frag_color = srgb_to_linear(texture(tex, _vs_out_texcoord)); + vec4 color = texture(tex, _vs_out_texcoord); + vec3 _srgb_to_linear_color = color.rgb; + frag_color = vec4(mix(_srgb_to_linear_color.rgb/12.92, pow((_srgb_to_linear_color.rgb+0.055)/1.055, vec3(2.4)), lessThan(_srgb_to_linear_color, vec3(0.04045))), color.a); } */ -- 2.43.0