• 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 #ifndef skgpu_graphite_PaintParamsKey_DEFINED
9 #define skgpu_graphite_PaintParamsKey_DEFINED
10 
11 #include "include/core/SkSpan.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/base/SkMacros.h"
14 #include "include/private/base/SkTArray.h"
15 #include "src/core/SkChecksum.h"
16 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
17 
18 #include <limits>
19 #include <cstring> // for memcmp
20 
21 class SkArenaAlloc;
22 
23 namespace skgpu::graphite {
24 
25 class ShaderCodeDictionary;
26 class ShaderNode;
27 class UniquePaintParamsID;
28 
29 // This class is a compact representation of the shader needed to implement a given
30 // PaintParams. Its structure is a series of nodes where each node consists of:
31 //   4 bytes: code-snippet ID
32 //   N child nodes, where N is the constant number of children defined by the ShaderCodeDictionary
33 //     for the node's snippet ID.
34 //
35 // All children of a child node are stored in the key before the next child is encoded in the key,
36 // e.g. iterating the data in a key is a depth-first traversal of the node tree.
37 class PaintParamsKey {
38 public:
39     // PaintParamsKey can only be created by using a PaintParamsKeyBuilder or by cloning the key
40     // data from a Builder-owned key, but they can be passed around by value after that.
41     constexpr PaintParamsKey(const PaintParamsKey&) = default;
42 
43     ~PaintParamsKey() = default;
44     PaintParamsKey& operator=(const PaintParamsKey&) = default;
45 
Invalid()46     static constexpr PaintParamsKey Invalid() { return PaintParamsKey(SkSpan<const int32_t>()); }
isValid()47     bool isValid() const { return !fData.empty(); }
48 
49     // Return a PaintParamsKey whose data is owned by the provided arena and is not attached to
50     // a PaintParamsKeyBuilder. The caller must ensure that the SkArenaAlloc remains alive longer
51     // than the returned key.
52     PaintParamsKey clone(SkArenaAlloc*) const;
53 
54     // Converts the key into a forest of ShaderNode trees. If the key is valid this will return at
55     // least one root node. If the key contains unknown shader snippet IDs, returns an empty span.
56     // All shader nodes, and the returned span's backing data, are owned by the provided arena.
57     // TODO: Strengthen PaintParams key generation so we can assume there's only ever one root node
58     // representing the final blend (either a shader blend (with 2 children: main effect & dst) or
59     // a fixed function blend (with 1 child being the main effect)).
60     SkSpan<const ShaderNode*> getRootNodes(const ShaderCodeDictionary*, SkArenaAlloc*) const;
61 
62     // Converts the key to a structured list of snippet names for debugging or labeling purposes.
63     SkString toString(const ShaderCodeDictionary* dict) const;
64 
65 #ifdef SK_DEBUG
66     void dump(const ShaderCodeDictionary*, UniquePaintParamsID) const;
67 #endif
68 
69     bool operator==(const PaintParamsKey& that) const {
70         return fData.size() == that.fData.size() &&
71                !memcmp(fData.data(), that.fData.data(), fData.size());
72     }
73     bool operator!=(const PaintParamsKey& that) const { return !(*this == that); }
74 
75     struct Hash {
operatorHash76         uint32_t operator()(const PaintParamsKey& k) const {
77             return SkChecksum::Hash32(k.fData.data(), k.fData.size_bytes());
78         }
79     };
80 
81 private:
82     friend class PaintParamsKeyBuilder;   // for the parented-data ctor
83 
PaintParamsKey(SkSpan<const int32_t> span)84     constexpr PaintParamsKey(SkSpan<const int32_t> span) : fData(span) {}
85 
86     // Returns null if the node or any of its children have an invalid snippet ID. Recursively
87     // creates a node and all of its children, incrementing 'currentIndex' by the total number of
88     // nodes created.
89     const ShaderNode* createNode(const ShaderCodeDictionary*,
90                                  int* currentIndex,
91                                  SkArenaAlloc* arena) const;
92 
93     // The memory referenced in 'fData' is always owned by someone else. It either shares the span
94     // of from the Builder, or clone() puts the span in an arena.
95     SkSpan<const int32_t> fData;
96 };
97 
98 // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same
99 // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks'
100 // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder
101 // can only have one extant key and that key must be destroyed before the builder can be reused
102 // to create another one.
103 //
104 // This arrangement is intended to improve performance in the expected case, where a builder is
105 // being used in a tight loop to generate keys which can be recycled once they've been used to
106 // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory
107 // into the dictionary to be prohibitive since that should be infrequent.
108 class PaintParamsKeyBuilder {
109 public:
PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)110     PaintParamsKeyBuilder(const ShaderCodeDictionary* dict) {
111         SkDEBUGCODE(fDict = dict;)
112     }
113 
~PaintParamsKeyBuilder()114     ~PaintParamsKeyBuilder() { SkASSERT(!fLocked); }
115 
beginBlock(BuiltInCodeSnippetID id)116     void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); }
beginBlock(int32_t codeSnippetID)117     void beginBlock(int32_t codeSnippetID) {
118         SkASSERT(!fLocked);
119         SkDEBUGCODE(this->pushStack(codeSnippetID);)
120         fData.push_back(codeSnippetID);
121     }
122 
123     // TODO: Have endBlock() be handled automatically with RAII, in which case we could have it
124     // validate the snippet ID being popped off the stack frame.
endBlock()125     void endBlock() {
126         SkDEBUGCODE(this->popStack();)
127     }
128 
129 #ifdef SK_DEBUG
130     // Check that the builder has been reset to its initial state prior to creating a new key.
131     void checkReset();
132 #endif
133 
134     // Helper to add blocks that don't have children
addBlock(BuiltInCodeSnippetID id)135     void addBlock(BuiltInCodeSnippetID id) {
136         this->beginBlock(id);
137         this->endBlock();
138     }
139 
140 private:
141     friend class AutoLockBuilderAsKey; // for lockAsKey() and unlock()
142 
143     // Returns a view of this builder as a PaintParamsKey. The Builder cannot be used until the
144     // returned Key goes out of scope.
lockAsKey()145     PaintParamsKey lockAsKey() {
146         SkASSERT(!fLocked);       // lockAsKey() is not re-entrant
147         SkASSERT(fStack.empty()); // All beginBlocks() had a matching endBlock()
148 
149         SkDEBUGCODE(fLocked = true;)
150         return PaintParamsKey({fData.data(), fData.size()});
151     }
152 
153     // Invalidates any PaintParamsKey returned by lockAsKey() unless it has been cloned.
unlock()154     void unlock() {
155         SkASSERT(fLocked);
156         fData.clear();
157 
158         SkDEBUGCODE(fLocked = false;)
159         SkDEBUGCODE(fStack.clear();)
160         SkDEBUGCODE(this->checkReset();)
161     }
162 
163     // The data array uses clear() on unlock so that it's underlying storage and repeated use of the
164     // builder will hit a high-water mark and avoid lots of allocations when recording draws.
165     skia_private::TArray<int32_t> fData;
166 
167 #ifdef SK_DEBUG
168     void pushStack(int32_t codeSnippetID);
169     void popStack();
170 
171     // Information about the current block being written
172     struct StackFrame {
173         int fCodeSnippetID;
174         int fNumExpectedChildren;
175         int fNumActualChildren = 0;
176     };
177 
178     const ShaderCodeDictionary* fDict;
179     skia_private::TArray<StackFrame> fStack;
180     bool fLocked = false;
181 #endif
182 };
183 
184 class AutoLockBuilderAsKey {
185 public:
AutoLockBuilderAsKey(PaintParamsKeyBuilder * builder)186     AutoLockBuilderAsKey(PaintParamsKeyBuilder* builder)
187             : fBuilder(builder)
188             , fKey(builder->lockAsKey()) {}
189 
~AutoLockBuilderAsKey()190     ~AutoLockBuilderAsKey() {
191         fBuilder->unlock();
192     }
193 
194     // Use as a PaintParamsKey
195     const PaintParamsKey& operator*() const { return fKey; }
196     const PaintParamsKey* operator->() const { return &fKey; }
197 
198 private:
199     PaintParamsKeyBuilder* fBuilder;
200     PaintParamsKey fKey;
201 };
202 
203 }  // namespace skgpu::graphite
204 
205 #endif // skgpu_graphite_PaintParamsKey_DEFINED
206