GrSkSLFP: Avoid making a global copy of inColor most of the time
We still need *a* copy, to satisfy SkSL that mutates the input color
parameter, but it almost never needs to be global, unless there are
calls to sample() from a function other than main.
With that tweak, we generate much better SkSL/GLSL code from every one
of our effects, avoiding the proliferation of global variables.
Bug: skia:12140
Change-Id: I5c3bd42df34a095969abcbbc60e69c653a08087a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/424299
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index 88880f4..71073b6 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -220,10 +220,11 @@
private:
enum Flags {
- kUsesSampleCoords_Flag = 0x1,
- kAllowColorFilter_Flag = 0x2,
- kAllowShader_Flag = 0x4,
- kAllowBlender_Flag = 0x8,
+ kUsesSampleCoords_Flag = 0x1,
+ kAllowColorFilter_Flag = 0x2,
+ kAllowShader_Flag = 0x4,
+ kAllowBlender_Flag = 0x8,
+ kSamplesOutsideMain_Flag = 0x10,
};
SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,
@@ -243,10 +244,11 @@
SkSL::ProgramKind kind);
uint32_t hash() const { return fHash; }
- bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
- bool allowShader() const { return (fFlags & kAllowShader_Flag); }
- bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
- bool allowBlender() const { return (fFlags & kAllowBlender_Flag); }
+ bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
+ bool allowShader() const { return (fFlags & kAllowShader_Flag); }
+ bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
+ bool allowBlender() const { return (fFlags & kAllowBlender_Flag); }
+ bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); }
const SkFilterColorProgram* getFilterColorProgram();
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 7d62bc7..b08515f 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -206,6 +206,10 @@
SkASSERT(!SkSL::Analysis::ReferencesFragCoords(*program));
}
+ if (SkSL::Analysis::CallsSampleOutsideMain(*program)) {
+ flags |= kSamplesOutsideMain_Flag;
+ }
+
size_t offset = 0;
std::vector<Uniform> uniforms;
std::vector<Child> children;
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index 9a1694b..1bd7a3d 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -171,10 +171,18 @@
// we call child processors (particularly from helper functions, which can't "see" the
// parameter to main). Even from within main, if the code mutates the parameter, calls to
// sample should still be passing the original color (by default).
- GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
- kHalf4_GrSLType);
- args.fFragBuilder->declareGlobal(inputColorCopy);
- args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorCopy.c_str(), args.fInputColor);
+ SkString inputColorName;
+ if (fp.fEffect->samplesOutsideMain()) {
+ GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
+ kHalf4_GrSLType);
+ args.fFragBuilder->declareGlobal(inputColorCopy);
+ inputColorName = inputColorCopy.getName();
+ args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorName.c_str(), args.fInputColor);
+ } else {
+ inputColorName = args.fFragBuilder->newTmpVarName("inColor");
+ args.fFragBuilder->codeAppendf(
+ "half4 %s = %s;\n", inputColorName.c_str(), args.fInputColor);
+ }
// Copy the incoming coords to a local variable. Code in main might modify the coords
// parameter. fSampleCoord could be a varying, so writes to it would be illegal.
@@ -198,7 +206,7 @@
FPCallbacks callbacks(this,
args,
- inputColorCopy.c_str(),
+ inputColorName.c_str(),
*program.fContext,
fp.uniformData(),
fp.uniformFlags());
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index 96dc90f..4865c5d 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -151,6 +151,30 @@
using INHERITED = ProgramVisitor;
};
+// Visitor that searches for calls to sample() from a function other than main()
+class SampleOutsideMainVisitor : public ProgramVisitor {
+public:
+ SampleOutsideMainVisitor() {}
+
+ bool visitExpression(const Expression& e) override {
+ if (e.is<FunctionCall>()) {
+ const FunctionDeclaration& f = e.as<FunctionCall>().function();
+ if (f.isBuiltin() && f.name() == "sample") {
+ return true;
+ }
+ }
+ return INHERITED::visitExpression(e);
+ }
+
+ bool visitProgramElement(const ProgramElement& p) override {
+ return p.is<FunctionDefinition>() &&
+ !p.as<FunctionDefinition>().declaration().isMain() &&
+ INHERITED::visitProgramElement(p);
+ }
+
+ using INHERITED = ProgramVisitor;
+};
+
// Visitor that counts the number of nodes visited
class NodeCountVisitor : public ProgramVisitor {
public:
@@ -592,6 +616,11 @@
return Analysis::ReferencesBuiltin(program, SK_FRAGCOORD_BUILTIN);
}
+bool Analysis::CallsSampleOutsideMain(const Program& program) {
+ SampleOutsideMainVisitor visitor;
+ return visitor.visit(program);
+}
+
int Analysis::NodeCountUpToLimit(const FunctionDefinition& function, int limit) {
return NodeCountVisitor{limit}.visit(*function.body());
}
diff --git a/src/sksl/SkSLAnalysis.h b/src/sksl/SkSLAnalysis.h
index 937bee4..596bfcb 100644
--- a/src/sksl/SkSLAnalysis.h
+++ b/src/sksl/SkSLAnalysis.h
@@ -50,6 +50,8 @@
static bool ReferencesSampleCoords(const Program& program);
static bool ReferencesFragCoords(const Program& program);
+ static bool CallsSampleOutsideMain(const Program& program);
+
static int NodeCountUpToLimit(const FunctionDefinition& function, int limit);
/**