xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLFindAndDeclareBuiltinFunctions.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkTypes.h"
9 #include "src/core/SkTHash.h"
10 #include "src/sksl/SkSLContext.h"
11 #include "src/sksl/SkSLIntrinsicList.h"
12 #include "src/sksl/SkSLProgramSettings.h"
13 #include "src/sksl/analysis/SkSLProgramUsage.h"
14 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
15 #include "src/sksl/ir/SkSLFunctionDefinition.h"
16 #include "src/sksl/ir/SkSLProgram.h"
17 #include "src/sksl/ir/SkSLSymbol.h"
18 #include "src/sksl/transform/SkSLTransform.h"
19 
20 #include <algorithm>
21 #include <cstddef>
22 #include <memory>
23 #include <string>
24 #include <string_view>
25 #include <vector>
26 
27 namespace SkSL {
28 
29 class ProgramElement;
30 
FindAndDeclareBuiltinFunctions(Program & program)31 void Transform::FindAndDeclareBuiltinFunctions(Program& program) {
32     ProgramUsage* usage = program.fUsage.get();
33     Context& context = *program.fContext;
34 
35     std::vector<const FunctionDefinition*> addedBuiltins;
36     for (;;) {
37         // Find all the built-ins referenced by the program but not yet included in the code.
38         size_t numBuiltinsAtStart = addedBuiltins.size();
39         for (const auto& [symbol, count] : usage->fCallCounts) {
40             const FunctionDeclaration& fn = symbol->as<FunctionDeclaration>();
41             if (!fn.isBuiltin() || count == 0) {
42                 // Not a built-in; skip it.
43                 continue;
44             }
45             if (fn.intrinsicKind() == k_dFdy_IntrinsicKind) {
46                 // Programs that invoke the `dFdy` intrinsic will need the RTFlip input.
47                 if (!context.fConfig->fSettings.fForceNoRTFlip) {
48                     program.fInterface.fRTFlipUniform |= Program::Interface::kRTFlip_Derivative;
49                 }
50             }
51             if (const FunctionDefinition* builtinDef = fn.definition()) {
52                 // Make sure we only add a built-in function once. We rarely add more than a handful
53                 // of builtin functions, so linear search here is good enough.
54                 if (std::find(addedBuiltins.begin(), addedBuiltins.end(), builtinDef) ==
55                     addedBuiltins.end()) {
56                     addedBuiltins.push_back(builtinDef);
57                 }
58             }
59         }
60 
61         if (addedBuiltins.size() == numBuiltinsAtStart) {
62             // If we didn't reference any more built-in functions than before, we're done.
63             break;
64         }
65 
66         // Sort the referenced builtin functions into a consistent order; otherwise our output will
67         // become non-deterministic. The exact order isn't particularly important; we sort backwards
68         // because we add elements to the shared-elements in reverse order at the end.
69         std::sort(addedBuiltins.begin() + numBuiltinsAtStart,
70                   addedBuiltins.end(),
71                   [](const FunctionDefinition* aDefinition, const FunctionDefinition* bDefinition) {
72                       const FunctionDeclaration& a = aDefinition->declaration();
73                       const FunctionDeclaration& b = bDefinition->declaration();
74                       if (a.name() != b.name()) {
75                           return a.name() > b.name();
76                       }
77                       return a.description() > b.description();
78                   });
79 
80         // Update the ProgramUsage to track all these newly discovered functions.
81         int usageCallCounts = usage->fCallCounts.count();
82 
83         for (size_t index = numBuiltinsAtStart; index < addedBuiltins.size(); ++index) {
84             usage->add(*addedBuiltins[index]);
85         }
86 
87         if (usage->fCallCounts.count() == usageCallCounts) {
88             // If we aren't making any more unique function calls than before, we're done.
89             break;
90         }
91     }
92 
93     // Insert the new functions into the program's shared elements, right at the front.
94     // They are added in reverse so that the deepest dependencies are added to the top.
95     program.fSharedElements.insert(program.fSharedElements.begin(),
96                                    addedBuiltins.rbegin(), addedBuiltins.rend());
97 }
98 
99 }  // namespace SkSL
100