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 74 ~PaintParamsKey() = default; 75 PaintParamsKey& operator=(const PaintParamsKey&) = default; 76 Invalid()77 static constexpr PaintParamsKey Invalid() { return PaintParamsKey(SkSpan<const uint32_t>()); } isValid()78 bool isValid() const { return !fData.empty(); } 79 80 // Return a PaintParamsKey whose data is owned by the provided arena and is not attached to 81 // a PaintParamsKeyBuilder. The caller must ensure that the SkArenaAlloc remains alive longer 82 // than the returned key. 83 PaintParamsKey clone(SkArenaAlloc*) const; 84 85 // Converts the key into a forest of ShaderNode trees. If the key is valid this will return at 86 // least one root node. If the key contains unknown shader snippet IDs, returns an empty span. 87 // All shader nodes, and the returned span's backing data, are owned by the provided arena. 88 // 89 // A valid key will produce either 2 or 3 root nodes. The first root node represents how the 90 // source color is computed. The second node defines the final blender between the calculated 91 // source color and the current pixel's dst color. If provided, the third node calculates an 92 // additional analytic coverage value to combine with the geometry's coverage. 93 SkSpan<const ShaderNode*> getRootNodes(const ShaderCodeDictionary*, SkArenaAlloc*) const; 94 95 // Converts the key to a structured list of snippet information for debugging or labeling 96 // purposes. 97 SkString toString(const ShaderCodeDictionary* dict, bool includeData) const; 98 99 #ifdef SK_DEBUG 100 void dump(const ShaderCodeDictionary*, UniquePaintParamsID) const; 101 #endif 102 103 bool operator==(const PaintParamsKey& that) const { 104 return fData.size() == that.fData.size() && 105 !memcmp(fData.data(), that.fData.data(), fData.size()); 106 } 107 bool operator!=(const PaintParamsKey& that) const { return !(*this == that); } 108 109 struct Hash { operatorHash110 uint32_t operator()(const PaintParamsKey& k) const { 111 return SkChecksum::Hash32(k.fData.data(), k.fData.size_bytes()); 112 } 113 }; 114 115 private: 116 friend class PaintParamsKeyBuilder; // for the parented-data ctor 117 PaintParamsKey(SkSpan<const uint32_t> span)118 constexpr PaintParamsKey(SkSpan<const uint32_t> span) : fData(span) {} 119 120 // Returns null if the node or any of its children have an invalid snippet ID. Recursively 121 // creates a node and all of its children, incrementing 'currentIndex' by the total number of 122 // nodes created. 123 const ShaderNode* createNode(const ShaderCodeDictionary*, 124 int* currentIndex, 125 SkArenaAlloc* arena) const; 126 127 // The memory referenced in 'fData' is always owned by someone else. It either shares the span 128 // from the Builder, or clone() puts the span in an arena. 129 SkSpan<const uint32_t> fData; 130 }; 131 132 // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same 133 // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks' 134 // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder 135 // can only have one extant key and that key must be destroyed before the builder can be reused 136 // to create another one. 137 // 138 // This arrangement is intended to improve performance in the expected case, where a builder is 139 // being used in a tight loop to generate keys which can be recycled once they've been used to 140 // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory 141 // into the dictionary to be prohibitive since that should be infrequent. 142 class PaintParamsKeyBuilder { 143 public: PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)144 PaintParamsKeyBuilder(const ShaderCodeDictionary* dict) { 145 SkDEBUGCODE(fDict = dict;) 146 } 147 ~PaintParamsKeyBuilder()148 ~PaintParamsKeyBuilder() { SkASSERT(!fLocked); } 149 beginBlock(BuiltInCodeSnippetID id)150 void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); } beginBlock(int32_t codeSnippetID)151 void beginBlock(int32_t codeSnippetID) { 152 SkASSERT(!fLocked); 153 SkDEBUGCODE(this->pushStack(codeSnippetID);) 154 fData.push_back(codeSnippetID); 155 } 156 157 // TODO: Have endBlock() be handled automatically with RAII, in which case we could have it 158 // validate the snippet ID being popped off the stack frame. endBlock()159 void endBlock() { 160 SkDEBUGCODE(this->popStack();) 161 } 162 163 #ifdef SK_DEBUG 164 // Check that the builder has been reset to its initial state prior to creating a new key. 165 void checkReset(); 166 #endif 167 168 // Helper to add blocks that don't have children addBlock(BuiltInCodeSnippetID id)169 void addBlock(BuiltInCodeSnippetID id) { 170 this->beginBlock(id); 171 this->endBlock(); 172 } 173 addData(SkSpan<const uint32_t> data)174 void addData(SkSpan<const uint32_t> data) { 175 // First push the data size followed by the actual data. 176 SkDEBUGCODE(this->validateData(data.size())); 177 fData.push_back(data.size()); 178 fData.push_back_n(data.size(), data.begin()); 179 } 180 181 private: 182 friend class AutoLockBuilderAsKey; // for lockAsKey() and unlock() 183 184 // Returns a view of this builder as a PaintParamsKey. The Builder cannot be used until the 185 // returned Key goes out of scope. lockAsKey()186 PaintParamsKey lockAsKey() { 187 SkASSERT(!fLocked); // lockAsKey() is not re-entrant 188 SkASSERT(fStack.empty()); // All beginBlocks() had a matching endBlock() 189 190 SkDEBUGCODE(fLocked = true;) 191 return PaintParamsKey({fData.data(), fData.size()}); 192 } 193 194 // Invalidates any PaintParamsKey returned by lockAsKey() unless it has been cloned. unlock()195 void unlock() { 196 SkASSERT(fLocked); 197 fData.clear(); 198 199 SkDEBUGCODE(fLocked = false;) 200 SkDEBUGCODE(fStack.clear();) 201 SkDEBUGCODE(this->checkReset();) 202 } 203 204 // The data array uses clear() on unlock so that it's underlying storage and repeated use of the 205 // builder will hit a high-water mark and avoid lots of allocations when recording draws. 206 skia_private::TArray<uint32_t> fData; 207 208 #ifdef SK_DEBUG 209 void pushStack(int32_t codeSnippetID); 210 void validateData(size_t dataSize); 211 void popStack(); 212 213 // Information about the current block being written 214 struct StackFrame { 215 int fCodeSnippetID; 216 int fNumExpectedChildren; 217 int fNumActualChildren = 0; 218 int fDataSize = -1; 219 }; 220 221 const ShaderCodeDictionary* fDict; 222 skia_private::TArray<StackFrame> fStack; 223 bool fLocked = false; 224 #endif 225 }; 226 227 class AutoLockBuilderAsKey { 228 public: AutoLockBuilderAsKey(PaintParamsKeyBuilder * builder)229 AutoLockBuilderAsKey(PaintParamsKeyBuilder* builder) 230 : fBuilder(builder) 231 , fKey(builder->lockAsKey()) {} 232 ~AutoLockBuilderAsKey()233 ~AutoLockBuilderAsKey() { 234 fBuilder->unlock(); 235 } 236 237 // Use as a PaintParamsKey 238 const PaintParamsKey& operator*() const { return fKey; } 239 const PaintParamsKey* operator->() const { return &fKey; } 240 241 private: 242 PaintParamsKeyBuilder* fBuilder; 243 PaintParamsKey fKey; 244 }; 245 246 } // namespace skgpu::graphite 247 248 #endif // skgpu_graphite_PaintParamsKey_DEFINED 249