• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/gpu/graphite/PaintParamsKey.h"
9 
10 #include <cstring>
11 #include "src/gpu/graphite/KeyHelpers.h"
12 #include "src/gpu/graphite/ShaderCodeDictionary.h"
13 
14 namespace skgpu::graphite {
15 
16 using DataPayloadType  = PaintParamsKey::DataPayloadType;
17 using DataPayloadField = PaintParamsKey::DataPayloadField;
18 
19 //--------------------------------------------------------------------------------------------------
read_header(SkSpan<const uint8_t> parentSpan,int headerOffset)20 static PaintParamsKey::Header read_header(SkSpan<const uint8_t> parentSpan, int headerOffset) {
21     SkASSERT(headerOffset + sizeof(PaintParamsKey::Header) <= parentSpan.size());
22 
23     PaintParamsKey::Header header;
24     memcpy(&header, &parentSpan[headerOffset], sizeof(PaintParamsKey::Header));
25     SkASSERT(header.blockSize >= sizeof(PaintParamsKey::Header));
26     SkASSERT(headerOffset + header.blockSize <= static_cast<int>(parentSpan.size()));
27 
28     return header;
29 }
30 
31 //--------------------------------------------------------------------------------------------------
PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)32 PaintParamsKeyBuilder::PaintParamsKeyBuilder(const ShaderCodeDictionary* dict)
33         : fDict(dict) {}
34 
35 #ifdef SK_DEBUG
checkReset()36 void PaintParamsKeyBuilder::checkReset() {
37     SkASSERT(!this->isLocked());
38     SkASSERT(this->sizeInBytes() == 0);
39     SkASSERT(fIsValid);
40     SkASSERT(fStack.empty());
41     SkASSERT(fBlendInfo == skgpu::BlendInfo());
42 }
43 #endif
44 
45 // Block headers have the following structure:
46 //   4 bytes: code-snippet ID
47 //   1 byte: size of the block, in bytes (header, plus all data payload bytes)
48 // This call stores the header's offset in the key on the stack to be used in 'endBlock'
beginBlock(int32_t codeSnippetID)49 void PaintParamsKeyBuilder::beginBlock(int32_t codeSnippetID) {
50     if (!this->isValid()) {
51         return;
52     }
53 
54     if (!fDict->isValidID(codeSnippetID)) {
55         // SKGPU_LOG_W("Unknown code snippet ID.");
56         this->makeInvalid();
57         return;
58     }
59 
60 #ifdef SK_DEBUG
61     if (!fStack.empty()) {
62         // The children of a block should appear before any of the parent's data
63         SkASSERT(fStack.back().fCurDataPayloadEntry == 0);
64         fStack.back().fNumActualChildren++;
65     }
66 
67     static constexpr DataPayloadField kHeader[2] = {
68             {"snippetID", DataPayloadType::kInt, 1},
69             {"blockSize", DataPayloadType::kByte, 1},
70     };
71 
72     static const SkSpan<const DataPayloadField> kHeaderExpectations(kHeader);
73 #endif
74 
75     SkASSERT(!this->isLocked());
76 
77     fStack.push_back({ codeSnippetID, this->sizeInBytes(),
78                        SkDEBUGCODE(kHeaderExpectations, 0) });
79 
80     this->addInt(codeSnippetID);
81     this->addByte(0);  // this will be filled in when endBlock is called
82 
83 #ifdef SK_DEBUG
84     const ShaderSnippet* snippet = fDict->getEntry(codeSnippetID);
85 
86     fStack.back().fDataPayloadExpectations = snippet->fDataPayloadExpectations;
87     fStack.back().fCurDataPayloadEntry = 0;
88     fStack.back().fNumExpectedChildren = snippet->fNumChildren;
89     fStack.back().fNumActualChildren = 0;
90 #endif
91 }
92 
93 // Update the size byte of a block header
endBlock()94 void PaintParamsKeyBuilder::endBlock() {
95     if (!this->isValid()) {
96         return;
97     }
98 
99     if (fStack.empty()) {
100         // SKGPU_LOG_W("Mismatched beginBlock/endBlocks.");
101         this->makeInvalid();
102         return;
103     }
104 
105     // All the expected fields should be filled in at this point
106     SkASSERT(fStack.back().fCurDataPayloadEntry ==
107              SkTo<int>(fStack.back().fDataPayloadExpectations.size()));
108     SkASSERT(fStack.back().fNumActualChildren == fStack.back().fNumExpectedChildren);
109     SkASSERT(!this->isLocked());
110 
111     int headerOffset = fStack.back().fHeaderOffset;
112 
113 #ifdef SK_DEBUG
114     // We don't use `read_header` here because the header block size isn't valid yet.
115     PaintParamsKey::Header header;
116     memcpy(&header, &fData[headerOffset], sizeof(PaintParamsKey::Header));
117     SkASSERT(header.codeSnippetID == fStack.back().fCodeSnippetID);
118     SkASSERT(header.blockSize == 0);
119 #endif
120 
121     int blockSize = this->sizeInBytes() - headerOffset;
122     if (blockSize > PaintParamsKey::kMaxBlockSize) {
123         // SKGPU_LOG_W("Key's data payload is too large.");
124         this->makeInvalid();
125         return;
126     }
127 
128     fData[headerOffset + PaintParamsKey::kBlockSizeOffsetInBytes] = blockSize;
129 
130     fStack.pop_back();
131 
132 #ifdef SK_DEBUG
133     if (!fStack.empty()) {
134         // The children of a block should appear before any of the parent's data
135         SkASSERT(fStack.back().fCurDataPayloadEntry == 0);
136     }
137 #endif
138 }
139 
140 #ifdef SK_DEBUG
checkExpectations(DataPayloadType actualType,uint32_t actualCount)141 void PaintParamsKeyBuilder::checkExpectations(DataPayloadType actualType, uint32_t actualCount) {
142     StackFrame& frame = fStack.back();
143     const auto& expectations = frame.fDataPayloadExpectations;
144 
145     // TODO: right now we reject writing 'n' bytes one at a time. We could allow it by tracking
146     // the number of bytes written in the stack frame.
147     SkASSERT(expectations[frame.fCurDataPayloadEntry].fType == actualType);
148     SkASSERT(expectations[frame.fCurDataPayloadEntry].fCount == actualCount);
149 
150     frame.fCurDataPayloadEntry++;
151 }
152 #endif // SK_DEBUG
153 
field_size(DataPayloadType type)154 static int field_size(DataPayloadType type) {
155     switch (type) {
156         case DataPayloadType::kByte:   return 1;
157         case DataPayloadType::kInt:    return 4;
158         case DataPayloadType::kFloat4: return 16;
159     }
160     SkUNREACHABLE;
161 }
162 
addToKey(uint32_t count,const void * data,DataPayloadType payloadType)163 void PaintParamsKeyBuilder::addToKey(uint32_t count,
164                                      const void* data,
165                                      DataPayloadType payloadType) {
166     if (!this->isValid()) {
167         return;
168     }
169 
170     if (fStack.empty()) {
171         // SKGPU_LOG_W("Missing call to 'beginBlock'.");
172         this->makeInvalid();
173         return;
174     }
175 
176     SkDEBUGCODE(this->checkExpectations(payloadType, count);)
177     SkASSERT(!this->isLocked());
178 
179     fData.append(field_size(payloadType) * count, reinterpret_cast<const uint8_t*>(data));
180 }
181 
addBytes(uint32_t numBytes,const uint8_t * data)182 void PaintParamsKeyBuilder::addBytes(uint32_t numBytes, const uint8_t* data) {
183     this->addToKey(numBytes, data, DataPayloadType::kByte);
184 }
185 
addInts(uint32_t numInts,const int32_t * data)186 void PaintParamsKeyBuilder::addInts(uint32_t numInts, const int32_t* data) {
187     this->addToKey(numInts, data, DataPayloadType::kInt);
188 }
189 
add(int numColors,const SkColor4f * colors)190 void PaintParamsKeyBuilder::add(int numColors, const SkColor4f* colors) {
191     this->addToKey(numColors, colors, DataPayloadType::kFloat4);
192 }
193 
lockAsKey()194 PaintParamsKey PaintParamsKeyBuilder::lockAsKey() {
195     if (!fStack.empty()) {
196         // SKGPU_LOG_W("Mismatched beginBlock/endBlocks.");
197         this->makeInvalid();  // fall through
198     }
199 
200     SkASSERT(!this->isLocked());
201 
202     // Partially reset for reuse. Note that the key resulting from this call will be holding a lock
203     // on this builder and must be deleted before this builder is fully reset.
204     fIsValid = true;
205     fStack.clear();
206 
207     return PaintParamsKey(SkSpan(fData.begin(), fData.size()), this);
208 }
209 
makeInvalid()210 void PaintParamsKeyBuilder::makeInvalid() {
211     SkASSERT(fIsValid);
212     SkASSERT(!this->isLocked());
213 
214     fStack.clear();
215     fData.clear();
216     this->beginBlock(BuiltInCodeSnippetID::kError);
217     this->endBlock();
218 
219     SkASSERT(fIsValid);
220     fIsValid = false;
221 }
222 
223 //--------------------------------------------------------------------------------------------------
PaintParamsKey(SkSpan<const uint8_t> span,PaintParamsKeyBuilder * originatingBuilder)224 PaintParamsKey::PaintParamsKey(SkSpan<const uint8_t> span,
225                                PaintParamsKeyBuilder* originatingBuilder)
226         : fData(span)
227         , fOriginatingBuilder(originatingBuilder) {
228     fOriginatingBuilder->lock();
229 }
230 
PaintParamsKey(SkSpan<const uint8_t> rawData)231 PaintParamsKey::PaintParamsKey(SkSpan<const uint8_t> rawData)
232         : fData(rawData)
233         , fOriginatingBuilder(nullptr) {
234 }
235 
~PaintParamsKey()236 PaintParamsKey::~PaintParamsKey() {
237     if (fOriginatingBuilder) {
238         fOriginatingBuilder->unlock();
239     }
240 }
241 
operator ==(const PaintParamsKey & that) const242 bool PaintParamsKey::operator==(const PaintParamsKey& that) const {
243     return fData.size() == that.fData.size() &&
244            !memcmp(fData.data(), that.fData.data(), fData.size());
245 }
246 
reader(const ShaderCodeDictionary * dict,int headerOffset) const247 PaintParamsKey::BlockReader PaintParamsKey::reader(const ShaderCodeDictionary* dict,
248                                                    int headerOffset) const {
249     return BlockReader(dict, fData, headerOffset);
250 }
251 
252 #ifdef SK_DEBUG
253 
254 // This just iterates over the top-level blocks calling block-specific dump methods.
dump(const ShaderCodeDictionary * dict) const255 void PaintParamsKey::dump(const ShaderCodeDictionary* dict) const {
256     SkDebugf("--------------------------------------\n");
257     SkDebugf("PaintParamsKey (%dB):\n", this->sizeInBytes());
258 
259     int curHeaderOffset = 0;
260     while (curHeaderOffset < this->sizeInBytes()) {
261         BlockReader reader = this->reader(dict, curHeaderOffset);
262         reader.dump(dict, /*indent=*/0);
263         curHeaderOffset += reader.blockSize();
264     }
265 }
266 #endif // SK_DEBUG
267 
AddBlockToShaderInfo(const ShaderCodeDictionary * dict,const PaintParamsKey::BlockReader & reader,ShaderInfo * result)268 void PaintParamsKey::AddBlockToShaderInfo(const ShaderCodeDictionary* dict,
269                                           const PaintParamsKey::BlockReader& reader,
270                                           ShaderInfo* result) {
271 
272     result->add(reader);
273     result->addFlags(dict->getEntry(reader.codeSnippetId())->fSnippetRequirementFlags);
274 
275     // The child blocks appear right after the parent block's header in the key and go
276     // right after the parent's SnippetEntry in the shader info
277     for (int i = 0; i < reader.numChildren(); ++i) {
278         BlockReader childReader = reader.child(dict, i);
279 
280         AddBlockToShaderInfo(dict, childReader, result);
281     }
282 }
283 
toShaderInfo(const ShaderCodeDictionary * dict,ShaderInfo * result) const284 void PaintParamsKey::toShaderInfo(const ShaderCodeDictionary* dict,
285                                   ShaderInfo* result) const {
286 
287     int curHeaderOffset = 0;
288     while (curHeaderOffset < this->sizeInBytes()) {
289         PaintParamsKey::BlockReader reader = this->reader(dict, curHeaderOffset);
290         AddBlockToShaderInfo(dict, reader, result);
291         curHeaderOffset += reader.blockSize();
292     }
293 }
294 
295 #if GRAPHITE_TEST_UTILS
isErrorKey() const296 bool PaintParamsKey::isErrorKey() const {
297     if (this->sizeInBytes() != sizeof(Header)) {
298         return false;
299     }
300     Header header = read_header(this->asSpan(), /*headerOffset=*/0);
301     return header.codeSnippetID == (int32_t) BuiltInCodeSnippetID::kError &&
302            header.blockSize == sizeof(Header);
303 }
304 #endif
305 
306 ////////////////////////////////////////////////////////////////////////////////////////////////////
307 
BlockReader(const ShaderCodeDictionary * dict,SkSpan<const uint8_t> parentSpan,int offsetInParent)308 PaintParamsKey::BlockReader::BlockReader(const ShaderCodeDictionary* dict,
309                                          SkSpan<const uint8_t> parentSpan,
310                                          int offsetInParent) {
311     Header header = read_header(parentSpan, offsetInParent);
312 
313     fBlock = parentSpan.subspan(offsetInParent, header.blockSize);
314     fEntry = dict->getEntry(header.codeSnippetID);
315     SkASSERT(fEntry);
316 }
317 
numChildren() const318 int PaintParamsKey::BlockReader::numChildren() const { return fEntry->fNumChildren; }
319 
child(const ShaderCodeDictionary * dict,int childIndex) const320 PaintParamsKey::BlockReader PaintParamsKey::BlockReader::child(
321         const ShaderCodeDictionary* dict,
322         int childIndex) const {
323     SkASSERT(childIndex < fEntry->fNumChildren);
324 
325     int childOffset = sizeof(Header);
326     for (int i = 0; i < childIndex; ++i) {
327         Header header = read_header(fBlock, childOffset);
328         childOffset += header.blockSize;
329     }
330 
331     return BlockReader(dict, fBlock, childOffset);
332 }
333 
codeSnippetId() const334 int32_t PaintParamsKey::BlockReader::codeSnippetId() const {
335     Header header = read_header(fBlock, 0);
336     return header.codeSnippetID;
337 }
338 
dataPayload() const339 SkSpan<const uint8_t> PaintParamsKey::BlockReader::dataPayload() const {
340     int payloadOffset = sizeof(Header);
341     for (int i = 0; i < fEntry->fNumChildren; ++i) {
342         Header header = read_header(fBlock, payloadOffset);
343         payloadOffset += header.blockSize;
344     }
345 
346     int payloadSize = this->blockSize() - payloadOffset;
347     return fBlock.subspan(payloadOffset, payloadSize);
348 }
349 
field_offset(SkSpan<const DataPayloadField> fields,int fieldIndex)350 static int field_offset(SkSpan<const DataPayloadField> fields, int fieldIndex) {
351     int byteOffset = 0;
352     for (int i = 0; i < fieldIndex; ++i) {
353         byteOffset += field_size(fields[i].fType) * fields[i].fCount;
354     }
355     return byteOffset;
356 }
357 
358 template <typename T>
payload_subspan_for_field(SkSpan<const uint8_t> dataPayload,SkSpan<const DataPayloadField> fields,int fieldIndex)359 static SkSpan<const T> payload_subspan_for_field(SkSpan<const uint8_t> dataPayload,
360                                                  SkSpan<const DataPayloadField> fields,
361                                                  int fieldIndex) {
362     int offset = field_offset(fields, fieldIndex);
363     return {reinterpret_cast<const T*>(&dataPayload[offset]), fields[fieldIndex].fCount};
364 }
365 
bytes(int fieldIndex) const366 SkSpan<const uint8_t> PaintParamsKey::BlockReader::bytes(int fieldIndex) const {
367     SkASSERT(fEntry->fDataPayloadExpectations[fieldIndex].fType == DataPayloadType::kByte);
368     return payload_subspan_for_field<uint8_t>(this->dataPayload(),
369                                               fEntry->fDataPayloadExpectations,
370                                               fieldIndex);
371 }
372 
ints(int fieldIndex) const373 SkSpan<const int32_t> PaintParamsKey::BlockReader::ints(int fieldIndex) const {
374     SkASSERT(fEntry->fDataPayloadExpectations[fieldIndex].fType == DataPayloadType::kInt);
375     return payload_subspan_for_field<int32_t>(this->dataPayload(),
376                                               fEntry->fDataPayloadExpectations,
377                                               fieldIndex);
378 }
379 
colors(int fieldIndex) const380 SkSpan<const SkColor4f> PaintParamsKey::BlockReader::colors(int fieldIndex) const {
381     SkASSERT(fEntry->fDataPayloadExpectations[fieldIndex].fType == DataPayloadType::kFloat4);
382     return payload_subspan_for_field<SkColor4f>(this->dataPayload(),
383                                                 fEntry->fDataPayloadExpectations,
384                                                 fieldIndex);
385 }
386 
387 #ifdef SK_DEBUG
388 
numDataPayloadFields() const389 int PaintParamsKey::BlockReader::numDataPayloadFields() const {
390     return fEntry->fDataPayloadExpectations.size();
391 }
392 
output_indent(int indent)393 static void output_indent(int indent) {
394     SkDebugf("%*c", 4 * indent, ' ');
395 }
396 
dump(const ShaderCodeDictionary * dict,int indent) const397 void PaintParamsKey::BlockReader::dump(const ShaderCodeDictionary* dict, int indent) const {
398     uint8_t id = static_cast<uint8_t>(this->codeSnippetId());
399     uint8_t blockSize = this->blockSize();
400 
401     auto entry = dict->getEntry(id);
402     if (!entry) {
403         output_indent(indent);
404         SkDebugf("unknown block! (%dB)\n", blockSize);
405     }
406 
407     output_indent(indent);
408     SkDebugf("%s block (%dB)\n", entry->fStaticFunctionName, blockSize);
409 
410     for (int i = 0; i < this->numChildren(); ++i) {
411         output_indent(indent);
412         // TODO: it would be nice if the names of the children were also stored (i.e., "src"/"dst")
413         SkDebugf("child %d:\n", i);
414 
415         PaintParamsKey::BlockReader childReader = this->child(dict, i);
416         childReader.dump(dict, indent+1);
417     }
418 
419     for (int i = 0; i < (int) fEntry->fDataPayloadExpectations.size(); ++i) {
420         output_indent(indent);
421         SkDebugf("%s[%d]: ",
422                  fEntry->fDataPayloadExpectations[i].fName,
423                  fEntry->fDataPayloadExpectations[i].fCount);
424         SkSpan<const uint8_t> bytes = this->bytes(i);
425         for (uint8_t b : bytes) {
426             SkDebugf("%d,", b);
427         }
428 
429         SkDebugf("\n");
430     }
431 }
432 
433 #endif // SK_DEBUG
434 
435 } // namespace skgpu::graphite
436