/src/skia/src/sksl/transform/SkSLFindAndDeclareBuiltinFunctions.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
31 | 340 | void Transform::FindAndDeclareBuiltinFunctions(Program& program) { |
32 | 340 | ProgramUsage* usage = program.fUsage.get(); |
33 | 340 | Context& context = *program.fContext; |
34 | | |
35 | 340 | std::vector<const FunctionDefinition*> addedBuiltins; |
36 | 401 | for (;;) { |
37 | | // Find all the built-ins referenced by the program but not yet included in the code. |
38 | 401 | size_t numBuiltinsAtStart = addedBuiltins.size(); |
39 | 418 | for (const auto& [symbol, count] : usage->fCallCounts) { |
40 | 418 | const FunctionDeclaration& fn = symbol->as<FunctionDeclaration>(); |
41 | 418 | if (!fn.isBuiltin() || count == 0) { |
42 | | // Not a built-in; skip it. |
43 | 42 | continue; |
44 | 42 | } |
45 | 376 | if (fn.intrinsicKind() == k_dFdy_IntrinsicKind) { |
46 | | // Programs that invoke the `dFdy` intrinsic will need the RTFlip input. |
47 | 0 | if (!context.fConfig->fSettings.fForceNoRTFlip) { |
48 | 0 | program.fInterface.fRTFlipUniform |= Program::Interface::kRTFlip_Derivative; |
49 | 0 | } |
50 | 0 | } |
51 | 376 | 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 | 131 | if (std::find(addedBuiltins.begin(), addedBuiltins.end(), builtinDef) == |
55 | 131 | addedBuiltins.end()) { |
56 | 65 | addedBuiltins.push_back(builtinDef); |
57 | 65 | } |
58 | 131 | } |
59 | 376 | } |
60 | | |
61 | 401 | if (addedBuiltins.size() == numBuiltinsAtStart) { |
62 | | // If we didn't reference any more built-in functions than before, we're done. |
63 | 340 | break; |
64 | 340 | } |
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 | 61 | std::sort(addedBuiltins.begin() + numBuiltinsAtStart, |
70 | 61 | addedBuiltins.end(), |
71 | 61 | [](const FunctionDefinition* aDefinition, const FunctionDefinition* bDefinition) { |
72 | 4 | const FunctionDeclaration& a = aDefinition->declaration(); |
73 | 4 | const FunctionDeclaration& b = bDefinition->declaration(); |
74 | 4 | if (a.name() != b.name()) { |
75 | 4 | return a.name() > b.name(); |
76 | 4 | } |
77 | 0 | return a.description() > b.description(); |
78 | 4 | }); |
79 | | |
80 | | // Update the ProgramUsage to track all these newly discovered functions. |
81 | 61 | int usageCallCounts = usage->fCallCounts.count(); |
82 | | |
83 | 126 | for (size_t index = numBuiltinsAtStart; index < addedBuiltins.size(); ++index) { |
84 | 65 | usage->add(*addedBuiltins[index]); |
85 | 65 | } |
86 | | |
87 | 61 | if (usage->fCallCounts.count() == usageCallCounts) { |
88 | | // If we aren't making any more unique function calls than before, we're done. |
89 | 0 | break; |
90 | 0 | } |
91 | 61 | } |
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 | 340 | program.fSharedElements.insert(program.fSharedElements.begin(), |
96 | 340 | addedBuiltins.rbegin(), addedBuiltins.rend()); |
97 | 340 | } |
98 | | |
99 | | } // namespace SkSL |