Flutter Engine
The Flutter Engine
ShaderCodeDictionary.h
Go to the documentation of this file.
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_ShaderCodeDictionary_DEFINED
9#define skgpu_graphite_ShaderCodeDictionary_DEFINED
10
12#include "include/core/SkSpan.h"
20#include "src/base/SkSpinlock.h"
22#include "src/core/SkTHash.h"
23#include "src/gpu/Blend.h"
28
29#include <array>
30#include <cstddef>
31#include <cstdint>
32#include <memory>
33#include <string>
34#include <string_view>
35
36class SkRuntimeEffect;
37
38namespace skgpu {
39class Swizzle;
40}
41
42namespace skgpu::graphite {
43
44class Caps;
45class RenderStep;
46class RuntimeEffectDictionary;
47
48// TODO: How to represent the type (e.g., 2D) of texture being sampled?
50public:
51 constexpr TextureAndSampler(const char* name) : fName(name) {}
52
53 const char* name() const { return fName; }
54
55private:
56 const char* fName;
57};
58
59enum class SnippetRequirementFlags : uint32_t {
60 kNone = 0x0,
61 kLocalCoords = 0x1,
62 kPriorStageOutput = 0x2, // AKA the "input" color, or the "src" argument for a blender
63 kBlenderDstColor = 0x4, // The "dst" argument for a blender
64 kSurfaceColor = 0x8,
65 kGradientBuffer = 0x10,
66 kStoresData = 0x20, // Indicates that the node stores numerical data
67};
69
70class ShaderInfo;
71class ShaderNode;
72
73// ShaderSnippets define the "ABI" of a SkSL module function and its required uniform data, as
74// well as functions for generating the invoking SkSL. Snippets are composed into an effect tree
75// using ShaderNodes.
77 using GeneratePreambleForSnippetFn = std::string (*)(const ShaderInfo& shaderInfo,
78 const ShaderNode*);
79 struct Args {
80 std::string fPriorStageOutput;
81 std::string fBlenderDstColor;
82 std::string fFragCoord;
83 };
84 using GenerateExpressionForSnippetFn = std::string (*)(const ShaderInfo& shaderInfo,
85 const ShaderNode*,
86 const Args& args);
87
88 ShaderSnippet() = default;
89
90 ShaderSnippet(const char* name,
91 SkSpan<const Uniform> uniforms,
92 SkEnumBitMask<SnippetRequirementFlags> snippetRequirementFlags,
93 SkSpan<const TextureAndSampler> texturesAndSamplers,
94 const char* functionName,
95 GenerateExpressionForSnippetFn expressionGenerator,
96 GeneratePreambleForSnippetFn preambleGenerator,
97 int numChildren)
98 : fName(name)
99 , fUniforms(uniforms)
100 , fSnippetRequirementFlags(snippetRequirementFlags)
101 , fTexturesAndSamplers(texturesAndSamplers)
102 , fStaticFunctionName(functionName)
103 , fExpressionGenerator(expressionGenerator)
104 , fPreambleGenerator(preambleGenerator)
105 , fNumChildren(numChildren) {}
106
107 bool needsLocalCoords() const {
109 }
112 }
113 bool needsBlenderDstColor() const {
115 }
116 bool storesData() const {
118 }
119
120 const char* fName = nullptr;
124 const char* fStaticFunctionName = nullptr;
128};
129
130// ShaderNodes organize snippets into an effect tree, and provide random access to the dynamically
131// bound child snippets. Each node has a fixed number of children defined by its code ID
132// (either a BuiltInCodeSnippetID or a runtime effect's assigned ID). All children are non-null.
133// A ShaderNode tree represents a decompressed PaintParamsKey.
135public:
136 // ShaderNodes should be created in conjunction with an SkArenaAlloc that owns all nodes.
137 ShaderNode(const ShaderSnippet* snippet,
139 int codeID,
140 int keyIndex,
142 : fEntry(snippet)
143 , fChildren(children)
144 , fCodeID(codeID)
145 , fKeyIndex(keyIndex)
146 , fRequiredFlags(snippet->fSnippetRequirementFlags)
147 , fData(data) {
148 SkASSERT(children.size() == (size_t) fEntry->fNumChildren);
149 // TODO: RuntimeEffects can actually mask off requirements if they invoke a child with
150 // explicit arguments.
151 for (const ShaderNode* child : children) {
152 fRequiredFlags |= child->requiredFlags();
153 }
154 // Data should only be provided if the snippet has the kStoresData flag.
155 SkASSERT(fData.empty() || snippet->storesData());
156 }
157
158 int32_t codeSnippetId() const { return fCodeID; }
159 int32_t keyIndex() const { return fKeyIndex; }
160 const ShaderSnippet* entry() const { return fEntry; }
161
162 SkEnumBitMask<SnippetRequirementFlags> requiredFlags() const { return fRequiredFlags; }
163
164 int numChildren() const { return fEntry->fNumChildren; }
165 SkSpan<const ShaderNode*> children() const { return fChildren; }
166 const ShaderNode* child(int childIndex) const { return fChildren[childIndex]; }
167
168 SkSpan<const uint32_t> data() const { return fData; }
169
170private:
171 const ShaderSnippet* fEntry; // Owned by the ShaderCodeDictionary
172 SkSpan<const ShaderNode*> fChildren; // Owned by the ShaderInfo's arena
173
174 int32_t fCodeID;
175 int32_t fKeyIndex; // index back to PaintParamsKey, unique across nodes within a ShaderInfo
176
178 SkSpan<const uint32_t> fData; // Subspan of PaintParamsKey's fData; shares same owner
179};
180
181// ShaderInfo holds all root ShaderNodes defined for a PaintParams as well as the extracted fixed
182// function blending parameters and other aggregate requirements for the effect trees that have
183// been linked into a single fragment program (sans any RenderStep fragment work and fixed SkSL
184// logic required for all rendering in Graphite).
185class ShaderInfo {
186public:
188 const ShaderCodeDictionary* dict,
189 const RuntimeEffectDictionary* rteDict,
190 const char* ssboIndex);
191
192 bool needsLocalCoords() const {
193 return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords);
194 }
195 bool needsSurfaceColor() const {
196 return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kSurfaceColor);
197 }
199 return fRuntimeEffectDictionary;
200 }
201 const char* ssboIndex() const { return fSsboIndex; }
202
203 const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; }
204
205 const skia_private::TArray<uint32_t>& data() const { return fData; }
206
207 std::string toSkSL(const Caps* caps,
208 const RenderStep* step,
209 bool useStorageBuffers,
210 int* numTexturesAndSamplersUsed,
211 int* numPaintUniforms,
212 int* renderStepUniformTotalBytes,
213 int* paintUniformsTotalBytes,
214 bool* hasGradientBuffer,
215 Swizzle writeSwizzle);
216
217private:
218 // Recursive method which traverses ShaderNodes in a depth-first manner to aggregate all
219 // ShaderNode data (not owned by ShaderNode) into ShaderInfo's owned fData.
220 // TODO(b/347072931): Ideally, this method could go away and each snippet's data could remain
221 // tied to its ID instead of accumulating it all here.
222 void aggregateSnippetData(const ShaderNode*);
223
224 // All shader nodes and arrays of children pointers are held in this arena
225 SkArenaAlloc fShaderNodeAlloc{256};
226
227 const RuntimeEffectDictionary* fRuntimeEffectDictionary;
228 const char* fSsboIndex;
229
230 // De-compressed shader tree from a PaintParamsKey with accumulated blend info and requirements.
231 // The blendInfo doesn't contribute to the program's SkSL but contains the fixed-function state
232 // required to function correctly, which the program's caller is responsible for configuring.
233 // TODO: There should really only be one root node representing the final blend, which has a
234 // child defining how the src color is calculated.
235 SkSpan<const ShaderNode*> fRootNodes;
237 skgpu::BlendInfo fBlendInfo;
238 SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags;
240};
241
242// ShaderCodeDictionary is a thread-safe dictionary of ShaderSnippets to code IDs for use with
243// creating PaintParamKeys, as well as assigning unique IDs to each encountered PaintParamKey.
244// It defines ShaderSnippets for every BuiltInCodeSnippetID and maintains records for IDs per
245// SkRuntimeEffect, including de-duplicating equivalent SkRuntimeEffect objects.
247public:
249
251
253
255 return this->lookup(id).toString(this, /*includeData=*/false);
256 }
257
260 BuiltInCodeSnippetID id) const {
261 return fBuiltInCodeSnippets[(int) id].fSnippetRequirementFlags;
262 }
263
264#if defined(SK_DEBUG)
265 bool isValidID(int snippetID) const SK_EXCLUDES(fSpinLock);
266
267 void dump(UniquePaintParamsID) const;
268#endif
269
270 // This method can return nullptr
271 const ShaderSnippet* getEntry(int codeSnippetID) const SK_EXCLUDES(fSpinLock);
272 const ShaderSnippet* getEntry(BuiltInCodeSnippetID codeSnippetID) const SK_EXCLUDES(fSpinLock) {
273 return this->getEntry(SkTo<int>(codeSnippetID));
274 }
275
277
278#if defined(GRAPHITE_TEST_UTILS)
279 int addRuntimeEffectSnippet(const char* name) SK_EXCLUDES(fSpinLock);
280#endif
281
282private:
283 const char* addTextToArena(std::string_view text);
284
285 SkSpan<const Uniform> convertUniforms(const SkRuntimeEffect* effect);
286
287 std::array<ShaderSnippet, kBuiltInCodeSnippetIDCount> fBuiltInCodeSnippets;
288
289 using KnownRuntimeEffectArray = std::array<ShaderSnippet, SkKnownRuntimeEffects::kStableKeyCnt>;
290 KnownRuntimeEffectArray fKnownRuntimeEffectCodeSnippets SK_GUARDED_BY(fSpinLock);
291
292 // The value returned from 'getEntry' must be stable so, hold the user-defined code snippet
293 // entries as pointers.
294 using RuntimeEffectArray = skia_private::TArray<std::unique_ptr<ShaderSnippet>>;
295 RuntimeEffectArray fUserDefinedCodeSnippets SK_GUARDED_BY(fSpinLock);
296
297 // TODO: can we do something better given this should have write-seldom/read-often behavior?
298 mutable SkSpinlock fSpinLock;
299
300 using PaintIDMap = skia_private::THashMap<PaintParamsKey,
303
304 PaintIDMap fPaintKeyToID SK_GUARDED_BY(fSpinLock);
305 skia_private::TArray<PaintParamsKey> fIDToPaintKey SK_GUARDED_BY(fSpinLock);
306
308 struct RuntimeEffectKey {
309 uint32_t fHash;
310 uint32_t fUniformSize;
311
312 bool operator==(RuntimeEffectKey rhs) const {
313 return fHash == rhs.fHash && fUniformSize == rhs.fUniformSize;
314 }
315 };
317
318 // A map from RuntimeEffectKeys (hash plus uniforms) to code-snippet IDs. RuntimeEffectKeys
319 // don't track the lifetime of a runtime effect at all; they live forever, and a newly-
320 // instantiated runtime effect with the same program as a previously-discarded effect will reuse
321 // an existing ID. Entries in the runtime-effect map are never removed; they only disappear when
322 // the context is discarded, which takes the ShaderCodeDictionary along with it. However, they
323 // are extremely small (< 20 bytes) so the memory footprint should be unnoticeable.
325 RuntimeEffectMap fRuntimeEffectMap SK_GUARDED_BY(fSpinLock);
326
327 // This arena holds:
328 // - the backing data for PaintParamsKeys in `fPaintKeyToID` and `fIDToPaintKey`
329 // - Uniform data created by `findOrCreateRuntimeEffectSnippet`
330 // and in all cases is guarded by `fSpinLock`
331 SkArenaAlloc fArena{256};
332};
333
334} // namespace skgpu::graphite
335
336#endif // skgpu_graphite_ShaderCodeDictionary_DEFINED
static int step(int x, SkScalar min, SkScalar max)
Definition: BlurTest.cpp:215
SkBlendMode fBlendMode
Definition: Layer.cpp:55
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kClear
r = 0
#define SK_BEGIN_REQUIRE_DENSE
Definition: SkMacros.h:37
#define SK_END_REQUIRE_DENSE
Definition: SkMacros.h:38
#define SK_EXCLUDES(...)
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Definition: SkYUVMath.cpp:629
constexpr bool empty() const
Definition: SkSpan_impl.h:96
SkString toString(const ShaderCodeDictionary *dict, bool includeData) const
PaintParamsKey lookup(UniquePaintParamsID) const SK_EXCLUDES(fSpinLock)
SkEnumBitMask< SnippetRequirementFlags > getSnippetRequirementFlags(BuiltInCodeSnippetID id) const
UniquePaintParamsID findOrCreate(PaintParamsKeyBuilder *) SK_EXCLUDES(fSpinLock)
const ShaderSnippet * getEntry(BuiltInCodeSnippetID codeSnippetID) const SK_EXCLUDES(fSpinLock)
int findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect *effect)
SkSpan< const Uniform > getUniforms(BuiltInCodeSnippetID) const
const ShaderSnippet * getEntry(int codeSnippetID) const SK_EXCLUDES(fSpinLock)
SkString idToString(UniquePaintParamsID id) const
ShaderInfo(UniquePaintParamsID id, const ShaderCodeDictionary *dict, const RuntimeEffectDictionary *rteDict, const char *ssboIndex)
const skgpu::BlendInfo & blendInfo() const
const skia_private::TArray< uint32_t > & data() const
const RuntimeEffectDictionary * runtimeEffectDictionary() const
ShaderNode(const ShaderSnippet *snippet, SkSpan< const ShaderNode * > children, int codeID, int keyIndex, SkSpan< const uint32_t > data)
const ShaderNode * child(int childIndex) const
const ShaderSnippet * entry() const
SkSpan< const ShaderNode * > children() const
SkEnumBitMask< SnippetRequirementFlags > requiredFlags() const
SkSpan< const uint32_t > data() const
constexpr TextureAndSampler(const char *name)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
std::u16string text
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
bool operator==(C p1, const scoped_nsprotocol< C > &p2)
SK_MAKE_BITMASK_OPS(DawnErrorType)
Definition: GpuTools.h:21
std::string(*)(const ShaderInfo &shaderInfo, const ShaderNode *) GeneratePreambleForSnippetFn
SkEnumBitMask< SnippetRequirementFlags > fSnippetRequirementFlags
GeneratePreambleForSnippetFn fPreambleGenerator
SkSpan< const Uniform > fUniforms
std::string(*)(const ShaderInfo &shaderInfo, const ShaderNode *, const Args &args) GenerateExpressionForSnippetFn
GenerateExpressionForSnippetFn fExpressionGenerator
SkSpan< const TextureAndSampler > fTexturesAndSamplers
ShaderSnippet(const char *name, SkSpan< const Uniform > uniforms, SkEnumBitMask< SnippetRequirementFlags > snippetRequirementFlags, SkSpan< const TextureAndSampler > texturesAndSamplers, const char *functionName, GenerateExpressionForSnippetFn expressionGenerator, GeneratePreambleForSnippetFn preambleGenerator, int numChildren)