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/SkColor.h" 12 #include "include/core/SkSpan.h" 13 #include "include/core/SkTypes.h" 14 #include "include/private/base/SkMacros.h" 15 #include "include/private/base/SkTDArray.h" 16 #include "src/gpu/Blend.h" 17 #include "src/gpu/graphite/BuiltInCodeSnippetID.h" 18 19 #include <array> 20 #include <limits> 21 22 23 namespace skgpu::graphite { 24 25 class PaintParamsKeyBuilder; 26 class ShaderCodeDictionary; 27 class ShaderInfo; 28 struct ShaderSnippet; 29 30 // This class is a compact representation of the shader needed to implement a given 31 // PaintParams. Its structure is a series of blocks where each block has a 32 // Header, consisting of 2 bytes: 33 // 4 bytes: code-snippet ID 34 // 1 byte: size of the block, in bytes (header, plus all data payload bytes) 35 // The rest of the data in the block is dependent on the individual code snippet. 36 // If a given block has child blocks, they appear in the key right after their parent 37 // block's header. 38 class PaintParamsKey { 39 public: 40 #pragma pack(push, 1) 41 struct Header { 42 int32_t codeSnippetID; 43 uint8_t blockSize; 44 }; 45 #pragma pack(pop) 46 47 static const int kBlockSizeOffsetInBytes = offsetof(Header, blockSize); 48 static const int kMaxBlockSize = std::numeric_limits<uint8_t>::max(); 49 50 enum class DataPayloadType { 51 kByte, 52 kInt, 53 kFloat4, 54 }; 55 56 // A given snippet's data payload is stored as an SkSpan of DataPayloadFields in the 57 // ShaderCodeDictionary. That span just defines the structure of the data payload. The actual 58 // data is stored in the paint params key. 59 struct DataPayloadField { 60 const char* fName; 61 DataPayloadType fType; 62 uint32_t fCount; 63 }; 64 65 ~PaintParamsKey(); 66 67 class BlockReader { 68 public: 69 // Returns the combined size of the header, all children, and every data payload. blockSize()70 uint8_t blockSize() const { 71 SkASSERT(fBlock[kBlockSizeOffsetInBytes] == fBlock.size()); 72 return SkTo<uint8_t>(fBlock.size()); 73 } 74 75 int numChildren() const; 76 77 // Returns the code-snippet ID of this block. 78 int32_t codeSnippetId() const; 79 80 // Return the childIndex-th child's BlockReader 81 BlockReader child(const ShaderCodeDictionary*, int childIndex) const; 82 83 // Retrieve the fieldIndex-th field in the data payload as a span. The type being read 84 // is checked against the data payload's structure. 85 SkSpan<const uint8_t> bytes(int fieldIndex) const; 86 SkSpan<const int32_t> ints(int fieldIndex) const; 87 SkSpan<const SkColor4f> colors(int fieldIndex) const; 88 entry()89 const ShaderSnippet* entry() const { return fEntry; } 90 91 #ifdef SK_DEBUG 92 int numDataPayloadFields() const; 93 void dump(const ShaderCodeDictionary*, int indent) const; 94 #endif 95 96 private: 97 friend class PaintParamsKey; // for ctor 98 99 BlockReader(const ShaderCodeDictionary*, 100 SkSpan<const uint8_t> parentSpan, 101 int offsetInParent); 102 103 // The data payload appears after any children and occupies the remainder of the 104 // block's space. 105 SkSpan<const uint8_t> dataPayload() const; 106 107 SkSpan<const uint8_t> fBlock; 108 const ShaderSnippet* fEntry; 109 }; 110 111 BlockReader reader(const ShaderCodeDictionary*, int headerOffset) const; 112 113 #ifdef SK_DEBUG byte(int offset)114 uint8_t byte(int offset) const { 115 SkASSERT(offset < this->sizeInBytes()); 116 return fData[offset]; 117 } 118 void dump(const ShaderCodeDictionary*) const; 119 #endif 120 void toShaderInfo(const ShaderCodeDictionary*, ShaderInfo*) const; 121 asSpan()122 SkSpan<const uint8_t> asSpan() const { return fData; } data()123 const uint8_t* data() const { return fData.data(); } sizeInBytes()124 int sizeInBytes() const { return SkTo<int>(fData.size()); } 125 126 bool operator==(const PaintParamsKey& that) const; 127 bool operator!=(const PaintParamsKey& that) const { return !(*this == that); } 128 129 #if GRAPHITE_TEST_UTILS 130 bool isErrorKey() const; 131 #endif 132 133 private: 134 friend class PaintParamsKeyBuilder; // for the parented-data ctor 135 friend class ShaderCodeDictionary; // for the raw-data ctor 136 137 // This ctor is to be used when paintparams keys are being consecutively generated 138 // by a key builder. The memory backing this key's span is shared between the 139 // builder and its keys. 140 PaintParamsKey(SkSpan<const uint8_t> span, PaintParamsKeyBuilder* originatingBuilder); 141 142 // This ctor is used when this key isn't being created by a builder (i.e., when the key 143 // is in the dictionary). In this case the dictionary will own the memory backing the span. 144 PaintParamsKey(SkSpan<const uint8_t> rawData); 145 146 static void AddBlockToShaderInfo(const ShaderCodeDictionary*, 147 const BlockReader&, 148 ShaderInfo*); 149 150 // The memory referenced in 'fData' is always owned by someone else. 151 // If 'fOriginatingBuilder' is null, the dictionary's SkArena owns the 'fData' memory and no 152 // explicit freeing is required. 153 // If 'fOriginatingBuilder' is non-null then the 'fData' memory must be explicitly locked (in 154 // the ctor) and unlocked (in the dtor) on the 'fOriginatingBuilder' object. 155 SkSpan<const uint8_t> fData; 156 157 // This class should only ever access the 'lock' and 'unlock' calls on 'fOriginatingBuilder' 158 PaintParamsKeyBuilder* fOriginatingBuilder; 159 }; 160 161 // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same 162 // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks' 163 // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder 164 // can only have one extant key and that key must be destroyed before the builder can be reused 165 // to create another one. 166 // 167 // This arrangement is intended to improve performance in the expected case, where a builder is 168 // being used in a tight loop to generate keys which can be recycled once they've been used to 169 // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory 170 // into the dictionary to be prohibitive since that should be infrequent. 171 class PaintParamsKeyBuilder { 172 public: 173 PaintParamsKeyBuilder(const ShaderCodeDictionary*); ~PaintParamsKeyBuilder()174 ~PaintParamsKeyBuilder() { 175 SkASSERT(!this->isLocked()); 176 } 177 setBlendInfo(const skgpu::BlendInfo & blendInfo)178 void setBlendInfo(const skgpu::BlendInfo& blendInfo) { 179 fBlendInfo = blendInfo; 180 } blendInfo()181 const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; } 182 183 void beginBlock(int32_t codeSnippetID); beginBlock(BuiltInCodeSnippetID id)184 void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); } 185 void endBlock(); 186 187 void addBytes(uint32_t numBytes, const uint8_t* data); addByte(uint8_t data)188 void addByte(uint8_t data) { 189 this->addBytes(1, &data); 190 } 191 void addInts(uint32_t numInts, const int32_t* data); addInt(int32_t data)192 void addInt(int32_t data) { 193 this->addInts(1, &data); 194 } 195 void add(int numColors, const SkColor4f* colors); add(const SkColor4f & color)196 void add(const SkColor4f& color) { 197 this->add(/*numColors=*/1, &color); 198 } 199 200 #ifdef SK_DEBUG 201 // Check that the builder has been reset to its initial state prior to creating a new key. 202 void checkReset(); byte(int offset)203 uint8_t byte(int offset) const { return fData[offset]; } 204 #endif 205 206 PaintParamsKey lockAsKey(); 207 sizeInBytes()208 int sizeInBytes() const { return fData.size(); } 209 isValid()210 bool isValid() const { return fIsValid; } 211 lock()212 void lock() { 213 SkASSERT(!fLocked); 214 SkDEBUGCODE(fLocked = true;) 215 } 216 unlock()217 void unlock() { 218 SkASSERT(fLocked); 219 fData.clear(); 220 fBlendInfo = {}; 221 222 SkDEBUGCODE(fLocked = false;) 223 SkDEBUGCODE(this->checkReset();) 224 } 225 226 SkDEBUGCODE(bool isLocked() const { return fLocked; }) 227 228 private: 229 void addToKey(uint32_t count, const void* data, PaintParamsKey::DataPayloadType payloadType); 230 void makeInvalid(); 231 232 #ifdef SK_DEBUG 233 void checkExpectations(PaintParamsKey::DataPayloadType actualType, uint32_t actualCount); 234 #endif 235 236 // Information about the current block being written 237 struct StackFrame { 238 int fCodeSnippetID; 239 int fHeaderOffset; 240 #ifdef SK_DEBUG 241 SkSpan<const PaintParamsKey::DataPayloadField> fDataPayloadExpectations; 242 int fCurDataPayloadEntry = 0; 243 int fNumExpectedChildren = 0; 244 int fNumActualChildren = 0; 245 #endif 246 }; 247 248 const ShaderCodeDictionary* fDict; 249 250 bool fIsValid = true; 251 SkDEBUGCODE(bool fLocked = false;) 252 253 // Use SkTDArray so that we can guarantee that rewind() preserves the underlying storage and 254 // repeated use of the builder will hit a high-water mark and avoid lots of allocations. 255 SkTDArray<StackFrame> fStack; 256 SkTDArray<uint8_t> fData; 257 skgpu::BlendInfo fBlendInfo; 258 }; 259 260 } // skgpu::graphite 261 262 #endif // skgpu_graphite_PaintParamsKey_DEFINED 263