xref: /aosp_15_r20/external/skia/src/gpu/graphite/PaintParamsKey.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/base/SkArenaAlloc.h"
11 #include "src/base/SkAutoMalloc.h"
12 #include "src/base/SkBase64.h"
13 #include "src/base/SkStringView.h"
14 #include "src/gpu/graphite/Caps.h"
15 #include "src/gpu/graphite/KeyHelpers.h"
16 #include "src/gpu/graphite/Log.h"
17 #include "src/gpu/graphite/ShaderCodeDictionary.h"
18 
19 using namespace skia_private;
20 
21 namespace skgpu::graphite {
22 
23 //--------------------------------------------------------------------------------------------------
24 // PaintParamsKeyBuilder
25 
26 #ifdef SK_DEBUG
27 
checkReset()28 void PaintParamsKeyBuilder::checkReset() {
29     SkASSERT(!fLocked);
30     SkASSERT(fData.empty());
31     SkASSERT(fStack.empty());
32 }
33 
pushStack(int32_t codeSnippetID)34 void PaintParamsKeyBuilder::pushStack(int32_t codeSnippetID) {
35     SkASSERT(fDict->isValidID(codeSnippetID));
36 
37     if (!fStack.empty()) {
38         fStack.back().fNumActualChildren++;
39         SkASSERT(fStack.back().fNumActualChildren <= fStack.back().fNumExpectedChildren);
40     }
41 
42     const ShaderSnippet* snippet = fDict->getEntry(codeSnippetID);
43     fStack.push_back({codeSnippetID, snippet->fNumChildren});
44 }
45 
validateData(size_t dataSize)46 void PaintParamsKeyBuilder::validateData(size_t dataSize) {
47     SkASSERT(!fStack.empty()); // addData() called within code snippet block
48 
49     const ShaderSnippet* snippet = fDict->getEntry(fStack.back().fCodeSnippetID);
50     SkASSERT(snippet->storesData()); // addData() only called for ShaderSnippets that support it
51     SkASSERT(fStack.back().fDataSize < 0); // And only called once
52     fStack.back().fDataSize = SkTo<int>(dataSize);
53 }
54 
popStack()55 void PaintParamsKeyBuilder::popStack() {
56     SkASSERT(!fStack.empty());
57     SkASSERT(fStack.back().fNumActualChildren == fStack.back().fNumExpectedChildren);
58     const bool expectsData = fDict->getEntry(fStack.back().fCodeSnippetID)->storesData();
59     const bool hasData = fStack.back().fDataSize >= 0;
60     SkASSERT(expectsData == hasData);
61     fStack.pop_back();
62 }
63 
64 #endif // SK_DEBUG
65 
66 //--------------------------------------------------------------------------------------------------
67 // PaintParamsKey
68 
clone(SkArenaAlloc * arena) const69 PaintParamsKey PaintParamsKey::clone(SkArenaAlloc* arena) const {
70     uint32_t* newData = arena->makeArrayDefault<uint32_t>(fData.size());
71     memcpy(newData, fData.data(), fData.size_bytes());
72     return PaintParamsKey({newData, fData.size()});
73 }
74 
75 
createNode(const ShaderCodeDictionary * dict,int * currentIndex,SkArenaAlloc * arena) const76 const ShaderNode* PaintParamsKey::createNode(const ShaderCodeDictionary* dict,
77                                              int* currentIndex,
78                                              SkArenaAlloc* arena) const {
79     SkASSERT(*currentIndex < SkTo<int>(fData.size()));
80     const int32_t index = (*currentIndex)++;
81     const int32_t id = fData[index];
82 
83     const ShaderSnippet* entry = dict->getEntry(id);
84     if (!entry) {
85         SKGPU_LOG_E("Unknown snippet ID in key: %d", id);
86         return nullptr;
87     }
88 
89     SkSpan<const uint32_t> dataSpan = {};
90     if (entry->storesData()) {
91         // If a snippet stores data, then the subsequent paint key index signifies the length of
92         // its data. Determine this data length and iterate currentIndex past it.
93         const int storedDataLengthIdx = (*currentIndex)++;
94         SkASSERT(storedDataLengthIdx < SkTo<int>(fData.size()));
95         const int dataLength = fData[storedDataLengthIdx];
96         SkASSERT(storedDataLengthIdx + dataLength < SkTo<int>(fData.size()));
97 
98         // Gather the data contents (length can now be inferred by the consumers of the data) to
99         // pass into ShaderNode creation. Iterate the paint key index past the data indices.
100         dataSpan = fData.subspan(storedDataLengthIdx + 1, dataLength);
101         *currentIndex += dataLength;
102     }
103 
104     const ShaderNode** childArray = arena->makeArray<const ShaderNode*>(entry->fNumChildren);
105     for (int i = 0; i < entry->fNumChildren; ++i) {
106         const ShaderNode* child = this->createNode(dict, currentIndex, arena);
107         if (!child) {
108             return nullptr;
109         }
110         childArray[i] = child;
111     }
112 
113     return arena->make<ShaderNode>(entry,
114                                    SkSpan(childArray, entry->fNumChildren),
115                                    id,
116                                    index,
117                                    dataSpan);
118 }
119 
getRootNodes(const ShaderCodeDictionary * dict,SkArenaAlloc * arena) const120 SkSpan<const ShaderNode*> PaintParamsKey::getRootNodes(const ShaderCodeDictionary* dict,
121                                                        SkArenaAlloc* arena) const {
122     // TODO: Once the PaintParamsKey creation is organized to represent a single tree starting at
123     // the final blend, there will only be a single root node and this can be simplified.
124     // For now, we don't know how many roots there are, so collect them into a local array before
125     // copying into the arena.
126     const int keySize = SkTo<int>(fData.size());
127 
128     // Normal PaintParams creation will have up to 7 roots for the different stages.
129     STArray<7, const ShaderNode*> roots;
130     int currentIndex = 0;
131     while (currentIndex < keySize) {
132         const ShaderNode* root = this->createNode(dict, &currentIndex, arena);
133         if (!root) {
134             return {}; // a bad key
135         }
136         roots.push_back(root);
137     }
138 
139     // Copy the accumulated roots into a span stored in the arena
140     const ShaderNode** rootSpan = arena->makeArray<const ShaderNode*>(roots.size());
141     memcpy(rootSpan, roots.data(), roots.size_bytes());
142     return SkSpan(rootSpan, roots.size());
143 }
144 
key_to_string(SkString * str,const ShaderCodeDictionary * dict,SkSpan<const uint32_t> keyData,int currentIndex,bool includeData,int indent)145 static int key_to_string(SkString* str,
146                          const ShaderCodeDictionary* dict,
147                          SkSpan<const uint32_t> keyData,
148                          int currentIndex,
149                          bool includeData,
150                          int indent) {
151     SkASSERT(currentIndex < SkTo<int>(keyData.size()));
152 
153     const bool multiline = indent >= 0;
154     if (multiline) {
155         // Format for multi-line printing
156         str->appendf("%*c", 2 * indent, ' ');
157     }
158 
159     uint32_t id = keyData[currentIndex++];
160     auto entry = dict->getEntry(id);
161     if (!entry) {
162         str->append("UnknownCodeSnippetID:");
163         str->appendS32(id);
164         str->append(" ");
165         return currentIndex;
166     }
167 
168     std::string_view name = entry->fName;
169     if (skstd::ends_with(name, "Shader")) {
170         name.remove_suffix(6);
171     }
172     str->append(name);
173 
174     if (entry->storesData()) {
175         SkASSERT(currentIndex + 1 < SkTo<int>(keyData.size()));
176         const int dataLength = keyData[currentIndex++];
177         SkASSERT(currentIndex + dataLength < SkTo<int>(keyData.size()));
178 
179         // Define a compact representation for the common case of shader snippets using just one
180         // dynamic sampler. Immutable samplers require a data length > 1 to be represented while a
181         // dynamic sampler is represented with just one, so we can simply consult the data length.
182         if (dataLength == 1) {
183             str->append("(0)");
184         } else {
185             str->append("(");
186             str->appendU32(dataLength);
187             if (includeData) {
188                 // Encode data in base64 to shorten it
189                 str->append(": ");
190                 SkAutoMalloc encodedData{SkBase64::EncodedSize(dataLength)};
191                 char* dst = static_cast<char*>(encodedData.get());
192                 size_t encodedLen = SkBase64::Encode(&keyData[currentIndex], dataLength, dst);
193                 str->append(dst, encodedLen);
194             }
195             str->append(")");
196         }
197 
198         currentIndex += dataLength;
199     }
200 
201     if (entry->fNumChildren > 0) {
202         if (multiline) {
203             str->append(":\n");
204             indent++;
205         } else {
206             str->append(" [ ");
207         }
208 
209         for (int i = 0; i < entry->fNumChildren; ++i) {
210             currentIndex = key_to_string(str, dict, keyData, currentIndex, includeData, indent);
211         }
212 
213         if (!multiline) {
214             str->append("]");
215         }
216     }
217 
218     if (!multiline) {
219         str->append(" ");
220     } else if (entry->fNumChildren == 0) {
221         str->append("\n");
222     }
223     return currentIndex;
224 }
225 
toString(const ShaderCodeDictionary * dict,bool includeData) const226 SkString PaintParamsKey::toString(const ShaderCodeDictionary* dict, bool includeData) const {
227     SkString str;
228     const int keySize = SkTo<int>(fData.size());
229     for (int currentIndex = 0; currentIndex < keySize;) {
230         currentIndex = key_to_string(&str, dict, fData, currentIndex, includeData, /*indent=*/-1);
231     }
232     return str.isEmpty() ? SkString("(empty)") : str;
233 }
234 
235 #ifdef SK_DEBUG
236 
dump(const ShaderCodeDictionary * dict,UniquePaintParamsID id) const237 void PaintParamsKey::dump(const ShaderCodeDictionary* dict, UniquePaintParamsID id) const {
238     const int keySize = SkTo<int>(fData.size());
239 
240     SkDebugf("--------------------------------------\n");
241     SkDebugf("PaintParamsKey %u (keySize: %d):\n", id.asUInt(), keySize);
242 
243     int currentIndex = 0;
244     while (currentIndex < keySize) {
245         SkString nodeStr;
246         currentIndex = key_to_string(&nodeStr, dict, fData, currentIndex,
247                                      /*includeData=*/true, /*indent=*/1);
248         SkDebugf("%s", nodeStr.c_str());
249     }
250 }
251 
252 #endif // SK_DEBUG
253 
254 } // namespace skgpu::graphite
255