xref: /aosp_15_r20/external/skia/src/gpu/graphite/PaintParamsKey.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_PaintParamsKey_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_PaintParamsKey_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMacros.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkChecksum.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker #include <limits>
19*c8dee2aaSAndroid Build Coastguard Worker #include <cstring> // for memcmp
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker class SkArenaAlloc;
22*c8dee2aaSAndroid Build Coastguard Worker struct SkSamplingOptions;
23*c8dee2aaSAndroid Build Coastguard Worker enum class SkTileMode;
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker class Caps;
28*c8dee2aaSAndroid Build Coastguard Worker class ShaderCodeDictionary;
29*c8dee2aaSAndroid Build Coastguard Worker class ShaderNode;
30*c8dee2aaSAndroid Build Coastguard Worker class TextureProxy;
31*c8dee2aaSAndroid Build Coastguard Worker class UniquePaintParamsID;
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker /**
34*c8dee2aaSAndroid Build Coastguard Worker  * This class is a compact representation of the shader needed to implement a given
35*c8dee2aaSAndroid Build Coastguard Worker  * PaintParams. Its structure is a series of nodes where each node consists of:
36*c8dee2aaSAndroid Build Coastguard Worker  *   4 bytes: code-snippet ID
37*c8dee2aaSAndroid Build Coastguard Worker  *   N child nodes, where N is the constant number of children defined by the ShaderCodeDictionary
38*c8dee2aaSAndroid Build Coastguard Worker  *     for the node's snippet ID.
39*c8dee2aaSAndroid Build Coastguard Worker  *
40*c8dee2aaSAndroid Build Coastguard Worker  * Some snippet definitions support embedding data into the PaintParamsKey, used when something
41*c8dee2aaSAndroid Build Coastguard Worker  * external to the generated SkSL needs produce unique pipelines (e.g. immutable samplers). For
42*c8dee2aaSAndroid Build Coastguard Worker  * snippets that store data, the data is stored immediately after the ID as:
43*c8dee2aaSAndroid Build Coastguard Worker  *   4 bytes: code-snippet ID
44*c8dee2aaSAndroid Build Coastguard Worker  *   4 bytes: data length
45*c8dee2aaSAndroid Build Coastguard Worker  *   0-M: variable length data
46*c8dee2aaSAndroid Build Coastguard Worker  *   N child nodes
47*c8dee2aaSAndroid Build Coastguard Worker  *
48*c8dee2aaSAndroid Build Coastguard Worker  * All children of a child node are stored in the key before the next child is encoded in the key,
49*c8dee2aaSAndroid Build Coastguard Worker  * e.g. iterating the data in a key is a depth-first traversal of the node tree.
50*c8dee2aaSAndroid Build Coastguard Worker  *
51*c8dee2aaSAndroid Build Coastguard Worker  * The PaintParamsKey stores multiple root nodes, with each root representing an effect tree that
52*c8dee2aaSAndroid Build Coastguard Worker  * affects different parts of the shading pipeline. The key is can only hold 2 or 3 roots:
53*c8dee2aaSAndroid Build Coastguard Worker  *  1. Color root node: produces the "src" color used in final blending with the "dst" color.
54*c8dee2aaSAndroid Build Coastguard Worker  *  2. Final blend node: defines the blend function combining src and dst colors. If this is a
55*c8dee2aaSAndroid Build Coastguard Worker  *     FixedBlend snippet the final pipeline may be able to lift it to HW blending.
56*c8dee2aaSAndroid Build Coastguard Worker  *  3. Clipping: optional, produces analytic coverage from a clip shader or shape.
57*c8dee2aaSAndroid Build Coastguard Worker  *
58*c8dee2aaSAndroid Build Coastguard Worker  * Logically the root effects produce a src color and the src coverage (augmenting any other
59*c8dee2aaSAndroid Build Coastguard Worker  * coverage coming from the RenderStep). A single src shading node could be used instead of the
60*c8dee2aaSAndroid Build Coastguard Worker  * two for color and blending, but its structure would always be:
61*c8dee2aaSAndroid Build Coastguard Worker  *
62*c8dee2aaSAndroid Build Coastguard Worker  *    [ BlendCompose [ [ color-root-node ] surface-color [ final-blend ] ] ]
63*c8dee2aaSAndroid Build Coastguard Worker  *
64*c8dee2aaSAndroid Build Coastguard Worker  * where "surface-color" would be a special snippet that produces the current dst color value.
65*c8dee2aaSAndroid Build Coastguard Worker  * To keep PaintParamsKeys memory cost lower, the BlendCompose and "surface-color" nodes are implied
66*c8dee2aaSAndroid Build Coastguard Worker  * when generating the SkSL and pipeline.
67*c8dee2aaSAndroid Build Coastguard Worker  */
68*c8dee2aaSAndroid Build Coastguard Worker class PaintParamsKey {
69*c8dee2aaSAndroid Build Coastguard Worker public:
70*c8dee2aaSAndroid Build Coastguard Worker     // PaintParamsKey can only be created by using a PaintParamsKeyBuilder or by cloning the key
71*c8dee2aaSAndroid Build Coastguard Worker     // data from a Builder-owned key, but they can be passed around by value after that.
72*c8dee2aaSAndroid Build Coastguard Worker     constexpr PaintParamsKey(const PaintParamsKey&) = default;
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker     ~PaintParamsKey() = default;
75*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKey& operator=(const PaintParamsKey&) = default;
76*c8dee2aaSAndroid Build Coastguard Worker 
Invalid()77*c8dee2aaSAndroid Build Coastguard Worker     static constexpr PaintParamsKey Invalid() { return PaintParamsKey(SkSpan<const uint32_t>()); }
isValid()78*c8dee2aaSAndroid Build Coastguard Worker     bool isValid() const { return !fData.empty(); }
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     // Return a PaintParamsKey whose data is owned by the provided arena and is not attached to
81*c8dee2aaSAndroid Build Coastguard Worker     // a PaintParamsKeyBuilder. The caller must ensure that the SkArenaAlloc remains alive longer
82*c8dee2aaSAndroid Build Coastguard Worker     // than the returned key.
83*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKey clone(SkArenaAlloc*) const;
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker     // Converts the key into a forest of ShaderNode trees. If the key is valid this will return at
86*c8dee2aaSAndroid Build Coastguard Worker     // least one root node. If the key contains unknown shader snippet IDs, returns an empty span.
87*c8dee2aaSAndroid Build Coastguard Worker     // All shader nodes, and the returned span's backing data, are owned by the provided arena.
88*c8dee2aaSAndroid Build Coastguard Worker     //
89*c8dee2aaSAndroid Build Coastguard Worker     // A valid key will produce either 2 or 3 root nodes. The first root node represents how the
90*c8dee2aaSAndroid Build Coastguard Worker     // source color is computed. The second node defines the final blender between the calculated
91*c8dee2aaSAndroid Build Coastguard Worker     // source color and the current pixel's dst color. If provided, the third node calculates an
92*c8dee2aaSAndroid Build Coastguard Worker     // additional analytic coverage value to combine with the geometry's coverage.
93*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const ShaderNode*> getRootNodes(const ShaderCodeDictionary*, SkArenaAlloc*) const;
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     // Converts the key to a structured list of snippet information for debugging or labeling
96*c8dee2aaSAndroid Build Coastguard Worker     // purposes.
97*c8dee2aaSAndroid Build Coastguard Worker     SkString toString(const ShaderCodeDictionary* dict, bool includeData) const;
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
100*c8dee2aaSAndroid Build Coastguard Worker     void dump(const ShaderCodeDictionary*, UniquePaintParamsID) const;
101*c8dee2aaSAndroid Build Coastguard Worker #endif
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(const PaintParamsKey& that) const {
104*c8dee2aaSAndroid Build Coastguard Worker         return fData.size() == that.fData.size() &&
105*c8dee2aaSAndroid Build Coastguard Worker                !memcmp(fData.data(), that.fData.data(), fData.size());
106*c8dee2aaSAndroid Build Coastguard Worker     }
107*c8dee2aaSAndroid Build Coastguard Worker     bool operator!=(const PaintParamsKey& that) const { return !(*this == that); }
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operatorHash110*c8dee2aaSAndroid Build Coastguard Worker         uint32_t operator()(const PaintParamsKey& k) const {
111*c8dee2aaSAndroid Build Coastguard Worker             return SkChecksum::Hash32(k.fData.data(), k.fData.size_bytes());
112*c8dee2aaSAndroid Build Coastguard Worker         }
113*c8dee2aaSAndroid Build Coastguard Worker     };
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker private:
116*c8dee2aaSAndroid Build Coastguard Worker     friend class PaintParamsKeyBuilder;   // for the parented-data ctor
117*c8dee2aaSAndroid Build Coastguard Worker 
PaintParamsKey(SkSpan<const uint32_t> span)118*c8dee2aaSAndroid Build Coastguard Worker     constexpr PaintParamsKey(SkSpan<const uint32_t> span) : fData(span) {}
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker     // Returns null if the node or any of its children have an invalid snippet ID. Recursively
121*c8dee2aaSAndroid Build Coastguard Worker     // creates a node and all of its children, incrementing 'currentIndex' by the total number of
122*c8dee2aaSAndroid Build Coastguard Worker     // nodes created.
123*c8dee2aaSAndroid Build Coastguard Worker     const ShaderNode* createNode(const ShaderCodeDictionary*,
124*c8dee2aaSAndroid Build Coastguard Worker                                  int* currentIndex,
125*c8dee2aaSAndroid Build Coastguard Worker                                  SkArenaAlloc* arena) const;
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker     // The memory referenced in 'fData' is always owned by someone else. It either shares the span
128*c8dee2aaSAndroid Build Coastguard Worker     // from the Builder, or clone() puts the span in an arena.
129*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const uint32_t> fData;
130*c8dee2aaSAndroid Build Coastguard Worker };
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker // The PaintParamsKeyBuilder and the PaintParamsKeys snapped from it share the same
133*c8dee2aaSAndroid Build Coastguard Worker // underlying block of memory. When an PaintParamsKey is snapped from the builder it 'locks'
134*c8dee2aaSAndroid Build Coastguard Worker // the memory and 'unlocks' it in its destructor. Because of this relationship, the builder
135*c8dee2aaSAndroid Build Coastguard Worker // can only have one extant key and that key must be destroyed before the builder can be reused
136*c8dee2aaSAndroid Build Coastguard Worker // to create another one.
137*c8dee2aaSAndroid Build Coastguard Worker //
138*c8dee2aaSAndroid Build Coastguard Worker // This arrangement is intended to improve performance in the expected case, where a builder is
139*c8dee2aaSAndroid Build Coastguard Worker // being used in a tight loop to generate keys which can be recycled once they've been used to
140*c8dee2aaSAndroid Build Coastguard Worker // find the dictionary's matching uniqueID. We don't expect the cost of copying the key's memory
141*c8dee2aaSAndroid Build Coastguard Worker // into the dictionary to be prohibitive since that should be infrequent.
142*c8dee2aaSAndroid Build Coastguard Worker class PaintParamsKeyBuilder {
143*c8dee2aaSAndroid Build Coastguard Worker public:
PaintParamsKeyBuilder(const ShaderCodeDictionary * dict)144*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKeyBuilder(const ShaderCodeDictionary* dict) {
145*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fDict = dict;)
146*c8dee2aaSAndroid Build Coastguard Worker     }
147*c8dee2aaSAndroid Build Coastguard Worker 
~PaintParamsKeyBuilder()148*c8dee2aaSAndroid Build Coastguard Worker     ~PaintParamsKeyBuilder() { SkASSERT(!fLocked); }
149*c8dee2aaSAndroid Build Coastguard Worker 
beginBlock(BuiltInCodeSnippetID id)150*c8dee2aaSAndroid Build Coastguard Worker     void beginBlock(BuiltInCodeSnippetID id) { this->beginBlock(static_cast<int32_t>(id)); }
beginBlock(int32_t codeSnippetID)151*c8dee2aaSAndroid Build Coastguard Worker     void beginBlock(int32_t codeSnippetID) {
152*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fLocked);
153*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(this->pushStack(codeSnippetID);)
154*c8dee2aaSAndroid Build Coastguard Worker         fData.push_back(codeSnippetID);
155*c8dee2aaSAndroid Build Coastguard Worker     }
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Have endBlock() be handled automatically with RAII, in which case we could have it
158*c8dee2aaSAndroid Build Coastguard Worker     // validate the snippet ID being popped off the stack frame.
endBlock()159*c8dee2aaSAndroid Build Coastguard Worker     void endBlock() {
160*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(this->popStack();)
161*c8dee2aaSAndroid Build Coastguard Worker     }
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
164*c8dee2aaSAndroid Build Coastguard Worker     // Check that the builder has been reset to its initial state prior to creating a new key.
165*c8dee2aaSAndroid Build Coastguard Worker     void checkReset();
166*c8dee2aaSAndroid Build Coastguard Worker #endif
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     // Helper to add blocks that don't have children
addBlock(BuiltInCodeSnippetID id)169*c8dee2aaSAndroid Build Coastguard Worker     void addBlock(BuiltInCodeSnippetID id) {
170*c8dee2aaSAndroid Build Coastguard Worker         this->beginBlock(id);
171*c8dee2aaSAndroid Build Coastguard Worker         this->endBlock();
172*c8dee2aaSAndroid Build Coastguard Worker     }
173*c8dee2aaSAndroid Build Coastguard Worker 
addData(SkSpan<const uint32_t> data)174*c8dee2aaSAndroid Build Coastguard Worker     void addData(SkSpan<const uint32_t> data) {
175*c8dee2aaSAndroid Build Coastguard Worker         // First push the data size followed by the actual data.
176*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(this->validateData(data.size()));
177*c8dee2aaSAndroid Build Coastguard Worker         fData.push_back(data.size());
178*c8dee2aaSAndroid Build Coastguard Worker         fData.push_back_n(data.size(), data.begin());
179*c8dee2aaSAndroid Build Coastguard Worker     }
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker private:
182*c8dee2aaSAndroid Build Coastguard Worker     friend class AutoLockBuilderAsKey; // for lockAsKey() and unlock()
183*c8dee2aaSAndroid Build Coastguard Worker 
184*c8dee2aaSAndroid Build Coastguard Worker     // Returns a view of this builder as a PaintParamsKey. The Builder cannot be used until the
185*c8dee2aaSAndroid Build Coastguard Worker     // returned Key goes out of scope.
lockAsKey()186*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKey lockAsKey() {
187*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fLocked);       // lockAsKey() is not re-entrant
188*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fStack.empty()); // All beginBlocks() had a matching endBlock()
189*c8dee2aaSAndroid Build Coastguard Worker 
190*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fLocked = true;)
191*c8dee2aaSAndroid Build Coastguard Worker         return PaintParamsKey({fData.data(), fData.size()});
192*c8dee2aaSAndroid Build Coastguard Worker     }
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker     // Invalidates any PaintParamsKey returned by lockAsKey() unless it has been cloned.
unlock()195*c8dee2aaSAndroid Build Coastguard Worker     void unlock() {
196*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fLocked);
197*c8dee2aaSAndroid Build Coastguard Worker         fData.clear();
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fLocked = false;)
200*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fStack.clear();)
201*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(this->checkReset();)
202*c8dee2aaSAndroid Build Coastguard Worker     }
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     // The data array uses clear() on unlock so that it's underlying storage and repeated use of the
205*c8dee2aaSAndroid Build Coastguard Worker     // builder will hit a high-water mark and avoid lots of allocations when recording draws.
206*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<uint32_t> fData;
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
209*c8dee2aaSAndroid Build Coastguard Worker     void pushStack(int32_t codeSnippetID);
210*c8dee2aaSAndroid Build Coastguard Worker     void validateData(size_t dataSize);
211*c8dee2aaSAndroid Build Coastguard Worker     void popStack();
212*c8dee2aaSAndroid Build Coastguard Worker 
213*c8dee2aaSAndroid Build Coastguard Worker     // Information about the current block being written
214*c8dee2aaSAndroid Build Coastguard Worker     struct StackFrame {
215*c8dee2aaSAndroid Build Coastguard Worker         int fCodeSnippetID;
216*c8dee2aaSAndroid Build Coastguard Worker         int fNumExpectedChildren;
217*c8dee2aaSAndroid Build Coastguard Worker         int fNumActualChildren = 0;
218*c8dee2aaSAndroid Build Coastguard Worker         int fDataSize = -1;
219*c8dee2aaSAndroid Build Coastguard Worker     };
220*c8dee2aaSAndroid Build Coastguard Worker 
221*c8dee2aaSAndroid Build Coastguard Worker     const ShaderCodeDictionary* fDict;
222*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<StackFrame> fStack;
223*c8dee2aaSAndroid Build Coastguard Worker     bool fLocked = false;
224*c8dee2aaSAndroid Build Coastguard Worker #endif
225*c8dee2aaSAndroid Build Coastguard Worker };
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker class AutoLockBuilderAsKey {
228*c8dee2aaSAndroid Build Coastguard Worker public:
AutoLockBuilderAsKey(PaintParamsKeyBuilder * builder)229*c8dee2aaSAndroid Build Coastguard Worker     AutoLockBuilderAsKey(PaintParamsKeyBuilder* builder)
230*c8dee2aaSAndroid Build Coastguard Worker             : fBuilder(builder)
231*c8dee2aaSAndroid Build Coastguard Worker             , fKey(builder->lockAsKey()) {}
232*c8dee2aaSAndroid Build Coastguard Worker 
~AutoLockBuilderAsKey()233*c8dee2aaSAndroid Build Coastguard Worker     ~AutoLockBuilderAsKey() {
234*c8dee2aaSAndroid Build Coastguard Worker         fBuilder->unlock();
235*c8dee2aaSAndroid Build Coastguard Worker     }
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker     // Use as a PaintParamsKey
238*c8dee2aaSAndroid Build Coastguard Worker     const PaintParamsKey& operator*() const { return fKey; }
239*c8dee2aaSAndroid Build Coastguard Worker     const PaintParamsKey* operator->() const { return &fKey; }
240*c8dee2aaSAndroid Build Coastguard Worker 
241*c8dee2aaSAndroid Build Coastguard Worker private:
242*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKeyBuilder* fBuilder;
243*c8dee2aaSAndroid Build Coastguard Worker     PaintParamsKey fKey;
244*c8dee2aaSAndroid Build Coastguard Worker };
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::graphite
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_PaintParamsKey_DEFINED
249