/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkTypes.h" #include "src/core/SkTHash.h" #include "src/sksl/SkSLContext.h" #include "src/sksl/SkSLIntrinsicList.h" #include "src/sksl/SkSLProgramSettings.h" #include "src/sksl/analysis/SkSLProgramUsage.h" #include "src/sksl/ir/SkSLFunctionDeclaration.h" #include "src/sksl/ir/SkSLFunctionDefinition.h" #include "src/sksl/ir/SkSLProgram.h" #include "src/sksl/ir/SkSLSymbol.h" #include "src/sksl/transform/SkSLTransform.h" #include #include #include #include #include #include namespace SkSL { class ProgramElement; void Transform::FindAndDeclareBuiltinFunctions(Program& program) { ProgramUsage* usage = program.fUsage.get(); Context& context = *program.fContext; std::vector addedBuiltins; for (;;) { // Find all the built-ins referenced by the program but not yet included in the code. size_t numBuiltinsAtStart = addedBuiltins.size(); for (const auto& [symbol, count] : usage->fCallCounts) { const FunctionDeclaration& fn = symbol->as(); if (!fn.isBuiltin() || count == 0) { // Not a built-in; skip it. continue; } if (fn.intrinsicKind() == k_dFdy_IntrinsicKind) { // Programs that invoke the `dFdy` intrinsic will need the RTFlip input. if (!context.fConfig->fSettings.fForceNoRTFlip) { program.fInterface.fRTFlipUniform |= Program::Interface::kRTFlip_Derivative; } } if (const FunctionDefinition* builtinDef = fn.definition()) { // Make sure we only add a built-in function once. We rarely add more than a handful // of builtin functions, so linear search here is good enough. if (std::find(addedBuiltins.begin(), addedBuiltins.end(), builtinDef) == addedBuiltins.end()) { addedBuiltins.push_back(builtinDef); } } } if (addedBuiltins.size() == numBuiltinsAtStart) { // If we didn't reference any more built-in functions than before, we're done. break; } // Sort the referenced builtin functions into a consistent order; otherwise our output will // become non-deterministic. The exact order isn't particularly important; we sort backwards // because we add elements to the shared-elements in reverse order at the end. std::sort(addedBuiltins.begin() + numBuiltinsAtStart, addedBuiltins.end(), [](const FunctionDefinition* aDefinition, const FunctionDefinition* bDefinition) { const FunctionDeclaration& a = aDefinition->declaration(); const FunctionDeclaration& b = bDefinition->declaration(); if (a.name() != b.name()) { return a.name() > b.name(); } return a.description() > b.description(); }); // Update the ProgramUsage to track all these newly discovered functions. int usageCallCounts = usage->fCallCounts.count(); for (size_t index = numBuiltinsAtStart; index < addedBuiltins.size(); ++index) { usage->add(*addedBuiltins[index]); } if (usage->fCallCounts.count() == usageCallCounts) { // If we aren't making any more unique function calls than before, we're done. break; } } // Insert the new functions into the program's shared elements, right at the front. // They are added in reverse so that the deepest dependencies are added to the top. program.fSharedElements.insert(program.fSharedElements.begin(), addedBuiltins.rbegin(), addedBuiltins.rend()); } } // namespace SkSL