• 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_ShaderCodeDictionary_DEFINED
9 #define skgpu_graphite_ShaderCodeDictionary_DEFINED
10 
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkSpan.h"
13 #include "include/core/SkTypes.h"
14 #include "include/private/base/SkMacros.h"
15 #include "include/private/base/SkTArray.h"
16 #include "include/private/base/SkThreadAnnotations.h"
17 #include "include/private/base/SkTo.h"
18 #include "src/base/SkArenaAlloc.h"
19 #include "src/base/SkEnumBitMask.h"
20 #include "src/base/SkSpinlock.h"
21 #include "src/core/SkKnownRuntimeEffects.h"
22 #include "src/core/SkTHash.h"
23 #include "src/gpu/Blend.h"
24 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
25 #include "src/gpu/graphite/PaintParamsKey.h"
26 #include "src/gpu/graphite/Uniform.h"
27 #include "src/gpu/graphite/UniquePaintParamsID.h"
28 
29 #include <array>
30 #include <cstddef>
31 #include <cstdint>
32 #include <memory>
33 #include <string>
34 #include <string_view>
35 
36 class SkRuntimeEffect;
37 
38 namespace skgpu {
39 class Swizzle;
40 }
41 
42 namespace skgpu::graphite {
43 
44 class Caps;
45 class RenderStep;
46 class RuntimeEffectDictionary;
47 
48 // TODO: How to represent the type (e.g., 2D) of texture being sampled?
49 class TextureAndSampler {
50 public:
TextureAndSampler(const char * name)51     constexpr TextureAndSampler(const char* name) : fName(name) {}
52 
name()53     const char* name() const { return fName; }
54 
55 private:
56     const char* fName;
57 };
58 
59 enum 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 };
67 SK_MAKE_BITMASK_OPS(SnippetRequirementFlags)
68 
69 class ShaderInfo;
70 class ShaderNode;
71 
72 // ShaderSnippets define the "ABI" of a SkSL module function and its required uniform data, as
73 // well as functions for generating the invoking SkSL. Snippets are composed into an effect tree
74 // using ShaderNodes.
75 struct ShaderSnippet {
76     using GeneratePreambleForSnippetFn = std::string (*)(const ShaderInfo& shaderInfo,
77                                                          const ShaderNode*);
78     struct Args {
79         std::string fPriorStageOutput;
80         std::string fBlenderDstColor;
81         std::string fFragCoord;
82     };
83     using GenerateExpressionForSnippetFn = std::string (*)(const ShaderInfo& shaderInfo,
84                                                            const ShaderNode*,
85                                                            const Args& args);
86 
87     ShaderSnippet() = default;
88 
ShaderSnippetShaderSnippet89     ShaderSnippet(const char* name,
90                   SkSpan<const Uniform> uniforms,
91                   SkEnumBitMask<SnippetRequirementFlags> snippetRequirementFlags,
92                   SkSpan<const TextureAndSampler> texturesAndSamplers,
93                   const char* functionName,
94                   GenerateExpressionForSnippetFn expressionGenerator,
95                   GeneratePreambleForSnippetFn preambleGenerator,
96                   int numChildren)
97         : fName(name)
98         , fUniforms(uniforms)
99         , fSnippetRequirementFlags(snippetRequirementFlags)
100         , fTexturesAndSamplers(texturesAndSamplers)
101         , fStaticFunctionName(functionName)
102         , fExpressionGenerator(expressionGenerator)
103         , fPreambleGenerator(preambleGenerator)
104         , fNumChildren(numChildren) {}
105 
needsLocalCoordsShaderSnippet106     bool needsLocalCoords() const {
107         return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords);
108     }
needsPriorStageOutputShaderSnippet109     bool needsPriorStageOutput() const {
110         return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kPriorStageOutput);
111     }
needsBlenderDstColorShaderSnippet112     bool needsBlenderDstColor() const {
113         return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kBlenderDstColor);
114     }
115 
116     const char* fName = nullptr;
117     SkSpan<const Uniform> fUniforms;
118     SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags{SnippetRequirementFlags::kNone};
119     SkSpan<const TextureAndSampler> fTexturesAndSamplers;
120     const char* fStaticFunctionName = nullptr;
121     GenerateExpressionForSnippetFn fExpressionGenerator = nullptr;
122     GeneratePreambleForSnippetFn fPreambleGenerator = nullptr;
123     int fNumChildren = 0;
124 };
125 
126 // ShaderNodes organize snippets into an effect tree, and provide random access to the dynamically
127 // bound child snippets. Each node has a fixed number of children defined by its code ID
128 // (either a BuiltInCodeSnippetID or a runtime effect's assigned ID). All children are non-null.
129 // A ShaderNode tree represents a decompressed PaintParamsKey.
130 class ShaderNode {
131 public:
132     // ShaderNodes should be created in conjunction with an SkArenaAlloc that owns all nodes.
ShaderNode(const ShaderSnippet * snippet,SkSpan<const ShaderNode * > children,int codeID,int keyIndex)133     ShaderNode(const ShaderSnippet* snippet,
134                SkSpan<const ShaderNode*> children,
135                int codeID,
136                int keyIndex)
137             : fEntry(snippet)
138             , fChildren(children)
139             , fCodeID(codeID)
140             , fKeyIndex(keyIndex)
141             , fRequiredFlags(snippet->fSnippetRequirementFlags) {
142         SkASSERT(children.size() == (size_t) fEntry->fNumChildren);
143         // TODO: RuntimeEffects can actually mask off requirements if they invoke a child with
144         // explicit arguments.
145         for (const ShaderNode* child : children) {
146             fRequiredFlags |= child->requiredFlags();
147         }
148     }
149 
codeSnippetId()150     int32_t codeSnippetId() const { return fCodeID; }
keyIndex()151     int32_t keyIndex() const { return fKeyIndex; }
entry()152     const ShaderSnippet* entry() const { return fEntry; }
153 
requiredFlags()154     SkEnumBitMask<SnippetRequirementFlags> requiredFlags() const { return fRequiredFlags; }
155 
numChildren()156     int numChildren() const { return fEntry->fNumChildren; }
children()157     SkSpan<const ShaderNode*> children() const { return fChildren; }
child(int childIndex)158     const ShaderNode* child(int childIndex) const { return fChildren[childIndex]; }
159 
160 private:
161     const ShaderSnippet* fEntry; // Owned by the ShaderCodeDictionary
162     SkSpan<const ShaderNode*> fChildren; // Owned by the ShaderInfo's arena
163 
164     int32_t fCodeID;
165     int32_t fKeyIndex; // index back to PaintParamsKey, unique across nodes within a ShaderInfo
166 
167     SkEnumBitMask<SnippetRequirementFlags> fRequiredFlags;
168 };
169 
170 // ShaderInfo holds all root ShaderNodes defined for a PaintParams as well as the extracted fixed
171 // function blending parameters and other aggregate requirements for the effect trees that have
172 // been linked into a single fragment program (sans any RenderStep fragment work and fixed SkSL
173 // logic required for all rendering in Graphite).
174 class ShaderInfo {
175 public:
176     ShaderInfo(UniquePaintParamsID id,
177                const ShaderCodeDictionary* dict,
178                const RuntimeEffectDictionary* rteDict,
179                const char* ssboIndex);
180 
needsLocalCoords()181     bool needsLocalCoords() const {
182         return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords);
183     }
needsSurfaceColor()184     bool needsSurfaceColor() const {
185         return SkToBool(fSnippetRequirementFlags & SnippetRequirementFlags::kSurfaceColor);
186     }
runtimeEffectDictionary()187     const RuntimeEffectDictionary* runtimeEffectDictionary() const {
188         return fRuntimeEffectDictionary;
189     }
ssboIndex()190     const char* ssboIndex() const { return fSsboIndex; }
191 
blendInfo()192     const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; }
193 
194     std::string toSkSL(const Caps* caps,
195                        const RenderStep* step,
196                        bool useStorageBuffers,
197                        int* numTexturesAndSamplersUsed,
198                        int* numPaintUniforms,
199                        int* renderStepUniformTotalBytes,
200                        int* paintUniformsTotalBytes,
201                        bool* hasGradientBuffer,
202                        Swizzle writeSwizzle);
203 
204 private:
205     // All shader nodes and arrays of children pointers are held in this arena
206     SkArenaAlloc fShaderNodeAlloc{256};
207 
208     const RuntimeEffectDictionary* fRuntimeEffectDictionary;
209     const char* fSsboIndex;
210 
211     // De-compressed shader tree from a PaintParamsKey with accumulated blend info and requirements.
212     // The blendInfo doesn't contribute to the program's SkSL but contains the fixed-function state
213     // required to function correctly, which the program's caller is responsible for configuring.
214     // TODO: There should really only be one root node representing the final blend, which has a
215     // child defining how the src color is calculated.
216     SkSpan<const ShaderNode*> fRootNodes;
217     SkBlendMode fBlendMode = SkBlendMode::kClear;
218     skgpu::BlendInfo fBlendInfo;
219     SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags;
220 };
221 
222 // ShaderCodeDictionary is a thread-safe dictionary of ShaderSnippets to code IDs for use with
223 // creating PaintParamKeys, as well as assigning unique IDs to each encountered PaintParamKey.
224 // It defines ShaderSnippets for every BuiltInCodeSnippetID and maintains records for IDs per
225 // SkRuntimeEffect, including de-duplicating equivalent SkRuntimeEffect objects.
226 class ShaderCodeDictionary {
227 public:
228     ShaderCodeDictionary();
229 
230     UniquePaintParamsID findOrCreate(PaintParamsKeyBuilder*) SK_EXCLUDES(fSpinLock);
231 
232     PaintParamsKey lookup(UniquePaintParamsID) const SK_EXCLUDES(fSpinLock);
233 
idToString(UniquePaintParamsID id)234     SkString idToString(UniquePaintParamsID id) const {
235         return this->lookup(id).toString(this);
236     }
237 
238     SkSpan<const Uniform> getUniforms(BuiltInCodeSnippetID) const;
getSnippetRequirementFlags(BuiltInCodeSnippetID id)239     SkEnumBitMask<SnippetRequirementFlags> getSnippetRequirementFlags(
240             BuiltInCodeSnippetID id) const {
241         return fBuiltInCodeSnippets[(int) id].fSnippetRequirementFlags;
242     }
243 
244 #if defined(SK_DEBUG)
245     bool isValidID(int snippetID) const SK_EXCLUDES(fSpinLock);
246 
247     void dump(UniquePaintParamsID) const;
248 #endif
249 
250     // This method can return nullptr
251     const ShaderSnippet* getEntry(int codeSnippetID) const SK_EXCLUDES(fSpinLock);
getEntry(BuiltInCodeSnippetID codeSnippetID)252     const ShaderSnippet* getEntry(BuiltInCodeSnippetID codeSnippetID) const SK_EXCLUDES(fSpinLock) {
253         return this->getEntry(SkTo<int>(codeSnippetID));
254     }
255 
256     int findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect);
257 
258 #if defined(GRAPHITE_TEST_UTILS)
259     int addRuntimeEffectSnippet(const char* name) SK_EXCLUDES(fSpinLock);
260 #endif
261 
262 private:
263     const char* addTextToArena(std::string_view text);
264 
265     SkSpan<const Uniform> convertUniforms(const SkRuntimeEffect* effect);
266 
267     std::array<ShaderSnippet, kBuiltInCodeSnippetIDCount> fBuiltInCodeSnippets;
268 
269     using KnownRuntimeEffectArray = std::array<ShaderSnippet, SkKnownRuntimeEffects::kStableKeyCnt>;
270     KnownRuntimeEffectArray fKnownRuntimeEffectCodeSnippets SK_GUARDED_BY(fSpinLock);
271 
272     // The value returned from 'getEntry' must be stable so, hold the user-defined code snippet
273     // entries as pointers.
274     using RuntimeEffectArray = skia_private::TArray<std::unique_ptr<ShaderSnippet>>;
275     RuntimeEffectArray fUserDefinedCodeSnippets SK_GUARDED_BY(fSpinLock);
276 
277     // TODO: can we do something better given this should have write-seldom/read-often behavior?
278     mutable SkSpinlock fSpinLock;
279 
280     using PaintIDMap = skia_private::THashMap<PaintParamsKey,
281                                               UniquePaintParamsID,
282                                               PaintParamsKey::Hash>;
283 
284     PaintIDMap fPaintKeyToID SK_GUARDED_BY(fSpinLock);
285     skia_private::TArray<PaintParamsKey> fIDToPaintKey SK_GUARDED_BY(fSpinLock);
286 
287     SK_BEGIN_REQUIRE_DENSE
288     struct RuntimeEffectKey {
289         uint32_t fHash;
290         uint32_t fUniformSize;
291 
292         bool operator==(RuntimeEffectKey rhs) const {
293             return fHash == rhs.fHash && fUniformSize == rhs.fUniformSize;
294         }
295     };
296     SK_END_REQUIRE_DENSE
297 
298     // A map from RuntimeEffectKeys (hash plus uniforms) to code-snippet IDs. RuntimeEffectKeys
299     // don't track the lifetime of a runtime effect at all; they live forever, and a newly-
300     // instantiated runtime effect with the same program as a previously-discarded effect will reuse
301     // an existing ID. Entries in the runtime-effect map are never removed; they only disappear when
302     // the context is discarded, which takes the ShaderCodeDictionary along with it. However, they
303     // are extremely small (< 20 bytes) so the memory footprint should be unnoticeable.
304     using RuntimeEffectMap = skia_private::THashMap<RuntimeEffectKey, int32_t>;
305     RuntimeEffectMap fRuntimeEffectMap SK_GUARDED_BY(fSpinLock);
306 
307     // This arena holds:
308     //   - the backing data for PaintParamsKeys in `fPaintKeyToID` and `fIDToPaintKey`
309     //   - Uniform data created by `findOrCreateRuntimeEffectSnippet`
310     // and in all cases is guarded by `fSpinLock`
311     SkArenaAlloc fArena{256};
312 };
313 
314 } // namespace skgpu::graphite
315 
316 #endif // skgpu_graphite_ShaderCodeDictionary_DEFINED
317