xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLSpecialization.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2024 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef SKSL_SPECIALIZATION
9*c8dee2aaSAndroid Build Coastguard Worker #define SKSL_SPECIALIZATION
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkChecksum.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkBitSet.h"
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
17*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker class Expression;
22*c8dee2aaSAndroid Build Coastguard Worker class FunctionCall;
23*c8dee2aaSAndroid Build Coastguard Worker class FunctionDeclaration;
24*c8dee2aaSAndroid Build Coastguard Worker class Variable;
25*c8dee2aaSAndroid Build Coastguard Worker struct Program;
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker namespace Analysis {
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker /**
30*c8dee2aaSAndroid Build Coastguard Worker  * Design docs for SkSL function specialization: go/sksl-function-specialization
31*c8dee2aaSAndroid Build Coastguard Worker  * https://docs.google.com/document/d/1dJdkk2-KmP-62EERzKygzsLLnxihCbDcFi3UHc1WzAM/edit?usp=sharing
32*c8dee2aaSAndroid Build Coastguard Worker  */
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker // The current index of the specialization function being walked through, used to
35*c8dee2aaSAndroid Build Coastguard Worker // track what the proper specialization function call should be if walking through a
36*c8dee2aaSAndroid Build Coastguard Worker // specialized function call stack.
37*c8dee2aaSAndroid Build Coastguard Worker using SpecializationIndex = int;
38*c8dee2aaSAndroid Build Coastguard Worker static constexpr SpecializationIndex kUnspecialized = -1;
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker // Global uniforms used by a specialization,
41*c8dee2aaSAndroid Build Coastguard Worker // maps <function parameter, expression referencing global uniform>
42*c8dee2aaSAndroid Build Coastguard Worker using SpecializedParameters = skia_private::THashMap<const Variable*, const Expression*>;
43*c8dee2aaSAndroid Build Coastguard Worker // The set of specializated implementations needed for a given function.
44*c8dee2aaSAndroid Build Coastguard Worker using Specializations = skia_private::TArray<SpecializedParameters>;
45*c8dee2aaSAndroid Build Coastguard Worker // The full set of all specializations required by the program.
46*c8dee2aaSAndroid Build Coastguard Worker using SpecializationMap = skia_private::THashMap<const FunctionDeclaration*, Specializations>;
47*c8dee2aaSAndroid Build Coastguard Worker 
48*c8dee2aaSAndroid Build Coastguard Worker // This can be used as a key into a map of specialized function declarations. Most backends which
49*c8dee2aaSAndroid Build Coastguard Worker // implement function specialization will have a need for this.
50*c8dee2aaSAndroid Build Coastguard Worker struct SpecializedFunctionKey {
51*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operatorSpecializedFunctionKey::Hash52*c8dee2aaSAndroid Build Coastguard Worker         size_t operator()(const SpecializedFunctionKey& entry) {
53*c8dee2aaSAndroid Build Coastguard Worker             return SkGoodHash()(entry.fDeclaration) ^
54*c8dee2aaSAndroid Build Coastguard Worker                    SkGoodHash()(entry.fSpecializationIndex);
55*c8dee2aaSAndroid Build Coastguard Worker         }
56*c8dee2aaSAndroid Build Coastguard Worker     };
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(const SpecializedFunctionKey& other) const {
59*c8dee2aaSAndroid Build Coastguard Worker         return fDeclaration == other.fDeclaration &&
60*c8dee2aaSAndroid Build Coastguard Worker                fSpecializationIndex == other.fSpecializationIndex;
61*c8dee2aaSAndroid Build Coastguard Worker     }
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration* fDeclaration = nullptr;
64*c8dee2aaSAndroid Build Coastguard Worker     SpecializationIndex fSpecializationIndex = Analysis::kUnspecialized;
65*c8dee2aaSAndroid Build Coastguard Worker };
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker // This is used as a key into the SpecializedCallMap.
68*c8dee2aaSAndroid Build Coastguard Worker struct SpecializedCallKey {
69*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operatorSpecializedCallKey::Hash70*c8dee2aaSAndroid Build Coastguard Worker         size_t operator()(const SpecializedCallKey& entry) {
71*c8dee2aaSAndroid Build Coastguard Worker             return SkGoodHash()(entry.fStablePointer) ^
72*c8dee2aaSAndroid Build Coastguard Worker                    SkGoodHash()(entry.fParentSpecializationIndex);
73*c8dee2aaSAndroid Build Coastguard Worker         }
74*c8dee2aaSAndroid Build Coastguard Worker     };
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(const SpecializedCallKey& other) const {
77*c8dee2aaSAndroid Build Coastguard Worker         return fStablePointer == other.fStablePointer &&
78*c8dee2aaSAndroid Build Coastguard Worker                fParentSpecializationIndex == other.fParentSpecializationIndex;
79*c8dee2aaSAndroid Build Coastguard Worker     }
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker     const FunctionCall* fStablePointer = nullptr;
82*c8dee2aaSAndroid Build Coastguard Worker     SpecializationIndex fParentSpecializationIndex = Analysis::kUnspecialized;
83*c8dee2aaSAndroid Build Coastguard Worker };
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker // The mapping of function calls and their inherited specialization to their corresponding
86*c8dee2aaSAndroid Build Coastguard Worker // specialization index in `Specializations`
87*c8dee2aaSAndroid Build Coastguard Worker using SpecializedCallMap = skia_private::THashMap<SpecializedCallKey,
88*c8dee2aaSAndroid Build Coastguard Worker                                                   SpecializationIndex,
89*c8dee2aaSAndroid Build Coastguard Worker                                                   SpecializedCallKey::Hash>;
90*c8dee2aaSAndroid Build Coastguard Worker struct SpecializationInfo {
91*c8dee2aaSAndroid Build Coastguard Worker     SpecializationMap fSpecializationMap;
92*c8dee2aaSAndroid Build Coastguard Worker     SpecializedCallMap fSpecializedCallMap;
93*c8dee2aaSAndroid Build Coastguard Worker };
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker // A function that returns true if the parameter variable fits the criteria
96*c8dee2aaSAndroid Build Coastguard Worker // to create a specialization.
97*c8dee2aaSAndroid Build Coastguard Worker using ParameterMatchesFn = std::function<bool(const Variable&)>;
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker // Finds functions that contain parameters that should be specialized on and writes the
100*c8dee2aaSAndroid Build Coastguard Worker // specialization info to the provided `SpecializationInfo`.
101*c8dee2aaSAndroid Build Coastguard Worker void FindFunctionsToSpecialize(const Program& program,
102*c8dee2aaSAndroid Build Coastguard Worker                                SpecializationInfo* info,
103*c8dee2aaSAndroid Build Coastguard Worker                                const ParameterMatchesFn& specializationFn);
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker // Given a function call and the active specialization index, looks up the specialization index for
106*c8dee2aaSAndroid Build Coastguard Worker // the call target. In other words: in the specialization map, we first look up the call target's
107*c8dee2aaSAndroid Build Coastguard Worker // declaration, which yields a Specialization array. We would find the correct mappings in the array
108*c8dee2aaSAndroid Build Coastguard Worker // at the SpecializationIndex returned by this function.
109*c8dee2aaSAndroid Build Coastguard Worker SpecializationIndex FindSpecializationIndexForCall(const FunctionCall& call,
110*c8dee2aaSAndroid Build Coastguard Worker                                                    const SpecializationInfo& info,
111*c8dee2aaSAndroid Build Coastguard Worker                                                    SpecializationIndex activeSpecializationIndex);
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker // Given a function, returns a bit-mask corresponding to each parameter. If a bit is set, the
114*c8dee2aaSAndroid Build Coastguard Worker // corresponding parameter is specialized and should be excluded from the argument/parameter list.
115*c8dee2aaSAndroid Build Coastguard Worker SkBitSet FindSpecializedParametersForFunction(const FunctionDeclaration& func,
116*c8dee2aaSAndroid Build Coastguard Worker                                               const SpecializationInfo& info);
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker // Given a function and its specialization index, invokes a callback once per specialized parameter.
119*c8dee2aaSAndroid Build Coastguard Worker // The callback will be passed the parameter's index, the parameter variable, and the specialized
120*c8dee2aaSAndroid Build Coastguard Worker // value at the given specialization index.
121*c8dee2aaSAndroid Build Coastguard Worker using ParameterMappingCallback = std::function<void(int paramIndex,
122*c8dee2aaSAndroid Build Coastguard Worker                                                     const Variable* param,
123*c8dee2aaSAndroid Build Coastguard Worker                                                     const Expression* value)>;
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker void GetParameterMappingsForFunction(const FunctionDeclaration& func,
126*c8dee2aaSAndroid Build Coastguard Worker                                      const SpecializationInfo& info,
127*c8dee2aaSAndroid Build Coastguard Worker                                      SpecializationIndex specializationIndex,
128*c8dee2aaSAndroid Build Coastguard Worker                                      const ParameterMappingCallback& callback);
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker }  // namespace Analysis
131*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker #endif
134