blob: c0f09a62b7f1602d2e39add7490b5f8d765ff8f7 [file] [log] [blame]
* 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/transform/SkSLTransform.h"
#include <algorithm>
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace SkSL {
class ProgramElement;
void Transform::FindAndDeclareBuiltinFunctions(Program& program) {
ProgramUsage* usage = program.fUsage.get();
Context& context = *program.fContext;
std::vector<const FunctionDefinition*> 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& [fn, count] : usage->fCallCounts) {
if (!fn->isBuiltin() || count == 0) {
// Not a built-in; skip it.
if (fn->intrinsicKind() == k_dFdy_IntrinsicKind) {
// Programs that invoke the `dFdy` intrinsic will need the RTFlip input.
program.fInterface.fUseFlipRTUniform = !context.fConfig->fSettings.fForceNoRTFlip;
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()) {
if (addedBuiltins.size() == numBuiltinsAtStart) {
// If we didn't reference any more built-in functions than before, we're done.
// 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,
[](const FunctionDefinition* aDefinition, const FunctionDefinition* bDefinition) {
const FunctionDeclaration& a = aDefinition->declaration();
const FunctionDeclaration& b = bDefinition->declaration();
if ( != {
return >;
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) {
if (usage->fCallCounts.count() == usageCallCounts) {
// If we aren't making any more unique function calls than before, we're done.
// 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.
addedBuiltins.rbegin(), addedBuiltins.rend());
} // namespace SkSL