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