• 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/core/SkPaintParamsKey.h"
9 
10 #include <cstring>
11 #include "src/core/SkKeyHelpers.h"
12 #include "src/core/SkShaderCodeDictionary.h"
13 
14 //--------------------------------------------------------------------------------------------------
SkPaintParamsKeyBuilder(const SkShaderCodeDictionary * dict)15 SkPaintParamsKeyBuilder::SkPaintParamsKeyBuilder(const SkShaderCodeDictionary* dict)
16         : fDict(dict) {
17 }
18 
19 // Block headers have the following structure:
20 //  1st byte: codeSnippetID
21 //  2nd byte: total blockSize in bytes
22 // This call stores the header's offset in the key on the stack to be used in 'endBlock'
beginBlock(int codeSnippetID)23 void SkPaintParamsKeyBuilder::beginBlock(int codeSnippetID) {
24     if (!this->isValid()) {
25         return;
26     }
27 
28     if (codeSnippetID < 0 || codeSnippetID > fDict->maxCodeSnippetID()) {
29         this->makeInvalid();
30         return;
31     }
32 
33     fData.reserve_back(SkPaintParamsKey::kBlockHeaderSizeInBytes);
34     fStack.push_back({ codeSnippetID, this->sizeInBytes() });
35 
36     this->addByte(SkTo<uint8_t>(codeSnippetID));
37     this->addByte(0);  // this needs to be patched up with a call to endBlock
38 }
39 
40 // Update the size byte of a block header
endBlock()41 void SkPaintParamsKeyBuilder::endBlock() {
42     if (!this->isValid()) {
43         return;
44     }
45 
46     if (fStack.empty()) {
47         SkASSERT(0);
48         this->makeInvalid();
49         return;
50     }
51 
52     int headerOffset = fStack.back().fHeaderOffset;
53 
54     SkASSERT(fData[headerOffset] == fStack.back().fCodeSnippetID);
55     SkASSERT(fData[headerOffset+SkPaintParamsKey::kBlockSizeOffsetInBytes] == 0);
56 
57     int blockSize = this->sizeInBytes() - headerOffset;
58     if (blockSize > SkPaintParamsKey::kMaxBlockSize) {
59         this->makeInvalid();
60         return;
61     }
62 
63     fData[headerOffset+SkPaintParamsKey::kBlockSizeOffsetInBytes] = blockSize;
64 
65     fStack.pop_back();
66 }
67 
addBytes(uint32_t numBytes,const uint8_t * data)68 void SkPaintParamsKeyBuilder::addBytes(uint32_t numBytes, const uint8_t* data) {
69     if (!this->isValid()) {
70         return;
71     }
72 
73     fData.push_back_n(numBytes, data);
74 }
75 
snap()76 std::unique_ptr<SkPaintParamsKey> SkPaintParamsKeyBuilder::snap() {
77     if (!fStack.empty()) {
78         this->makeInvalid();  // fall through
79     }
80 
81     auto key = std::unique_ptr<SkPaintParamsKey>(new SkPaintParamsKey(std::move(fData)));
82 
83     // Reset for reuse
84     fIsValid = true;
85     fStack.clear();
86 
87     return key;
88 }
89 
makeInvalid()90 void SkPaintParamsKeyBuilder::makeInvalid() {
91     SkASSERT(fIsValid);
92 
93     fStack.clear();
94     fData.reset();
95     this->beginBlock(SkBuiltInCodeSnippetID::kError);
96     this->endBlock();
97 
98     SkASSERT(fIsValid);
99     fIsValid = false;
100 }
101 
102 //--------------------------------------------------------------------------------------------------
SkPaintParamsKey(SkTArray<uint8_t,true> && data)103 SkPaintParamsKey::SkPaintParamsKey(SkTArray<uint8_t, true>&& data) : fData(std::move(data)) {}
104 
operator ==(const SkPaintParamsKey & that) const105 bool SkPaintParamsKey::operator==(const SkPaintParamsKey& that) const {
106     return fData == that.fData;
107 }
108 
109 #ifdef SK_DEBUG
110 typedef void (*DumpMethod)(const SkPaintParamsKey&, int headerOffset);
111 
112 namespace {
113 
dump_unknown_block(const SkPaintParamsKey & key,int headerOffset)114 void dump_unknown_block(const SkPaintParamsKey& key, int headerOffset) {
115     uint8_t id = key.byte(headerOffset);
116     uint8_t blockSize = key.byte(headerOffset+1);
117     SkASSERT(blockSize >= 2 && headerOffset+blockSize <= key.sizeInBytes());
118 
119     SkDebugf("Unknown block - id: %d size: %dB\n", id, blockSize);
120 }
121 
get_dump_method(SkBuiltInCodeSnippetID id)122 DumpMethod get_dump_method(SkBuiltInCodeSnippetID id) {
123     switch (id) {
124         case SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw:  return DepthStencilOnlyBlock::Dump;
125 
126         // SkShader code snippets
127         case SkBuiltInCodeSnippetID::kSolidColorShader:      return SolidColorShaderBlock::Dump;
128 
129         case SkBuiltInCodeSnippetID::kLinearGradientShader:  [[fallthrough]];
130         case SkBuiltInCodeSnippetID::kRadialGradientShader:  [[fallthrough]];
131         case SkBuiltInCodeSnippetID::kSweepGradientShader:   [[fallthrough]];
132         case SkBuiltInCodeSnippetID::kConicalGradientShader: return GradientShaderBlocks::Dump;
133 
134         case SkBuiltInCodeSnippetID::kImageShader:           return ImageShaderBlock::Dump;
135         case SkBuiltInCodeSnippetID::kBlendShader:           return BlendShaderBlock::Dump;
136 
137         // BlendMode code snippets
138         case SkBuiltInCodeSnippetID::kSimpleBlendMode:       return BlendModeBlock::Dump;
139 
140         default:                                             return dump_unknown_block;
141     }
142 }
143 
144 } // anonymous namespace
145 
DumpBlock(const SkPaintParamsKey & key,int headerOffset)146 int SkPaintParamsKey::DumpBlock(const SkPaintParamsKey& key, int headerOffset) {
147     auto [codeSnippetID, blockSize] = key.readCodeSnippetID(headerOffset);
148 
149     get_dump_method(codeSnippetID)(key, headerOffset);
150 
151     return blockSize;
152 }
153 
154 // This just iterates over the top-level blocks calling block-specific dump methods.
dump() const155 void SkPaintParamsKey::dump() const {
156     SkDebugf("SkPaintParamsKey %dB:\n", this->sizeInBytes());
157 
158     int curHeaderOffset = 0;
159     while (curHeaderOffset < this->sizeInBytes()) {
160         int blockSize = DumpBlock(*this, curHeaderOffset);
161         curHeaderOffset += blockSize;
162     }
163 }
164 #endif
165 
AddBlockToShaderInfo(SkShaderCodeDictionary * dict,const SkPaintParamsKey & key,int headerOffset,SkShaderInfo * result)166 int SkPaintParamsKey::AddBlockToShaderInfo(SkShaderCodeDictionary* dict,
167                                            const SkPaintParamsKey& key,
168                                            int headerOffset,
169                                            SkShaderInfo* result) {
170     auto [codeSnippetID, blockSize] = key.readCodeSnippetID(headerOffset);
171 
172     if (codeSnippetID != SkBuiltInCodeSnippetID::kSimpleBlendMode) {
173         auto entry = dict->getEntry(codeSnippetID);
174 
175         result->add(*entry);
176 
177         // The child blocks appear right after the parent block's header in the key and go
178         // right after the parent's SnippetEntry in the shader info
179         int childOffset = headerOffset + kBlockHeaderSizeInBytes;
180         for (int i = 0; i < entry->fNumChildren; ++i) {
181             SkASSERT(childOffset < headerOffset + blockSize);
182 
183             int childBlockSize = AddBlockToShaderInfo(dict, key, childOffset, result);
184             childOffset += childBlockSize;
185         }
186 
187         if (codeSnippetID != SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw) {
188             result->setWritesColor();
189         }
190     }
191 
192     return blockSize;
193 }
194 
toShaderInfo(SkShaderCodeDictionary * dict,SkShaderInfo * result) const195 void SkPaintParamsKey::toShaderInfo(SkShaderCodeDictionary* dict, SkShaderInfo* result) const {
196 
197     int curHeaderOffset = 0;
198     while (curHeaderOffset < this->sizeInBytes()) {
199         int blockSize = AddBlockToShaderInfo(dict, *this, curHeaderOffset, result);
200         curHeaderOffset += blockSize;
201     }
202 }
203