1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 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 skgpu_graphite_PaintParamsKey_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_PaintParamsKey_DEFINED 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMacros.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkChecksum.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/BuiltInCodeSnippetID.h" 17*c8dee2aaSAndroid Build Coastguard Worker 18*c8dee2aaSAndroid Build Coastguard Worker #include <limits> 19*c8dee2aaSAndroid Build Coastguard Worker #include <cstring> // for memcmp 20*c8dee2aaSAndroid Build Coastguard Worker 21*c8dee2aaSAndroid Build Coastguard Worker class SkArenaAlloc; 22*c8dee2aaSAndroid Build Coastguard Worker struct SkSamplingOptions; 23*c8dee2aaSAndroid Build Coastguard Worker enum class SkTileMode; 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite { 26*c8dee2aaSAndroid Build Coastguard Worker 27*c8dee2aaSAndroid Build Coastguard Worker class Caps; 28*c8dee2aaSAndroid Build Coastguard Worker class ShaderCodeDictionary; 29*c8dee2aaSAndroid Build Coastguard Worker class ShaderNode; 30*c8dee2aaSAndroid Build Coastguard Worker class TextureProxy; 31*c8dee2aaSAndroid Build Coastguard Worker class UniquePaintParamsID; 32*c8dee2aaSAndroid Build Coastguard Worker 33*c8dee2aaSAndroid Build Coastguard Worker /** 34*c8dee2aaSAndroid Build Coastguard Worker * This class is a compact representation of the shader needed to implement a given 35*c8dee2aaSAndroid Build Coastguard Worker * PaintParams. Its structure is a series of nodes where each node consists of: 36*c8dee2aaSAndroid Build Coastguard Worker * 4 bytes: code-snippet ID 37*c8dee2aaSAndroid Build Coastguard Worker * N child nodes, where N is the constant number of children defined by the ShaderCodeDictionary 38*c8dee2aaSAndroid Build Coastguard Worker * for the node's snippet ID. 39*c8dee2aaSAndroid Build Coastguard Worker * 40*c8dee2aaSAndroid Build Coastguard Worker * Some snippet definitions support embedding data into the PaintParamsKey, used when something 41*c8dee2aaSAndroid Build Coastguard Worker * external to the generated SkSL needs produce unique pipelines (e.g. immutable samplers). For 42*c8dee2aaSAndroid Build Coastguard Worker * snippets that store data, the data is stored immediately after the ID as: 43*c8dee2aaSAndroid Build Coastguard Worker * 4 bytes: code-snippet ID 44*c8dee2aaSAndroid Build Coastguard Worker * 4 bytes: data length 45*c8dee2aaSAndroid Build Coastguard Worker * 0-M: variable length data 46*c8dee2aaSAndroid Build Coastguard Worker * N child nodes 47*c8dee2aaSAndroid Build Coastguard Worker * 48*c8dee2aaSAndroid Build Coastguard Worker * All children of a child node are stored in the key before the next child is encoded in the key, 49*c8dee2aaSAndroid Build Coastguard Worker * e.g. iterating the data in a key is a depth-first traversal of the node tree. 50*c8dee2aaSAndroid Build Coastguard Worker * 51*c8dee2aaSAndroid Build Coastguard Worker * The PaintParamsKey stores multiple root nodes, with each root representing an effect tree that 52*c8dee2aaSAndroid Build Coastguard Worker * affects different parts of the shading pipeline. The key is can only hold 2 or 3 roots: 53*c8dee2aaSAndroid Build Coastguard Worker * 1. Color root node: produces the "src" color used in final blending with the "dst" color. 54*c8dee2aaSAndroid Build Coastguard Worker * 2. Final blend node: defines the blend function combining src and dst colors. If this is a 55*c8dee2aaSAndroid Build Coastguard Worker * FixedBlend snippet the final pipeline may be able to lift it to HW blending. 56*c8dee2aaSAndroid Build Coastguard Worker * 3. Clipping: optional, produces analytic coverage from a clip shader or shape. 57*c8dee2aaSAndroid Build Coastguard Worker * 58*c8dee2aaSAndroid Build Coastguard Worker * Logically the root effects produce a src color and the src coverage (augmenting any other 59*c8dee2aaSAndroid Build Coastguard Worker * coverage coming from the RenderStep). A single src shading node could be used instead of the 60*c8dee2aaSAndroid Build Coastguard Worker * two for color and blending, but its structure would always be: 61*c8dee2aaSAndroid Build Coastguard Worker * 62*c8dee2aaSAndroid Build Coastguard Worker * [ BlendCompose [ [ color-root-node ] surface-color [ final-blend ] ] ] 63*c8dee2aaSAndroid Build Coastguard Worker * 64*c8dee2aaSAndroid Build Coastguard Worker * where "surface-color" would be a special snippet that produces the current dst color value. 65*c8dee2aaSAndroid Build Coastguard Worker * To keep PaintParamsKeys memory cost lower, the BlendCompose and "surface-color" nodes are implied 66*c8dee2aaSAndroid Build Coastguard Worker * when generating the SkSL and pipeline. 67*c8dee2aaSAndroid Build Coastguard Worker */ 68*c8dee2aaSAndroid Build Coastguard Worker class PaintParamsKey { 69*c8dee2aaSAndroid Build Coastguard Worker public: 70*c8dee2aaSAndroid Build Coastguard Worker // PaintParamsKey can only be created by using a PaintParamsKeyBuilder or by cloning the key 71*c8dee2aaSAndroid Build Coastguard Worker // data from a Builder-owned key, but they can be passed around by value after that. 72*c8dee2aaSAndroid Build Coastguard Worker constexpr PaintParamsKey(const PaintParamsKey&) = default; 73*c8dee2aaSAndroid Build Coastguard Worker 74*c8dee2aaSAndroid Build Coastguard Worker ~PaintParamsKey() = default; 75*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKey& operator=(const PaintParamsKey&) = default; 76*c8dee2aaSAndroid Build Coastguard Worker Invalid()77*c8dee2aaSAndroid Build Coastguard Worker static constexpr PaintParamsKey Invalid() { return PaintParamsKey(SkSpan<const uint32_t>()); } isValid()78*c8dee2aaSAndroid Build Coastguard Worker bool isValid() const { return !fData.empty(); } 79*c8dee2aaSAndroid Build Coastguard Worker 80*c8dee2aaSAndroid Build Coastguard Worker // Return a PaintParamsKey whose data is owned by the provided arena and is not attached to 81*c8dee2aaSAndroid Build Coastguard Worker // a PaintParamsKeyBuilder. The caller must ensure that the SkArenaAlloc remains alive longer 82*c8dee2aaSAndroid Build Coastguard Worker // than the returned key. 83*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKey clone(SkArenaAlloc*) const; 84*c8dee2aaSAndroid Build Coastguard Worker 85*c8dee2aaSAndroid Build Coastguard Worker // Converts the key into a forest of ShaderNode trees. If the key is valid this will return at 86*c8dee2aaSAndroid Build Coastguard Worker // least one root node. If the key contains unknown shader snippet IDs, returns an empty span. 87*c8dee2aaSAndroid Build Coastguard Worker // All shader nodes, and the returned span's backing data, are owned by the provided arena. 88*c8dee2aaSAndroid Build Coastguard Worker // 89*c8dee2aaSAndroid Build Coastguard Worker // A valid key will produce either 2 or 3 root nodes. The first root node represents how the 90*c8dee2aaSAndroid Build Coastguard Worker // source color is computed. The second node defines the final blender between the calculated 91*c8dee2aaSAndroid Build Coastguard Worker // source color and the current pixel's dst color. If provided, the third node calculates an 92*c8dee2aaSAndroid Build Coastguard Worker // additional analytic coverage value to combine with the geometry's coverage. 93*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const ShaderNode*> getRootNodes(const ShaderCodeDictionary*, SkArenaAlloc*) const; 94*c8dee2aaSAndroid Build Coastguard Worker 95*c8dee2aaSAndroid Build Coastguard Worker // Converts the key to a structured list of snippet information for debugging or labeling 96*c8dee2aaSAndroid Build Coastguard Worker // purposes. 97*c8dee2aaSAndroid Build Coastguard Worker SkString toString(const ShaderCodeDictionary* dict, bool includeData) const; 98*c8dee2aaSAndroid Build Coastguard Worker 99*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG 100*c8dee2aaSAndroid Build Coastguard Worker void dump(const ShaderCodeDictionary*, UniquePaintParamsID) const; 101*c8dee2aaSAndroid Build Coastguard Worker #endif 102*c8dee2aaSAndroid Build Coastguard Worker 103*c8dee2aaSAndroid Build Coastguard Worker bool operator==(const PaintParamsKey& that) const { 104*c8dee2aaSAndroid Build Coastguard Worker return fData.size() == that.fData.size() && 105*c8dee2aaSAndroid Build Coastguard Worker !memcmp(fData.data(), that.fData.data(), fData.size()); 106*c8dee2aaSAndroid Build Coastguard Worker } 107*c8dee2aaSAndroid Build Coastguard Worker bool operator!=(const PaintParamsKey& that) const { return !(*this == that); } 108*c8dee2aaSAndroid Build Coastguard Worker 109*c8dee2aaSAndroid Build Coastguard Worker struct Hash { operatorHash110*c8dee2aaSAndroid Build Coastguard Worker uint32_t operator()(const PaintParamsKey& k) const { 111*c8dee2aaSAndroid Build Coastguard Worker return SkChecksum::Hash32(k.fData.data(), k.fData.size_bytes()); 112*c8dee2aaSAndroid Build Coastguard Worker } 113*c8dee2aaSAndroid Build Coastguard Worker }; 114*c8dee2aaSAndroid Build Coastguard Worker 115*c8dee2aaSAndroid Build Coastguard Worker private: 116*c8dee2aaSAndroid Build Coastguard Worker friend class PaintParamsKeyBuilder; // for the parented-data ctor 117*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKey(SkSpan<const uint32_t> span)118*c8dee2aaSAndroid Build Coastguard Worker constexpr PaintParamsKey(SkSpan<const uint32_t> span) : fData(span) {} 119*c8dee2aaSAndroid Build Coastguard Worker 120*c8dee2aaSAndroid Build Coastguard Worker // Returns null if the node or any of its children have an invalid snippet ID. Recursively 121*c8dee2aaSAndroid Build Coastguard Worker // creates a node and all of its children, incrementing 'currentIndex' by the total number of 122*c8dee2aaSAndroid Build Coastguard Worker // nodes created. 123*c8dee2aaSAndroid Build Coastguard Worker const ShaderNode* createNode(const ShaderCodeDictionary*, 124*c8dee2aaSAndroid Build Coastguard Worker int* currentIndex, 125*c8dee2aaSAndroid Build Coastguard Worker SkArenaAlloc* arena) const; 126*c8dee2aaSAndroid Build Coastguard Worker 127*c8dee2aaSAndroid Build Coastguard Worker // The memory referenced in 'fData' is always owned by someone else. It either shares the span 128*c8dee2aaSAndroid Build Coastguard Worker // from the Builder, or clone() puts the span in an arena. 129*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const uint32_t> fData; 130*c8dee2aaSAndroid Build Coastguard Worker }; 131*c8dee2aaSAndroid Build Coastguard Worker 132*c8dee2aaSAndroid Build Coastguard Worker // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same 133*c8dee2aaSAndroid Build Coastguard Worker // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks' 134*c8dee2aaSAndroid Build Coastguard Worker // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder 135*c8dee2aaSAndroid Build Coastguard Worker // can only have one extant key and that key must be destroyed before the builder can be reused 136*c8dee2aaSAndroid Build Coastguard Worker // to create another one. 137*c8dee2aaSAndroid Build Coastguard Worker // 138*c8dee2aaSAndroid Build Coastguard Worker // This arrangement is intended to improve performance in the expected case, where a builder is 139*c8dee2aaSAndroid Build Coastguard Worker // being used in a tight loop to generate keys which can be recycled once they've been used to 140*c8dee2aaSAndroid Build Coastguard Worker // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory 141*c8dee2aaSAndroid Build Coastguard Worker // into the dictionary to be prohibitive since that should be infrequent. 142*c8dee2aaSAndroid Build Coastguard Worker class PaintParamsKeyBuilder { 143*c8dee2aaSAndroid Build Coastguard Worker public: PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)144*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKeyBuilder(const ShaderCodeDictionary* dict) { 145*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(fDict = dict;) 146*c8dee2aaSAndroid Build Coastguard Worker } 147*c8dee2aaSAndroid Build Coastguard Worker ~PaintParamsKeyBuilder()148*c8dee2aaSAndroid Build Coastguard Worker ~PaintParamsKeyBuilder() { SkASSERT(!fLocked); } 149*c8dee2aaSAndroid Build Coastguard Worker beginBlock(BuiltInCodeSnippetID id)150*c8dee2aaSAndroid Build Coastguard Worker void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); } beginBlock(int32_t codeSnippetID)151*c8dee2aaSAndroid Build Coastguard Worker void beginBlock(int32_t codeSnippetID) { 152*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fLocked); 153*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(this->pushStack(codeSnippetID);) 154*c8dee2aaSAndroid Build Coastguard Worker fData.push_back(codeSnippetID); 155*c8dee2aaSAndroid Build Coastguard Worker } 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Worker // TODO: Have endBlock() be handled automatically with RAII, in which case we could have it 158*c8dee2aaSAndroid Build Coastguard Worker // validate the snippet ID being popped off the stack frame. endBlock()159*c8dee2aaSAndroid Build Coastguard Worker void endBlock() { 160*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(this->popStack();) 161*c8dee2aaSAndroid Build Coastguard Worker } 162*c8dee2aaSAndroid Build Coastguard Worker 163*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG 164*c8dee2aaSAndroid Build Coastguard Worker // Check that the builder has been reset to its initial state prior to creating a new key. 165*c8dee2aaSAndroid Build Coastguard Worker void checkReset(); 166*c8dee2aaSAndroid Build Coastguard Worker #endif 167*c8dee2aaSAndroid Build Coastguard Worker 168*c8dee2aaSAndroid Build Coastguard Worker // Helper to add blocks that don't have children addBlock(BuiltInCodeSnippetID id)169*c8dee2aaSAndroid Build Coastguard Worker void addBlock(BuiltInCodeSnippetID id) { 170*c8dee2aaSAndroid Build Coastguard Worker this->beginBlock(id); 171*c8dee2aaSAndroid Build Coastguard Worker this->endBlock(); 172*c8dee2aaSAndroid Build Coastguard Worker } 173*c8dee2aaSAndroid Build Coastguard Worker addData(SkSpan<const uint32_t> data)174*c8dee2aaSAndroid Build Coastguard Worker void addData(SkSpan<const uint32_t> data) { 175*c8dee2aaSAndroid Build Coastguard Worker // First push the data size followed by the actual data. 176*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(this->validateData(data.size())); 177*c8dee2aaSAndroid Build Coastguard Worker fData.push_back(data.size()); 178*c8dee2aaSAndroid Build Coastguard Worker fData.push_back_n(data.size(), data.begin()); 179*c8dee2aaSAndroid Build Coastguard Worker } 180*c8dee2aaSAndroid Build Coastguard Worker 181*c8dee2aaSAndroid Build Coastguard Worker private: 182*c8dee2aaSAndroid Build Coastguard Worker friend class AutoLockBuilderAsKey; // for lockAsKey() and unlock() 183*c8dee2aaSAndroid Build Coastguard Worker 184*c8dee2aaSAndroid Build Coastguard Worker // Returns a view of this builder as a PaintParamsKey. The Builder cannot be used until the 185*c8dee2aaSAndroid Build Coastguard Worker // returned Key goes out of scope. lockAsKey()186*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKey lockAsKey() { 187*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fLocked); // lockAsKey() is not re-entrant 188*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fStack.empty()); // All beginBlocks() had a matching endBlock() 189*c8dee2aaSAndroid Build Coastguard Worker 190*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(fLocked = true;) 191*c8dee2aaSAndroid Build Coastguard Worker return PaintParamsKey({fData.data(), fData.size()}); 192*c8dee2aaSAndroid Build Coastguard Worker } 193*c8dee2aaSAndroid Build Coastguard Worker 194*c8dee2aaSAndroid Build Coastguard Worker // Invalidates any PaintParamsKey returned by lockAsKey() unless it has been cloned. unlock()195*c8dee2aaSAndroid Build Coastguard Worker void unlock() { 196*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fLocked); 197*c8dee2aaSAndroid Build Coastguard Worker fData.clear(); 198*c8dee2aaSAndroid Build Coastguard Worker 199*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(fLocked = false;) 200*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(fStack.clear();) 201*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(this->checkReset();) 202*c8dee2aaSAndroid Build Coastguard Worker } 203*c8dee2aaSAndroid Build Coastguard Worker 204*c8dee2aaSAndroid Build Coastguard Worker // The data array uses clear() on unlock so that it's underlying storage and repeated use of the 205*c8dee2aaSAndroid Build Coastguard Worker // builder will hit a high-water mark and avoid lots of allocations when recording draws. 206*c8dee2aaSAndroid Build Coastguard Worker skia_private::TArray<uint32_t> fData; 207*c8dee2aaSAndroid Build Coastguard Worker 208*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG 209*c8dee2aaSAndroid Build Coastguard Worker void pushStack(int32_t codeSnippetID); 210*c8dee2aaSAndroid Build Coastguard Worker void validateData(size_t dataSize); 211*c8dee2aaSAndroid Build Coastguard Worker void popStack(); 212*c8dee2aaSAndroid Build Coastguard Worker 213*c8dee2aaSAndroid Build Coastguard Worker // Information about the current block being written 214*c8dee2aaSAndroid Build Coastguard Worker struct StackFrame { 215*c8dee2aaSAndroid Build Coastguard Worker int fCodeSnippetID; 216*c8dee2aaSAndroid Build Coastguard Worker int fNumExpectedChildren; 217*c8dee2aaSAndroid Build Coastguard Worker int fNumActualChildren = 0; 218*c8dee2aaSAndroid Build Coastguard Worker int fDataSize = -1; 219*c8dee2aaSAndroid Build Coastguard Worker }; 220*c8dee2aaSAndroid Build Coastguard Worker 221*c8dee2aaSAndroid Build Coastguard Worker const ShaderCodeDictionary* fDict; 222*c8dee2aaSAndroid Build Coastguard Worker skia_private::TArray<StackFrame> fStack; 223*c8dee2aaSAndroid Build Coastguard Worker bool fLocked = false; 224*c8dee2aaSAndroid Build Coastguard Worker #endif 225*c8dee2aaSAndroid Build Coastguard Worker }; 226*c8dee2aaSAndroid Build Coastguard Worker 227*c8dee2aaSAndroid Build Coastguard Worker class AutoLockBuilderAsKey { 228*c8dee2aaSAndroid Build Coastguard Worker public: AutoLockBuilderAsKey(PaintParamsKeyBuilder * builder)229*c8dee2aaSAndroid Build Coastguard Worker AutoLockBuilderAsKey(PaintParamsKeyBuilder* builder) 230*c8dee2aaSAndroid Build Coastguard Worker : fBuilder(builder) 231*c8dee2aaSAndroid Build Coastguard Worker , fKey(builder->lockAsKey()) {} 232*c8dee2aaSAndroid Build Coastguard Worker ~AutoLockBuilderAsKey()233*c8dee2aaSAndroid Build Coastguard Worker ~AutoLockBuilderAsKey() { 234*c8dee2aaSAndroid Build Coastguard Worker fBuilder->unlock(); 235*c8dee2aaSAndroid Build Coastguard Worker } 236*c8dee2aaSAndroid Build Coastguard Worker 237*c8dee2aaSAndroid Build Coastguard Worker // Use as a PaintParamsKey 238*c8dee2aaSAndroid Build Coastguard Worker const PaintParamsKey& operator*() const { return fKey; } 239*c8dee2aaSAndroid Build Coastguard Worker const PaintParamsKey* operator->() const { return &fKey; } 240*c8dee2aaSAndroid Build Coastguard Worker 241*c8dee2aaSAndroid Build Coastguard Worker private: 242*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKeyBuilder* fBuilder; 243*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKey fKey; 244*c8dee2aaSAndroid Build Coastguard Worker }; 245*c8dee2aaSAndroid Build Coastguard Worker 246*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite 247*c8dee2aaSAndroid Build Coastguard Worker 248*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_PaintParamsKey_DEFINED 249