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