Emit helper function in GenerateDefaultGlueCode when children exist.

A runtime effect needs to be able to invoke a child snippet from
a `child.eval()` expression anywhere. Because `eval` occurs in an
expression context, we don't have the ability to emit multiple
statements, or declare new variables. That means all of our glue code
needs to eventually become glue-expressions.

When there are no children, the default glue code is just a function
call to a snippet, so this will actually work fine as an expression.
However, when there _are_ children, the glue code becomes arbitrarily
complex. (We need to call child snippets, which could themselves call
more children.)

Now, when children exist, the default glue code emits a helper
function which invokes all of the children first, followed by
invoking the shader snippet passing along all the child outputs.
This gives us some of that Ganesh-like tree structure when
it's needed, but simple flat code when it's not.

Change-Id: I85e42f0794872c9f378ec63581364e0ed55f6047
Bug: skia:13508
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/558308
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2022-07-14 12:30:29 -04:00 committed by SkCQ
parent cd938c5b0f
commit 5556c2ca9a

View File

@ -187,11 +187,8 @@ std::string SkShaderInfo::toSkSL() const {
int binding = 0;
preamble += skgpu::graphite::GetMtlTexturesAndSamplers(fBlockReaders, &binding);
std::string mainBody = "void main() {\n";
if (this->needsLocalCoords()) {
mainBody += " const float4x4 initialPreLocal = float4x4(1);\n";
}
std::string mainBody = "void main() {\n"
" const float4x4 initialPreLocal = float4x4(1.0);\n";
std::string parentPreLocal = "initialPreLocal";
std::string lastOutputVar = "initialColor";
@ -347,7 +344,7 @@ static std::string append_default_snippet_arguments(const SkShaderSnippet* entry
}
#endif
// The default glue code just calls a helper function with the signature:
// The default glue code just calls a built-in function with the signature:
// half4 BuiltinFunctionName(/* all uniforms as parameters */);
// and stores the result in a variable named "resultName".
void GenerateDefaultGlueCode(const SkShaderInfo& shaderInfo,
@ -377,10 +374,12 @@ void GenerateDefaultGlueCode(const SkShaderInfo& shaderInfo,
#endif // defined(SK_GRAPHITE_ENABLED) && defined(SK_ENABLE_SKSL)
}
// The default-with-children glue code just calls a helper function with the signature:
// half4 BuiltinFunctionName(/* all uniforms as parameters */,
// /* all child output variable names as parameters */);
// and stores the result in a variable named "resultName".
// The default-with-children glue code creates a function in the preamble with a signature of:
// half4 BuiltinFunctionName_N(half4 inColor, float4x4 preLocal) { ... }
// This function invokes each child in sequence, and then calls the built-in function, passing all
// uniforms and child outputs along:
// half4 BuiltinFunctionName(/* all uniforms as parameters */,
// /* all child output variable names as parameters */);
void GenerateDefaultGlueCodeWithChildren(const SkShaderInfo& shaderInfo,
const std::string& resultName,
int* entryIndex,
@ -400,22 +399,38 @@ void GenerateDefaultGlueCodeWithChildren(const SkShaderInfo& shaderInfo,
SkASSERT(entry->fUniforms.front().type() == SkSLType::kFloat4x4);
}
// Invoke all children ahead of this entry's glue code.
// Create a helper function that invokes each of the children, then calls the snippet.
int curEntryIndex = *entryIndex;
std::string helperFnName = get_mangled_name(entry->fStaticFunctionName, curEntryIndex);
std::string helperFn = SkSL::String::printf("half4 %s(half4 inColor, float4x4 preLocal) {",
helperFnName.c_str());
// Invoke all children from inside the helper function.
std::vector<std::string> childOutputVarNames = emit_child_glue_code(shaderInfo,
entryIndex,
priorStageOutputName,
currentPreLocalName,
"inColor",
"preLocal",
preamble,
mainBody,
indent);
&helperFn,
/*indent=*/0);
SkASSERT((int)childOutputVarNames.size() == entry->fNumChildren);
// Finally, invoke the snippet from the helper function, passing uniforms and child outputs.
SkSL::String::appendf(&helperFn, " return %s", entry->fStaticFunctionName);
helperFn += append_default_snippet_arguments(entry, curEntryIndex, "preLocal",
childOutputVarNames);
helperFn += ";\n"
"}\n";
// Add the helper function to the bottom of the preamble.
*preamble += helperFn;
// Invoke the helper function from the main body.
add_indent(mainBody, indent);
SkSL::String::appendf(mainBody, "%s = %s", resultName.c_str(), entry->fStaticFunctionName);
*mainBody += append_default_snippet_arguments(entry, curEntryIndex, currentPreLocalName,
childOutputVarNames);
*mainBody += ";\n";
SkSL::String::appendf(mainBody,
"%s = %s(%s, %s);",
resultName.c_str(),
helperFnName.c_str(),
priorStageOutputName.c_str(),
currentPreLocalName.c_str());
#endif // defined(SK_GRAPHITE_ENABLED) && defined(SK_ENABLE_SKSL)
}
@ -612,8 +627,14 @@ public:
void defineFunction(const char* decl, const char* body, bool isMain) override {
if (isMain) {
SkSL::String::appendf(fPreamble, "half4 %s_%d(float2 coords, half4 inColor) {\n%s}\n",
kRuntimeShaderName, fEntryIndex, body);
SkSL::String::appendf(fPreamble,
"half4 %s_%d(float4x4 preLocal, half4 inColor) {\n"
" float2 coords=(preLocal * dev2LocalUni * sk_FragCoord).xy;\n"
"%s"
"}\n",
kRuntimeShaderName,
fEntryIndex,
body);
} else {
SkSL::String::appendf(fPreamble, "%s {\n%s}\n", decl, body);
}
@ -695,8 +716,7 @@ void GenerateRuntimeShaderGlueCode(const SkShaderInfo& shaderInfo,
SkASSERT(reader.entry()->fUniforms[0].type() == SkSLType::kFloat4x4);
add_indent(mainBody, indent);
SkSL::String::appendf(mainBody,
"%s = %s_%d((%s * dev2LocalUni * sk_FragCoord).xy, (%s));\n",
SkSL::String::appendf(mainBody, "%s = %s_%d(%s, %s);\n",
resultName.c_str(),
entry->fName, *entryIndex,
currentPreLocalName.c_str(),