// Visitor that determines the merged SampleUsage for a given child in the program. class MergeSampleUsageVisitor : public ProgramVisitor { public:
MergeSampleUsageVisitor(const Context& context, const Variable& child, bool writesToSampleCoords)
: fContext(context), fChild(child), fWritesToSampleCoords(writesToSampleCoords) {}
bool visitExpression(const Expression& e) override { switch (e.kind()) { case ExpressionKind::kChildCall: { const ChildCall& cc = e.as<ChildCall>(); if (&cc.child() == &fChild) { // Determine the type of call at this site, and merge it with the accumulated // state const ExpressionArray& arguments = cc.arguments();
SkASSERT(!arguments.empty());
const Expression* maybeCoords = arguments[0].get(); if (maybeCoords->type().matches(*fContext.fTypes.fFloat2)) { // If the coords are a direct reference to the program's sample-coords, and // those coords are never modified, we can conservatively turn this into // PassThrough sampling. In all other cases, we consider it Explicit. if (!fWritesToSampleCoords && maybeCoords->is<VariableReference>() &&
maybeCoords->as<VariableReference>().variable() == fMainCoordsParam) {
fUsage.merge(SampleUsage::PassThrough());
++fElidedSampleCoordCount;
} else {
fUsage.merge(SampleUsage::Explicit());
}
} else { // child(inputColor) or child(srcColor, dstColor) -> PassThrough
fUsage.merge(SampleUsage::PassThrough());
}
} break;
} case ExpressionKind::kFunctionCall: { // If this child effect is ever passed via a function call... const FunctionCall& call = e.as<FunctionCall>(); for (const std::unique_ptr<Expression>& arg : call.arguments()) { if (arg->is<VariableReference>() &&
arg->as<VariableReference>().variable() == &fChild) { // ... we must treat it as explicitly sampled, since the program's // sample-coords only exist as a parameter to `main`.
fUsage.merge(SampleUsage::Explicit()); break;
}
} break;
} default: break;
} return INHERITED::visitExpression(e);
}
using INHERITED = ProgramVisitor;
};
// Visitor that searches for child calls from a function other than main() class SampleOutsideMainVisitor : public ProgramVisitor { public:
SampleOutsideMainVisitor() {}
bool visitExpression(const Expression& e) override { // No need to recurse into expressions, these can never contain return statements returnfalse;
}
using INHERITED = ProgramVisitor; using INHERITED::visitProgramElement;
};
// Visitor that counts the number of nodes visited class NodeCountVisitor : public ProgramVisitor { public:
NodeCountVisitor(int limit) : fLimit(limit) {}
int visit(const Statement& s) {
this->visitStatement(s); return fCount;
}
// This isn't actually using ProgramVisitor, because it only considers a subset of the fields for // any given expression kind. For instance, when indexing an array (e.g. `x[1]`), we only want to // know if the base (`x`) is assignable; the index expression (`1`) doesn't need to be. class IsAssignableVisitor { public:
IsAssignableVisitor(ErrorReporter* errors) : fErrors(errors) {}
default:
fErrors->error(expr.fPosition, "cannot assign to this expression"); break;
}
}
private: void checkSwizzleWrite(const Swizzle& swizzle) { int bits = 0; for (int8_t idx : swizzle.components()) {
SkASSERT(idx >= SwizzleComponent::X && idx <= SwizzleComponent::W); int bit = 1 << idx; if (bits & bit) {
fErrors->error(swizzle.fPosition, "cannot write to the same swizzle field more than once"); break;
}
bits |= bit;
}
}
bool Analysis::ReferencesBuiltin(const Program& program, int builtin) {
SkASSERT(program.fUsage); for (constauto& [variable, counts] : program.fUsage->fVariableCounts) { if (counts.fRead > 0 && variable->layout().fBuiltin == builtin) { returntrue;
}
} returnfalse;
}
bool Analysis::ReferencesSampleCoords(const Program& program) { // Look for main(). for (const std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) { if (pe->is<FunctionDefinition>()) { const FunctionDeclaration& func = pe->as<FunctionDefinition>().declaration(); if (func.isMain()) { // See if main() has a coords parameter that is read from anywhere. if (const Variable* coords = func.getMainCoordsParameter()) {
ProgramUsage::VariableCounts counts = program.fUsage->get(*coords); return counts.fRead > 0;
}
}
}
} // The program is missing a main(). returnfalse;
}
bool Analysis::IsCompileTimeConstant(const Expression& expr) { class IsCompileTimeConstantVisitor : public ProgramVisitor { public: bool visitExpression(const Expression& expr) override { switch (expr.kind()) { case Expression::Kind::kLiteral: // Literals are compile-time constants. returnfalse;
case Expression::Kind::kConstructorArray: case Expression::Kind::kConstructorCompound: case Expression::Kind::kConstructorDiagonalMatrix: case Expression::Kind::kConstructorMatrixResize: case Expression::Kind::kConstructorSplat: case Expression::Kind::kConstructorStruct: // Constructors might be compile-time constants, if they are composed entirely // of literals and constructors. (Casting constructors are intentionally omitted // here. If the value inside was a compile-time constant, we would have not have // generated a cast at all.) return INHERITED::visitExpression(expr);
default: // This expression isn't a compile-time constant.
fIsConstant = false; returntrue;
}
}
bool fIsConstant = true; using INHERITED = ProgramVisitor;
};
bool Analysis::DetectVarDeclarationWithoutScope(const Statement& stmt, ErrorReporter* errors) { // A variable declaration can create either a lone VarDeclaration or an unscoped Block // containing multiple VarDeclaration statements. We need to detect either case. const Variable* var; if (stmt.is<VarDeclaration>()) { // The single-variable case. No blocks at all.
var = stmt.as<VarDeclaration>().var();
} elseif (stmt.is<Block>()) { // The multiple-variable case: an unscoped, non-empty block... const Block& block = stmt.as<Block>(); if (block.isScope() || block.children().empty()) { returnfalse;
} // ... holding a variable declaration. const Statement& innerStmt = *block.children().front(); if (!innerStmt.is<VarDeclaration>()) { returnfalse;
}
var = innerStmt.as<VarDeclaration>().var();
} else { // This statement wasn't a variable declaration. No problem. returnfalse;
}
// Report an error.
SkASSERT(var); if (errors) {
errors->error(var->fPosition, "variable '" + std::string(var->name()) + "' must be created in a scope");
} returntrue;
}
int Analysis::NodeCountUpToLimit(const FunctionDefinition& function, int limit) { return NodeCountVisitor{limit}.visit(*function.body());
}
bool ProgramVisitor::visit(const Program& program) { for (const ProgramElement* pe : program.elements()) { if (this->visitProgramElement(*pe)) { returntrue;
}
} returnfalse;
}
template <typename T> bool TProgramVisitor<T>::visitExpression(typename T::Expression& e) { switch (e.kind()) { case Expression::Kind::kEmpty: case Expression::Kind::kFunctionReference: case Expression::Kind::kLiteral: case Expression::Kind::kMethodReference: case Expression::Kind::kPoison: case Expression::Kind::kSetting: case Expression::Kind::kTypeReference: case Expression::Kind::kVariableReference: // Leaf expressions return false returnfalse;
case Expression::Kind::kBinary: { auto& b = e.template as<BinaryExpression>(); return (b.left() && this->visitExpressionPtr(b.left())) ||
(b.right() && this->visitExpressionPtr(b.right()));
} case Expression::Kind::kChildCall: { // We don't visit the child variable itself, just the arguments auto& c = e.template as<ChildCall>(); for (auto& arg : c.arguments()) { if (arg && this->visitExpressionPtr(arg)) { returntrue; }
} returnfalse;
} case Expression::Kind::kConstructorArray: case Expression::Kind::kConstructorArrayCast: case Expression::Kind::kConstructorCompound: case Expression::Kind::kConstructorCompoundCast: case Expression::Kind::kConstructorDiagonalMatrix: case Expression::Kind::kConstructorMatrixResize: case Expression::Kind::kConstructorScalarCast: case Expression::Kind::kConstructorSplat: case Expression::Kind::kConstructorStruct: { auto& c = e.asAnyConstructor(); for (auto& arg : c.argumentSpan()) { if (this->visitExpressionPtr(arg)) { returntrue; }
} returnfalse;
} case Expression::Kind::kFieldAccess: return this->visitExpressionPtr(e.template as<FieldAccess>().base());
case Expression::Kind::kFunctionCall: { auto& c = e.template as<FunctionCall>(); for (auto& arg : c.arguments()) { if (arg && this->visitExpressionPtr(arg)) { returntrue; }
} returnfalse;
} case Expression::Kind::kIndex: { auto& i = e.template as<IndexExpression>(); return this->visitExpressionPtr(i.base()) || this->visitExpressionPtr(i.index());
} case Expression::Kind::kPostfix: return this->visitExpressionPtr(e.template as<PostfixExpression>().operand());
case Expression::Kind::kPrefix: return this->visitExpressionPtr(e.template as<PrefixExpression>().operand());
case Expression::Kind::kSwizzle: { auto& s = e.template as<Swizzle>(); return s.base() && this->visitExpressionPtr(s.base());
}
template <typename T> bool TProgramVisitor<T>::visitStatement(typename T::Statement& s) { switch (s.kind()) { case Statement::Kind::kBreak: case Statement::Kind::kContinue: case Statement::Kind::kDiscard: case Statement::Kind::kNop: // Leaf statements just return false returnfalse;
case Statement::Kind::kBlock: for (auto& stmt : s.template as<Block>().children()) { if (stmt && this->visitStatementPtr(stmt)) { returntrue;
}
} returnfalse;
case Statement::Kind::kSwitchCase: { auto& sc = s.template as<SwitchCase>(); return this->visitStatementPtr(sc.statement());
} case Statement::Kind::kDo: { auto& d = s.template as<DoStatement>(); return this->visitExpressionPtr(d.test()) || this->visitStatementPtr(d.statement());
} case Statement::Kind::kExpression: return this->visitExpressionPtr(s.template as<ExpressionStatement>().expression());
case Statement::Kind::kFor: { auto& f = s.template as<ForStatement>(); return (f.initializer() && this->visitStatementPtr(f.initializer())) ||
(f.test() && this->visitExpressionPtr(f.test())) ||
(f.next() && this->visitExpressionPtr(f.next())) ||
this->visitStatementPtr(f.statement());
} case Statement::Kind::kIf: { auto& i = s.template as<IfStatement>(); return (i.test() && this->visitExpressionPtr(i.test())) ||
(i.ifTrue() && this->visitStatementPtr(i.ifTrue())) ||
(i.ifFalse() && this->visitStatementPtr(i.ifFalse()));
} case Statement::Kind::kReturn: { auto& r = s.template as<ReturnStatement>(); return r.expression() && this->visitExpressionPtr(r.expression());
} case Statement::Kind::kSwitch: { auto& sw = s.template as<SwitchStatement>(); return this->visitExpressionPtr(sw.value()) || this->visitStatementPtr(sw.caseBlock());
} case Statement::Kind::kVarDeclaration: { auto& v = s.template as<VarDeclaration>(); return v.value() && this->visitExpressionPtr(v.value());
} default:
SkUNREACHABLE;
}
}
template <typename T> bool TProgramVisitor<T>::visitProgramElement(typename T::ProgramElement& pe) { switch (pe.kind()) { case ProgramElement::Kind::kExtension: case ProgramElement::Kind::kFunctionPrototype: case ProgramElement::Kind::kInterfaceBlock: case ProgramElement::Kind::kModifiers: case ProgramElement::Kind::kStructDefinition: // Leaf program elements just return false by default returnfalse;
case ProgramElement::Kind::kFunction: return this->visitStatementPtr(pe.template as<FunctionDefinition>().body());
case ProgramElement::Kind::kGlobalVar: return this->visitStatementPtr(pe.template as<GlobalVarDeclaration>().declaration());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.