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 #ifndef skgpu_graphite_PaintParamsKey_DEFINED 9 #define skgpu_graphite_PaintParamsKey_DEFINED 10 11 #include "include/core/SkSpan.h" 12 #include "include/core/SkTypes.h" 13 #include "include/private/base/SkMacros.h" 14 #include "include/private/base/SkTArray.h" 15 #include "src/core/SkChecksum.h" 16 #include "src/gpu/graphite/BuiltInCodeSnippetID.h" 17 18 #include <limits> 19 #include <cstring> // for memcmp 20 21 class SkArenaAlloc; 22 23 namespace skgpu::graphite { 24 25 class ShaderCodeDictionary; 26 class ShaderNode; 27 class UniquePaintParamsID; 28 29 // This class is a compact representation of the shader needed to implement a given 30 // PaintParams. Its structure is a series of nodes where each node consists of: 31 // 4 bytes: code-snippet ID 32 // N child nodes, where N is the constant number of children defined by the ShaderCodeDictionary 33 // for the node's snippet ID. 34 // 35 // All children of a child node are stored in the key before the next child is encoded in the key, 36 // e.g. iterating the data in a key is a depth-first traversal of the node tree. 37 class PaintParamsKey { 38 public: 39 // PaintParamsKey can only be created by using a PaintParamsKeyBuilder or by cloning the key 40 // data from a Builder-owned key, but they can be passed around by value after that. 41 constexpr PaintParamsKey(const PaintParamsKey&) = default; 42 43 ~PaintParamsKey() = default; 44 PaintParamsKey& operator=(const PaintParamsKey&) = default; 45 Invalid()46 static constexpr PaintParamsKey Invalid() { return PaintParamsKey(SkSpan<const int32_t>()); } isValid()47 bool isValid() const { return !fData.empty(); } 48 49 // Return a PaintParamsKey whose data is owned by the provided arena and is not attached to 50 // a PaintParamsKeyBuilder. The caller must ensure that the SkArenaAlloc remains alive longer 51 // than the returned key. 52 PaintParamsKey clone(SkArenaAlloc*) const; 53 54 // Converts the key into a forest of ShaderNode trees. If the key is valid this will return at 55 // least one root node. If the key contains unknown shader snippet IDs, returns an empty span. 56 // All shader nodes, and the returned span's backing data, are owned by the provided arena. 57 // TODO: Strengthen PaintParams key generation so we can assume there's only ever one root node 58 // representing the final blend (either a shader blend (with 2 children: main effect & dst) or 59 // a fixed function blend (with 1 child being the main effect)). 60 SkSpan<const ShaderNode*> getRootNodes(const ShaderCodeDictionary*, SkArenaAlloc*) const; 61 62 // Converts the key to a structured list of snippet names for debugging or labeling purposes. 63 SkString toString(const ShaderCodeDictionary* dict) const; 64 65 #ifdef SK_DEBUG 66 void dump(const ShaderCodeDictionary*, UniquePaintParamsID) const; 67 #endif 68 69 bool operator==(const PaintParamsKey& that) const { 70 return fData.size() == that.fData.size() && 71 !memcmp(fData.data(), that.fData.data(), fData.size()); 72 } 73 bool operator!=(const PaintParamsKey& that) const { return !(*this == that); } 74 75 struct Hash { operatorHash76 uint32_t operator()(const PaintParamsKey& k) const { 77 return SkChecksum::Hash32(k.fData.data(), k.fData.size_bytes()); 78 } 79 }; 80 81 private: 82 friend class PaintParamsKeyBuilder; // for the parented-data ctor 83 PaintParamsKey(SkSpan<const int32_t> span)84 constexpr PaintParamsKey(SkSpan<const int32_t> span) : fData(span) {} 85 86 // Returns null if the node or any of its children have an invalid snippet ID. Recursively 87 // creates a node and all of its children, incrementing 'currentIndex' by the total number of 88 // nodes created. 89 const ShaderNode* createNode(const ShaderCodeDictionary*, 90 int* currentIndex, 91 SkArenaAlloc* arena) const; 92 93 // The memory referenced in 'fData' is always owned by someone else. It either shares the span 94 // of from the Builder, or clone() puts the span in an arena. 95 SkSpan<const int32_t> fData; 96 }; 97 98 // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same 99 // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks' 100 // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder 101 // can only have one extant key and that key must be destroyed before the builder can be reused 102 // to create another one. 103 // 104 // This arrangement is intended to improve performance in the expected case, where a builder is 105 // being used in a tight loop to generate keys which can be recycled once they've been used to 106 // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory 107 // into the dictionary to be prohibitive since that should be infrequent. 108 class PaintParamsKeyBuilder { 109 public: PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)110 PaintParamsKeyBuilder(const ShaderCodeDictionary* dict) { 111 SkDEBUGCODE(fDict = dict;) 112 } 113 ~PaintParamsKeyBuilder()114 ~PaintParamsKeyBuilder() { SkASSERT(!fLocked); } 115 beginBlock(BuiltInCodeSnippetID id)116 void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); } beginBlock(int32_t codeSnippetID)117 void beginBlock(int32_t codeSnippetID) { 118 SkASSERT(!fLocked); 119 SkDEBUGCODE(this->pushStack(codeSnippetID);) 120 fData.push_back(codeSnippetID); 121 } 122 123 // TODO: Have endBlock() be handled automatically with RAII, in which case we could have it 124 // validate the snippet ID being popped off the stack frame. endBlock()125 void endBlock() { 126 SkDEBUGCODE(this->popStack();) 127 } 128 129 #ifdef SK_DEBUG 130 // Check that the builder has been reset to its initial state prior to creating a new key. 131 void checkReset(); 132 #endif 133 134 // Helper to add blocks that don't have children addBlock(BuiltInCodeSnippetID id)135 void addBlock(BuiltInCodeSnippetID id) { 136 this->beginBlock(id); 137 this->endBlock(); 138 } 139 140 private: 141 friend class AutoLockBuilderAsKey; // for lockAsKey() and unlock() 142 143 // Returns a view of this builder as a PaintParamsKey. The Builder cannot be used until the 144 // returned Key goes out of scope. lockAsKey()145 PaintParamsKey lockAsKey() { 146 SkASSERT(!fLocked); // lockAsKey() is not re-entrant 147 SkASSERT(fStack.empty()); // All beginBlocks() had a matching endBlock() 148 149 SkDEBUGCODE(fLocked = true;) 150 return PaintParamsKey({fData.data(), fData.size()}); 151 } 152 153 // Invalidates any PaintParamsKey returned by lockAsKey() unless it has been cloned. unlock()154 void unlock() { 155 SkASSERT(fLocked); 156 fData.clear(); 157 158 SkDEBUGCODE(fLocked = false;) 159 SkDEBUGCODE(fStack.clear();) 160 SkDEBUGCODE(this->checkReset();) 161 } 162 163 // The data array uses clear() on unlock so that it's underlying storage and repeated use of the 164 // builder will hit a high-water mark and avoid lots of allocations when recording draws. 165 skia_private::TArray<int32_t> fData; 166 167 #ifdef SK_DEBUG 168 void pushStack(int32_t codeSnippetID); 169 void popStack(); 170 171 // Information about the current block being written 172 struct StackFrame { 173 int fCodeSnippetID; 174 int fNumExpectedChildren; 175 int fNumActualChildren = 0; 176 }; 177 178 const ShaderCodeDictionary* fDict; 179 skia_private::TArray<StackFrame> fStack; 180 bool fLocked = false; 181 #endif 182 }; 183 184 class AutoLockBuilderAsKey { 185 public: AutoLockBuilderAsKey(PaintParamsKeyBuilder * builder)186 AutoLockBuilderAsKey(PaintParamsKeyBuilder* builder) 187 : fBuilder(builder) 188 , fKey(builder->lockAsKey()) {} 189 ~AutoLockBuilderAsKey()190 ~AutoLockBuilderAsKey() { 191 fBuilder->unlock(); 192 } 193 194 // Use as a PaintParamsKey 195 const PaintParamsKey& operator*() const { return fKey; } 196 const PaintParamsKey* operator->() const { return &fKey; } 197 198 private: 199 PaintParamsKeyBuilder* fBuilder; 200 PaintParamsKey fKey; 201 }; 202 203 } // namespace skgpu::graphite 204 205 #endif // skgpu_graphite_PaintParamsKey_DEFINED 206