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>
This commit is contained in:
Brian Osman 2021-07-02 13:25:35 -04:00 committed by Skia Commit-Bot
parent e9ab391765
commit 04d79fc594
5 changed files with 58 additions and 13 deletions

View File

@ -220,10 +220,11 @@ public:
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 @@ private:
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();

View File

@ -206,6 +206,10 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(std::unique_ptr<SkSL::Program> pro
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;

View File

@ -171,10 +171,18 @@ public:
// 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 @@ public:
FPCallbacks callbacks(this,
args,
inputColorCopy.c_str(),
inputColorName.c_str(),
*program.fContext,
fp.uniformData(),
fp.uniformFlags());

View File

@ -151,6 +151,30 @@ public:
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 @@ bool Analysis::ReferencesFragCoords(const Program& program) {
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());
}

View File

@ -50,6 +50,8 @@ struct Analysis {
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);
/**