Write SkSL visitor pattern and consolidate sampling analysis

Besides moving the existing coord-overrides and sample matrix merging
logic on to a shared visitor pattern, this updates the sample coord
reference detection to actually look for references to that built-in.

Previously, we only had this behavior in the CPP code generator. The
.h generator just did a string search for sk_TransformedCoords2D, and
runtime effects just looked at the main signature. Now, the .h generator
is more robust, and runtime effects that declare the main coords parameter
but never use it will not be marked as using the sample coords directly.

Change-Id: I802d610dbda512cf3823c58f349307b3926aa58f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299458
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2020-06-29 17:27:00 -04:00 committed by Skia Commit-Bot
parent 95c250c247
commit 8f3a83671a
10 changed files with 449 additions and 382 deletions

View File

@ -12,6 +12,8 @@ skia_sksl_sources = [
"$_src/sksl/SkSLASTFile.h",
"$_src/sksl/SkSLASTNode.cpp",
"$_src/sksl/SkSLASTNode.h",
"$_src/sksl/SkSLAnalysis.cpp",
"$_src/sksl/SkSLAnalysis.h",
"$_src/sksl/SkSLByteCode.cpp",
"$_src/sksl/SkSLByteCode.h",
"$_src/sksl/SkSLByteCodeGenerator.cpp",

View File

@ -12,9 +12,6 @@
namespace SkSL {
struct Program;
struct Variable;
/**
* Represents the matrix applied to a fragment processor by its parent's sample(child, matrix) call.
*/
@ -49,8 +46,6 @@ struct SampleMatrix {
return SampleMatrix(Kind::kVariable, "", hasPerspective);
}
static SampleMatrix Make(const Program& program, const Variable& fp);
SampleMatrix merge(const SampleMatrix& other);
bool operator==(const SampleMatrix& other) const {

View File

@ -20,6 +20,7 @@
#include "src/core/SkUtils.h"
#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
@ -87,21 +88,7 @@ SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
}
SkASSERT(!compiler->errorCount());
// FIXME can the SkSL::Program just provide this for us?
bool mainHasSampleCoords = false;
for (const auto& e : *program) {
if (e.fKind == SkSL::ProgramElement::kFunction_Kind) {
const SkSL::FunctionDefinition& func = (const SkSL::FunctionDefinition&) e;
if (func.fDeclaration.fName == "main") {
SkASSERT(func.fDeclaration.fParameters.size() <= 2);
if (!func.fDeclaration.fParameters.empty() &&
func.fDeclaration.fParameters.front()->fType.fName == "float2") {
mainHasSampleCoords = true;
break;
}
}
}
}
bool mainHasSampleCoords = SkSL::Analysis::ReferencesSampleCoords(*program);
size_t offset = 0, uniformSize = 0;
std::vector<Variable> inAndUniformVars;
@ -151,7 +138,8 @@ SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
if (var.fModifiers.fFlags & flag) {
if (&var.fType == ctx.fFragmentProcessor_Type.get()) {
children.push_back(var.fName);
sampleMatrices.push_back(SkSL::SampleMatrix::Make(*program, var));
sampleMatrices.push_back(
SkSL::Analysis::GetSampleMatrix(*program, var));
continue;
}

367
src/sksl/SkSLAnalysis.cpp Normal file
View File

@ -0,0 +1,367 @@
/*
* Copyright 2020 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/SkSLAnalysis.h"
#include "include/private/SkSLSampleMatrix.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLProgramElement.h"
#include "src/sksl/ir/SkSLStatement.h"
// ProgramElements
#include "src/sksl/ir/SkSLEnum.h"
#include "src/sksl/ir/SkSLExtension.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLInterfaceBlock.h"
#include "src/sksl/ir/SkSLModifiers.h"
#include "src/sksl/ir/SkSLSection.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
// Statements
#include "src/sksl/ir/SkSLBlock.h"
#include "src/sksl/ir/SkSLBreakStatement.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLIfStatement.h"
#include "src/sksl/ir/SkSLNop.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLVarDeclarationsStatement.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
// Expressions
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLExternalFunctionCall.h"
#include "src/sksl/ir/SkSLExternalValueReference.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
#include "src/sksl/ir/SkSLFloatLiteral.h"
#include "src/sksl/ir/SkSLFunctionCall.h"
#include "src/sksl/ir/SkSLFunctionReference.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLIntLiteral.h"
#include "src/sksl/ir/SkSLNullLiteral.h"
#include "src/sksl/ir/SkSLPostfixExpression.h"
#include "src/sksl/ir/SkSLPrefixExpression.h"
#include "src/sksl/ir/SkSLSetting.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLTypeReference.h"
#include "src/sksl/ir/SkSLVariableReference.h"
namespace SkSL {
namespace {
static bool is_sample_call_to_fp(const FunctionCall& fc, const Variable& fp) {
const FunctionDeclaration& f = fc.fFunction;
return f.fBuiltin && f.fName == "sample" && fc.fArguments.size() >= 1 &&
fc.fArguments[0]->fKind == Expression::kVariableReference_Kind &&
&((VariableReference&) *fc.fArguments[0]).fVariable == &fp;
}
// Visitor that determines the merged SampleMatrix for a given child 'fp' in the program.
class MergeSampleMatrixVisitor : public ProgramVisitor {
public:
MergeSampleMatrixVisitor(const Variable& fp) : fFP(fp) {}
SampleMatrix visit(const Program& program) {
fMatrix = SampleMatrix(); // reset to none
this->INHERITED::visit(program);
return fMatrix;
}
protected:
const Variable& fFP;
SampleMatrix fMatrix;
bool visitExpression(const Expression& e) override {
// Looking for sample(fp, inColor?, float3x3)
if (e.fKind == Expression::kFunctionCall_Kind) {
const FunctionCall& fc = (const FunctionCall&) e;
if (is_sample_call_to_fp(fc, fFP) && fc.fArguments.size() >= 2 &&
fc.fArguments.back()->fType == *this->program().fContext->fFloat3x3_Type) {
// Determine the type of matrix for this call site, then merge it with the
// previously accumulated matrix state.
if (fc.fArguments.back()->isConstantOrUniform()) {
if (fc.fArguments.back()->fKind == Expression::Kind::kVariableReference_Kind ||
fc.fArguments.back()->fKind == Expression::Kind::kConstructor_Kind) {
// FIXME if this is a constant, we should parse the float3x3 constructor and
// determine if the resulting matrix introduces perspective.
fMatrix.merge(SampleMatrix::MakeConstUniform(
fc.fArguments.back()->description()));
} else {
// FIXME this is really to workaround a restriction of the downstream code
// that relies on the SampleMatrix's fExpression to identify uniform names.
// Once they are tracked separately, any constant/uniform expression can
// work, but right now this avoids issues from '0.5 * matrix' that is both
// a constant AND a uniform.
fMatrix.merge(SampleMatrix::MakeVariable());
}
} else {
fMatrix.merge(SampleMatrix::MakeVariable());
}
// NOTE: we don't return true here just because we found a sample matrix usage,
// we need to process the entire program and merge across all encountered calls.
}
}
return this->INHERITED::visitExpression(e);
}
typedef ProgramVisitor INHERITED;
};
// Visitor that searches a program for sample() calls with the given 'fp' as the argument
// and returns true if explicit float2 coords were passed to that call site.
class ExplicitCoordsVisitor : public ProgramVisitor {
public:
ExplicitCoordsVisitor(const Variable& fp) : fFP(fp) {}
protected:
bool visitExpression(const Expression& e) override {
// Looking for sample(fp, inColor?, float2)
if (e.fKind == Expression::kFunctionCall_Kind) {
const FunctionCall& fc = (const FunctionCall&) e;
if (is_sample_call_to_fp(fc, fFP) && fc.fArguments.size() >= 2 &&
fc.fArguments.back()->fType == *this->program().fContext->fFloat2_Type) {
return true;
}
}
return this->INHERITED::visitExpression(e);
}
const Variable& fFP;
typedef ProgramVisitor INHERITED;
};
// Visitor that searches through a main function of the program for reference to the
// sample coordinates provided by the parent FP or main program.
class SampleCoordsVisitor : public ProgramVisitor {
protected:
// Only bother recursing through the main function for the sample coord builtin
bool visitProgramElement(const ProgramElement& pe) override {
if (pe.fKind == ProgramElement::kFunction_Kind &&
((const FunctionDefinition&) pe).fDeclaration.fName == "main") {
return this->INHERITED::visitProgramElement(pe);
}
// No recursion, but returning false will allow visitor to continue to siblings
return false;
}
bool visitExpression(const Expression& e) override {
if (e.fKind == Expression::kVariableReference_Kind) {
// For SkRuntimeEffects
const VariableReference& var = (const VariableReference&) e;
return var.fVariable.fModifiers.fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
} else if (e.fKind == Expression::kIndex_Kind) {
// For .fp files that use sk_TransformedCoords2D[0] for the time being
const IndexExpression& index = (const IndexExpression&) e;
if (index.fBase->fKind == Expression::kVariableReference_Kind) {
const VariableReference& base = (const VariableReference&) *index.fBase;
if (base.fVariable.fModifiers.fLayout.fBuiltin == SK_TRANSFORMEDCOORDS2D_BUILTIN) {
SkASSERT(index.fIndex->fKind == Expression::kIntLiteral_Kind &&
((IntLiteral&) *index.fIndex).fValue == 0);
return true;
}
}
}
return this->INHERITED::visitExpression(e);
}
typedef ProgramVisitor INHERITED;
};
}
////////////////////////////////////////////////////////////////////////////////
// Analysis
SampleMatrix Analysis::GetSampleMatrix(const Program& program, const Variable& fp) {
MergeSampleMatrixVisitor visitor(fp);
return visitor.visit(program);
}
bool Analysis::IsExplicitlySampled(const Program& program, const Variable& fp) {
ExplicitCoordsVisitor visitor(fp);
return visitor.visit(program);
}
bool Analysis::ReferencesSampleCoords(const Program& program) {
SampleCoordsVisitor visitor;
return visitor.visit(program);
}
////////////////////////////////////////////////////////////////////////////////
// ProgramVisitor
bool ProgramVisitor::visit(const Program& program) {
fProgram = &program;
bool result = false;
for (const auto& pe : program) {
if (this->visitProgramElement(pe)) {
result = true;
break;
}
}
fProgram = nullptr;
return result;
}
bool ProgramVisitor::visitExpression(const Expression& e) {
switch(e.fKind) {
case Expression::kBoolLiteral_Kind:
case Expression::kDefined_Kind:
case Expression::kExternalValue_Kind:
case Expression::kFieldAccess_Kind:
case Expression::kFloatLiteral_Kind:
case Expression::kFunctionReference_Kind:
case Expression::kIntLiteral_Kind:
case Expression::kNullLiteral_Kind:
case Expression::kSetting_Kind:
case Expression::kTypeReference_Kind:
case Expression::kVariableReference_Kind:
// Leaf expressions return false
return false;
case Expression::kBinary_Kind: {
const BinaryExpression& b = (const BinaryExpression&) e;
return this->visitExpression(*b.fLeft) || this->visitExpression(*b.fRight); }
case Expression::kConstructor_Kind: {
const Constructor& c = (const Constructor&) e;
for (const auto& arg : c.fArguments) {
if (this->visitExpression(*arg)) { return true; }
}
return false; }
case Expression::kExternalFunctionCall_Kind: {
const ExternalFunctionCall& c = (const ExternalFunctionCall&) e;
for (const auto& arg : c.fArguments) {
if (this->visitExpression(*arg)) { return true; }
}
return false; }
case Expression::kFunctionCall_Kind: {
const FunctionCall& c = (const FunctionCall&) e;
for (const auto& arg : c.fArguments) {
if (this->visitExpression(*arg)) { return true; }
}
return false; }
case Expression::kIndex_Kind:{
const IndexExpression& i = (const IndexExpression&) e;
return this->visitExpression(*i.fBase) || this->visitExpression(*i.fIndex); }
case Expression::kPostfix_Kind:
return this->visitExpression(*((const PostfixExpression&) e).fOperand);
case Expression::kPrefix_Kind:
return this->visitExpression(*((const PrefixExpression&) e).fOperand);
case Expression::kSwizzle_Kind:
return this->visitExpression(*((const Swizzle&) e).fBase);
case Expression::kTernary_Kind: {
const TernaryExpression& t = (const TernaryExpression&) e;
return this->visitExpression(*t.fTest) ||
this->visitExpression(*t.fIfTrue) ||
this->visitExpression(*t.fIfFalse); }
default:
SkUNREACHABLE;
}
}
bool ProgramVisitor::visitStatement(const Statement& s) {
switch(s.fKind) {
case Statement::kBreak_Kind:
case Statement::kContinue_Kind:
case Statement::kDiscard_Kind:
case Statement::kNop_Kind:
// Leaf statements just return false
return false;
case Statement::kBlock_Kind:
for (const auto& s : ((const Block&) s).fStatements) {
if (this->visitStatement(*s)) { return true; }
}
return false;
case Statement::kDo_Kind: {
const DoStatement& d = (const DoStatement&) s;
return this->visitExpression(*d.fTest) || this->visitStatement(*d.fStatement); }
case Statement::kExpression_Kind:
return this->visitExpression(*((const ExpressionStatement&) s).fExpression);
case Statement::kFor_Kind: {
const ForStatement& f = (const ForStatement&) s;
return (f.fInitializer && this->visitStatement(*f.fInitializer)) ||
(f.fInitializer && this->visitExpression(*f.fTest)) ||
(f.fNext && this->visitExpression(*f.fNext)) ||
this->visitStatement(*f.fStatement); }
case Statement::kIf_Kind: {
const IfStatement& i = (const IfStatement&) s;
return this->visitExpression(*i.fTest) ||
this->visitStatement(*i.fIfTrue) ||
(i.fIfFalse && this->visitStatement(*i.fIfFalse)); }
case Statement::kReturn_Kind: {
const ReturnStatement& r = (const ReturnStatement&) s;
return r.fExpression && this->visitExpression(*r.fExpression); }
case Statement::kSwitch_Kind: {
const SwitchStatement& sw = (const SwitchStatement&) s;
if (this->visitExpression(*sw.fValue)) { return true; }
for (const auto& c : sw.fCases) {
if (c->fValue && this->visitExpression(*c->fValue)) { return true; }
for (const auto& st : c->fStatements) {
if (this->visitStatement(*st)) { return true; }
}
}
return false; }
case Statement::kVarDeclaration_Kind: {
const VarDeclaration& v = (const VarDeclaration&) s;
for (const auto& s : v.fSizes) {
if (this->visitExpression(*s)) { return true; }
}
return v.fValue && this->visitExpression(*v.fValue); }
case Statement::kVarDeclarations_Kind: {
// Technically this statement points to a program element, but it's convenient
// to have program element > statement > expression, so visit the declaration elements
// directly without going up to visitProgramElement.
const VarDeclarations& vars = *((const VarDeclarationsStatement&) s).fDeclaration;
for (const auto& v : vars.fVars) {
if (this->visitStatement(*v)) { return true; }
}
return false;
}
case Statement::kWhile_Kind: {
const WhileStatement& w = (const WhileStatement&) s;
return this->visitExpression(*w.fTest) || this->visitStatement(*w.fStatement); }
default:
SkUNREACHABLE;
}
}
bool ProgramVisitor::visitProgramElement(const ProgramElement& pe) {
switch(pe.fKind) {
case ProgramElement::kEnum_Kind:
case ProgramElement::kExtension_Kind:
case ProgramElement::kModifiers_Kind:
case ProgramElement::kSection_Kind:
// Leaf program elements just return false by default
return false;
case ProgramElement::kFunction_Kind:
return this->visitStatement(*((const FunctionDefinition&) pe).fBody);
case ProgramElement::kInterfaceBlock_Kind:
for (const auto& e : ((const InterfaceBlock&) pe).fSizes) {
if (this->visitExpression(*e)) { return true; }
}
return false;
case ProgramElement::kVar_Kind:
for (const auto& v : ((const VarDeclarations&) pe).fVars) {
if (this->visitStatement(*v)) { return true; }
}
return false;
default:
SkUNREACHABLE;
}
}
}

70
src/sksl/SkSLAnalysis.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkSLAnalysis_DEFINED
#define SkSLAnalysis_DEFINED
#include "include/private/SkSLSampleMatrix.h"
#include "src/sksl/SkSLDefines.h"
namespace SkSL {
struct Expression;
struct Program;
struct ProgramElement;
struct Statement;
struct Variable;
/**
* Provides utilities for analyzing SkSL statically before it's composed into a full program.
*/
struct Analysis {
static SampleMatrix GetSampleMatrix(const Program& program, const Variable& fp);
static bool IsExplicitlySampled(const Program& program, const Variable& fp);
static bool ReferencesSampleCoords(const Program& program);
};
/**
* Utility class to visit every element, statement, and expression in an SkSL program IR.
* This is intended for simple analysis and accumulation, where custom visitation behavior is only
* needed for a limited set of expression kinds.
*
* Subclasses should override visitExpression/visitStatement/visitProgramElement as needed and
* intercept elements of interest. They can then invoke the base class's function to visit all
* sub expressions. They can also choose not to call the base function to arrest recursion, or
* implement custom recursion.
*
* The visit functions return a bool that determines how the default implementation recurses. Once
* any visit call returns true, the default behavior stops recursing and propagates true up the
* stack.
*/
class ProgramVisitor {
public:
virtual ~ProgramVisitor() { SkASSERT(!fProgram); }
bool visit(const Program&);
protected:
const Program& program() const {
SkASSERT(fProgram);
return *fProgram;
}
virtual bool visitExpression(const Expression&);
virtual bool visitStatement(const Statement&);
virtual bool visitProgramElement(const ProgramElement&);
private:
const Program* fProgram;
};
}
#endif

View File

@ -8,6 +8,7 @@
#include "src/sksl/SkSLCPPCodeGenerator.h"
#include "include/private/SkSLSampleMatrix.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLCPPUniformCTypes.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLHCodeGenerator.h"
@ -447,7 +448,7 @@ void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
} else if (c.fArguments.back()->fType.name() == "float3x3") {
// Invoking child with a matrix, sampling relative to the input coords.
invokeFunction = "invokeChildWithMatrix";
SampleMatrix matrix = SampleMatrix::Make(fProgram, child);
SampleMatrix matrix = Analysis::GetSampleMatrix(fProgram, child);
if (!matrix.isConstUniform()) {
inputCoord = "_matrix" + to_string(c.fOffset);

View File

@ -8,6 +8,7 @@
#include "src/sksl/SkSLHCodeGenerator.h"
#include "include/private/SkSLSampleMatrix.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLParser.h"
#include "src/sksl/SkSLUtil.h"
#include "src/sksl/ir/SkSLEnum.h"
@ -273,8 +274,7 @@ void HCodeGenerator::writeConstructor() {
this->writef(" {\n");
this->writeSection(CONSTRUCTOR_CODE_SECTION);
int usesSampleCoordsDirectly = fProgram.fSource->find("sk_TransformedCoords", 0);
if (usesSampleCoordsDirectly >= 0) {
if (Analysis::ReferencesSampleCoords(fProgram)) {
this->writef(" this->setUsesSampleCoordsDirectly();\n");
}
@ -289,8 +289,8 @@ void HCodeGenerator::writeConstructor() {
this->writef(" SkASSERT(%s);", String(param->fName).c_str());
}
bool explicitCoords = fSectionAndParameterHelper.hasCoordOverrides(*param);
SampleMatrix matrix = SampleMatrix::Make(fProgram, *param);
bool explicitCoords = Analysis::IsExplicitlySampled(fProgram, *param);
SampleMatrix matrix = Analysis::GetSampleMatrix(fProgram, *param);
String registerFunc;
String matrixArg;

View File

@ -50,187 +50,4 @@ SampleMatrix SampleMatrix::merge(const SampleMatrix& other) {
return *this;
}
struct SampleMatrixExtractor {
SampleMatrixExtractor(const Program& program, const Variable& fp)
: fProgram(program), fFP(fp) {}
SampleMatrix getMatrix(const Expression&) const;
SampleMatrix getMatrix(const Statement&) const;
SampleMatrix getMatrix(const Expression* e) const {
return e ? this->getMatrix(*e) : SampleMatrix();
}
SampleMatrix getMatrix(const Statement* s) const {
return s ? this->getMatrix(*s) : SampleMatrix();
}
SampleMatrix getMatrix(const ProgramElement& pe) const {
if (pe.fKind == ProgramElement::kFunction_Kind) {
return this->getMatrix(*((const FunctionDefinition&) pe).fBody);
}
return SampleMatrix();
}
const Program& fProgram;
const Variable& fFP;
};
SampleMatrix SampleMatrix::Make(const Program& program, const Variable& fp) {
SampleMatrix result;
SampleMatrixExtractor extractor(program, fp);
for (const auto& pe : program) {
result.merge(extractor.getMatrix(pe));
}
return result;
}
SampleMatrix SampleMatrixExtractor::getMatrix(const Expression& e) const {
switch (e.fKind) {
case Expression::kFunctionCall_Kind: {
const FunctionCall& fc = (const FunctionCall&) e;
const FunctionDeclaration& f = fc.fFunction;
if (f.fBuiltin && f.fName == "sample" && fc.fArguments.size() >= 2 &&
fc.fArguments.back()->fType == *fProgram.fContext->fFloat3x3_Type &&
fc.fArguments[0]->fKind == Expression::kVariableReference_Kind &&
&((VariableReference&) *fc.fArguments[0]).fVariable == &fFP) {
if (fc.fArguments.back()->isConstantOrUniform()) {
if (fc.fArguments.back()->fKind == Expression::Kind::kVariableReference_Kind ||
fc.fArguments.back()->fKind == Expression::Kind::kConstructor_Kind) {
// FIXME if this is a constant, we should parse the float3x3 constructor and
// determine if the resulting matrix introduces perspective.
return SampleMatrix::MakeConstUniform(fc.fArguments.back()->description());
} else {
// FIXME this is really to workaround a restriction of the downstream code
// that relies on the SampleMatrix's fExpression to identify uniform names.
// Once they are tracked separately, any constant/uniform expression can
// work, but right now this avoids issues from '0.5 * matrix' that is both
// a constant AND a uniform.
return SampleMatrix::MakeVariable();
}
} else {
return SampleMatrix::MakeVariable();
}
}
SampleMatrix result;
for (const auto& e : fc.fArguments) {
result.merge(this->getMatrix(*e));
}
return result;
}
case Expression::kConstructor_Kind: {
SampleMatrix result;
const Constructor& c = (const Constructor&) e;
for (const auto& e : c.fArguments) {
result.merge(this->getMatrix(*e));
}
return result;
}
case Expression::kFieldAccess_Kind:
return this->getMatrix(*((const FieldAccess&) e).fBase);
case Expression::kSwizzle_Kind:
return this->getMatrix(*((const Swizzle&) e).fBase);
case Expression::kBinary_Kind: {
const BinaryExpression& b = (const BinaryExpression&) e;
return this->getMatrix(*b.fLeft).merge(
this->getMatrix(*b.fRight));
}
case Expression::kIndex_Kind: {
const IndexExpression& idx = (const IndexExpression&) e;
return this->getMatrix(*idx.fBase).merge(
this->getMatrix(*idx.fIndex));
}
case Expression::kPrefix_Kind:
return this->getMatrix(*((const PrefixExpression&) e).fOperand);
case Expression::kPostfix_Kind:
return this->getMatrix(*((const PostfixExpression&) e).fOperand);
case Expression::kTernary_Kind: {
const TernaryExpression& t = (const TernaryExpression&) e;
return this->getMatrix(*t.fTest).merge(
this->getMatrix(*t.fIfTrue)).merge(
this->getMatrix(*t.fIfFalse));
}
case Expression::kVariableReference_Kind:
return SampleMatrix();
case Expression::kBoolLiteral_Kind:
case Expression::kDefined_Kind:
case Expression::kExternalFunctionCall_Kind:
case Expression::kExternalValue_Kind:
case Expression::kFloatLiteral_Kind:
case Expression::kFunctionReference_Kind:
case Expression::kIntLiteral_Kind:
case Expression::kNullLiteral_Kind:
case Expression::kSetting_Kind:
case Expression::kTypeReference_Kind:
return SampleMatrix();
}
SkASSERT(false);
return SampleMatrix();
}
SampleMatrix SampleMatrixExtractor::getMatrix(const Statement& s) const {
switch (s.fKind) {
case Statement::kBlock_Kind: {
SampleMatrix result;
for (const auto& child : ((const Block&) s).fStatements) {
result.merge(this->getMatrix(*child));
}
return result;
}
case Statement::kVarDeclaration_Kind:
return this->getMatrix(((const VarDeclaration&) s).fValue.get());
case Statement::kVarDeclarations_Kind: {
const VarDeclarations& decls = *((const VarDeclarationsStatement&) s).fDeclaration;
SampleMatrix result;
for (const auto& stmt : decls.fVars) {
result.merge(this->getMatrix(*stmt));
}
return result;
}
case Statement::kExpression_Kind:
return this->getMatrix(*((const ExpressionStatement&) s).fExpression);
case Statement::kReturn_Kind:
return this->getMatrix(((const ReturnStatement&) s).fExpression.get());
case Statement::kIf_Kind: {
const IfStatement& i = (const IfStatement&) s;
return this->getMatrix(*i.fTest).merge(
this->getMatrix(*i.fIfTrue)).merge(
this->getMatrix(i.fIfFalse.get()));
}
case Statement::kFor_Kind: {
const ForStatement& f = (const ForStatement&) s;
return this->getMatrix(f.fInitializer.get()).merge(
this->getMatrix(f.fTest.get()).merge(
this->getMatrix(f.fNext.get()).merge(
this->getMatrix(*f.fStatement))));
}
case Statement::kWhile_Kind: {
const WhileStatement& w = (const WhileStatement&) s;
return this->getMatrix(*w.fTest).merge(
this->getMatrix(*w.fStatement));
}
case Statement::kDo_Kind: {
const DoStatement& d = (const DoStatement&) s;
return this->getMatrix(*d.fTest).merge(
this->getMatrix(*d.fStatement));
}
case Statement::kSwitch_Kind: {
SampleMatrix result;
const SwitchStatement& sw = (const SwitchStatement&) s;
for (const auto& c : sw.fCases) {
for (const auto& st : c->fStatements) {
result.merge(this->getMatrix(*st));
}
}
return result.merge(this->getMatrix(*sw.fValue));
}
case Statement::kBreak_Kind:
case Statement::kContinue_Kind:
case Statement::kDiscard_Kind:
case Statement::kNop_Kind:
return SampleMatrix();
}
SkASSERT(false);
return SampleMatrix();
}
} // namespace

View File

@ -70,169 +70,4 @@ SectionAndParameterHelper::SectionAndParameterHelper(const Program* program, Err
}
}
bool SectionAndParameterHelper::hasCoordOverrides(const Variable& fp) {
for (const auto& pe : fProgram) {
if (this->hasCoordOverrides(pe, fp)) {
return true;
}
}
return false;
}
bool SectionAndParameterHelper::hasCoordOverrides(const ProgramElement& pe, const Variable& fp) {
if (pe.fKind == ProgramElement::kFunction_Kind) {
return this->hasCoordOverrides(*((const FunctionDefinition&) pe).fBody, fp);
}
return false;
}
bool SectionAndParameterHelper::hasCoordOverrides(const Expression& e, const Variable& fp) {
switch (e.fKind) {
case Expression::kFunctionCall_Kind: {
const FunctionCall& fc = (const FunctionCall&) e;
const FunctionDeclaration& f = fc.fFunction;
if (f.fBuiltin && f.fName == "sample" && fc.fArguments.size() >= 2 &&
fc.fArguments.back()->fType == *fProgram.fContext->fFloat2_Type &&
fc.fArguments[0]->fKind == Expression::kVariableReference_Kind &&
&((VariableReference&) *fc.fArguments[0]).fVariable == &fp) {
return true;
}
for (const auto& e : fc.fArguments) {
if (this->hasCoordOverrides(*e, fp)) {
return true;
}
}
return false;
}
case Expression::kConstructor_Kind: {
const Constructor& c = (const Constructor&) e;
for (const auto& e : c.fArguments) {
if (this->hasCoordOverrides(*e, fp)) {
return true;
}
}
return false;
}
case Expression::kFieldAccess_Kind: {
return this->hasCoordOverrides(*((const FieldAccess&) e).fBase, fp);
}
case Expression::kSwizzle_Kind:
return this->hasCoordOverrides(*((const Swizzle&) e).fBase, fp);
case Expression::kBinary_Kind: {
const BinaryExpression& b = (const BinaryExpression&) e;
return this->hasCoordOverrides(*b.fLeft, fp) ||
this->hasCoordOverrides(*b.fRight, fp);
}
case Expression::kIndex_Kind: {
const IndexExpression& idx = (const IndexExpression&) e;
return this->hasCoordOverrides(*idx.fBase, fp) ||
this->hasCoordOverrides(*idx.fIndex, fp);
}
case Expression::kPrefix_Kind:
return this->hasCoordOverrides(*((const PrefixExpression&) e).fOperand, fp);
case Expression::kPostfix_Kind:
return this->hasCoordOverrides(*((const PostfixExpression&) e).fOperand, fp);
case Expression::kTernary_Kind: {
const TernaryExpression& t = (const TernaryExpression&) e;
return this->hasCoordOverrides(*t.fTest, fp) ||
this->hasCoordOverrides(*t.fIfTrue, fp) ||
this->hasCoordOverrides(*t.fIfFalse, fp);
}
case Expression::kVariableReference_Kind:
return false;
case Expression::kBoolLiteral_Kind:
case Expression::kDefined_Kind:
case Expression::kExternalFunctionCall_Kind:
case Expression::kExternalValue_Kind:
case Expression::kFloatLiteral_Kind:
case Expression::kFunctionReference_Kind:
case Expression::kIntLiteral_Kind:
case Expression::kNullLiteral_Kind:
case Expression::kSetting_Kind:
case Expression::kTypeReference_Kind:
return false;
}
SkASSERT(false);
return false;
}
bool SectionAndParameterHelper::hasCoordOverrides(const Statement& s, const Variable& fp) {
switch (s.fKind) {
case Statement::kBlock_Kind: {
for (const auto& child : ((const Block&) s).fStatements) {
if (this->hasCoordOverrides(*child, fp)) {
return true;
}
}
return false;
}
case Statement::kVarDeclaration_Kind: {
const VarDeclaration& var = (const VarDeclaration&) s;
if (var.fValue) {
return hasCoordOverrides(*var.fValue, fp);
}
return false;
}
case Statement::kVarDeclarations_Kind: {
const VarDeclarations& decls = *((const VarDeclarationsStatement&) s).fDeclaration;
for (const auto& stmt : decls.fVars) {
if (this->hasCoordOverrides(*stmt, fp)) {
return true;
}
}
return false;
}
case Statement::kExpression_Kind:
return this->hasCoordOverrides(*((const ExpressionStatement&) s).fExpression, fp);
case Statement::kReturn_Kind: {
const ReturnStatement& r = (const ReturnStatement&) s;
if (r.fExpression) {
return this->hasCoordOverrides(*r.fExpression, fp);
}
return false;
}
case Statement::kIf_Kind: {
const IfStatement& i = (const IfStatement&) s;
return this->hasCoordOverrides(*i.fTest, fp) ||
this->hasCoordOverrides(*i.fIfTrue, fp) ||
(i.fIfFalse && this->hasCoordOverrides(*i.fIfFalse, fp));
}
case Statement::kFor_Kind: {
const ForStatement& f = (const ForStatement&) s;
return (f.fInitializer ? this->hasCoordOverrides(*f.fInitializer, fp) : 0) ||
(f.fTest ? this->hasCoordOverrides(*f.fTest, fp) : 0) ||
(f.fNext ? this->hasCoordOverrides(*f.fNext, fp) : 0) ||
this->hasCoordOverrides(*f.fStatement, fp);
}
case Statement::kWhile_Kind: {
const WhileStatement& w = (const WhileStatement&) s;
return this->hasCoordOverrides(*w.fTest, fp) ||
this->hasCoordOverrides(*w.fStatement, fp);
}
case Statement::kDo_Kind: {
const DoStatement& d = (const DoStatement&) s;
return this->hasCoordOverrides(*d.fTest, fp) ||
this->hasCoordOverrides(*d.fStatement, fp);
}
case Statement::kSwitch_Kind: {
const SwitchStatement& sw = (const SwitchStatement&) s;
for (const auto& c : sw.fCases) {
for (const auto& st : c->fStatements) {
if (this->hasCoordOverrides(*st, fp)) {
return true;
}
}
}
return this->hasCoordOverrides(*sw.fValue, fp);
}
case Statement::kBreak_Kind:
case Statement::kContinue_Kind:
case Statement::kDiscard_Kind:
case Statement::kNop_Kind:
return false;
}
SkASSERT(false);
return false;
}
}

View File

@ -62,8 +62,6 @@ public:
return fParameters;
}
bool hasCoordOverrides(const Variable& fp);
static bool IsParameter(const Variable& var) {
return (var.fModifiers.fFlags & Modifiers::kIn_Flag) &&
-1 == var.fModifiers.fLayout.fBuiltin;
@ -109,12 +107,6 @@ public:
}
private:
bool hasCoordOverrides(const Statement& s, const Variable& fp);
bool hasCoordOverrides(const Expression& e, const Variable& fp);
bool hasCoordOverrides(const ProgramElement& p, const Variable& fp);
const Program& fProgram;
std::vector<const Variable*> fParameters;
std::unordered_map<String, std::vector<const Section*>> fSections;