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