• 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/SkSpan.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/SkSpinlock.h"
14 #include "include/private/base/SkMacros.h"
15 #include "include/private/base/SkThreadAnnotations.h"
16 #include "include/private/base/SkTo.h"
17 #include "src/base/SkArenaAlloc.h"
18 #include "src/core/SkEnumBitMask.h"
19 #include "src/core/SkTHash.h"
20 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
21 #include "src/gpu/graphite/PaintParamsKey.h"
22 #include "src/gpu/graphite/Uniform.h"
23 #include "src/gpu/graphite/UniquePaintParamsID.h"
24 
25 #include <array>
26 #include <cstddef>
27 #include <cstdint>
28 #include <memory>
29 #include <string>
30 #include <string_view>
31 #include <vector>
32 
33 class SkRuntimeEffect;
34 
35 namespace skgpu::graphite {
36 
37 class RenderStep;
38 class RuntimeEffectDictionary;
39 
40 struct ResourceBindingRequirements;
41 
42 // TODO: How to represent the type (e.g., 2D) of texture being sampled?
43 class TextureAndSampler {
44 public:
TextureAndSampler(const char * name)45     constexpr TextureAndSampler(const char* name) : fName(name) {}
46 
name()47     const char* name() const { return fName; }
48 
49 private:
50     const char* fName;
51 };
52 
53 enum class SnippetRequirementFlags : uint32_t {
54     kNone = 0x0,
55     kLocalCoords = 0x1,
56     kPriorStageOutput = 0x2,  // AKA the "input" color, or the "source" color for a blender
57     kDestColor = 0x4,
58 };
59 SK_MAKE_BITMASK_OPS(SnippetRequirementFlags);
60 
61 struct ShaderSnippet {
62     using GeneratePreambleForSnippetFn = void (*)(const ShaderInfo& shaderInfo,
63                                                   int* entryIndex,
64                                                   const PaintParamsKey::BlockReader&,
65                                                   std::string* preamble);
66 
67     struct Args {
68         std::string_view fPriorStageOutput;
69         std::string_view fDestColor;
70         std::string_view fFragCoord;
71     };
72     using GenerateExpressionForSnippetFn = std::string (*)(const ShaderInfo& shaderInfo,
73                                                            int entryIndex,
74                                                            const PaintParamsKey::BlockReader&,
75                                                            const Args& args);
76 
77     ShaderSnippet() = default;
78 
ShaderSnippetShaderSnippet79     ShaderSnippet(const char* name,
80                   SkSpan<const Uniform> uniforms,
81                   SkEnumBitMask<SnippetRequirementFlags> snippetRequirementFlags,
82                   SkSpan<const TextureAndSampler> texturesAndSamplers,
83                   const char* functionName,
84                   GenerateExpressionForSnippetFn expressionGenerator,
85                   GeneratePreambleForSnippetFn preambleGenerator,
86                   int numChildren,
87                   SkSpan<const PaintParamsKey::DataPayloadField> dataPayloadExpectations)
88         : fName(name)
89         , fUniforms(uniforms)
90         , fSnippetRequirementFlags(snippetRequirementFlags)
91         , fTexturesAndSamplers(texturesAndSamplers)
92         , fStaticFunctionName(functionName)
93         , fExpressionGenerator(expressionGenerator)
94         , fPreambleGenerator(preambleGenerator)
95         , fNumChildren(numChildren)
96         , fDataPayloadExpectations(dataPayloadExpectations) {}
97 
98     std::string getMangledUniformName(const ShaderInfo& shaderInfo,
99                                       int uniformIdx,
100                                       int mangleId) const;
101     std::string getMangledSamplerName(int samplerIdx, int mangleId) const;
102 
needsLocalCoordsShaderSnippet103     bool needsLocalCoords() const {
104         return fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords;
105     }
needsPriorStageOutputShaderSnippet106     bool needsPriorStageOutput() const {
107         return fSnippetRequirementFlags & SnippetRequirementFlags::kPriorStageOutput;
108     }
needsDestColorShaderSnippet109     bool needsDestColor() const {
110         return fSnippetRequirementFlags & SnippetRequirementFlags::kDestColor;
111     }
112 
113     const char* fName = nullptr;
114     SkSpan<const Uniform> fUniforms;
115     SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags{SnippetRequirementFlags::kNone};
116     SkSpan<const TextureAndSampler> fTexturesAndSamplers;
117     const char* fStaticFunctionName = nullptr;
118     GenerateExpressionForSnippetFn fExpressionGenerator = nullptr;
119     GeneratePreambleForSnippetFn fPreambleGenerator = nullptr;
120     int fNumChildren = 0;
121     SkSpan<const PaintParamsKey::DataPayloadField> fDataPayloadExpectations;
122 };
123 
124 // This is just a simple collection object that gathers together all the information needed
125 // for program creation and its invocation.
126 class ShaderInfo {
127 public:
128     ShaderInfo(const RuntimeEffectDictionary* rteDict = nullptr,
129                const char* ssboIndex = nullptr)
fRuntimeEffectDictionary(rteDict)130             : fRuntimeEffectDictionary(rteDict)
131             , fSsboIndex(ssboIndex) {}
132     ~ShaderInfo() = default;
133     ShaderInfo(ShaderInfo&&) = default;
134     ShaderInfo& operator=(ShaderInfo&&) = default;
135     ShaderInfo(const ShaderInfo&) = delete;
136     ShaderInfo& operator=(const ShaderInfo&) = delete;
137 
add(const PaintParamsKey::BlockReader & reader)138     void add(const PaintParamsKey::BlockReader& reader) {
139         fBlockReaders.push_back(reader);
140     }
addFlags(SkEnumBitMask<SnippetRequirementFlags> flags)141     void addFlags(SkEnumBitMask<SnippetRequirementFlags> flags) {
142         fSnippetRequirementFlags |= flags;
143     }
needsLocalCoords()144     bool needsLocalCoords() const {
145         return fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords;
146     }
blockReader(int index)147     const PaintParamsKey::BlockReader& blockReader(int index) const {
148         return fBlockReaders[index];
149     }
runtimeEffectDictionary()150     const RuntimeEffectDictionary* runtimeEffectDictionary() const {
151         return fRuntimeEffectDictionary;
152     }
ssboIndex()153     const char* ssboIndex() const { return fSsboIndex; }
154 
setBlendInfo(const skgpu::BlendInfo & blendInfo)155     void setBlendInfo(const skgpu::BlendInfo& blendInfo) {
156         fBlendInfo = blendInfo;
157     }
blendInfo()158     const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; }
159 
160     std::string toSkSL(const ResourceBindingRequirements& bindingReqs,
161                        const RenderStep* step,
162                        const bool useStorageBuffers,
163                        const bool defineLocalCoordsVarying,
164                        int* numTexturesAndSamplersUsed) const;
165 
166 private:
167     std::vector<PaintParamsKey::BlockReader> fBlockReaders;
168 
169     SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags{SnippetRequirementFlags::kNone};
170     const RuntimeEffectDictionary* fRuntimeEffectDictionary = nullptr;
171 
172     const char* fSsboIndex;
173 
174     // The blendInfo doesn't actually contribute to the program's creation but, it contains the
175     // matching fixed-function settings that the program's caller needs to set up.
176     skgpu::BlendInfo fBlendInfo;
177 };
178 
179 class ShaderCodeDictionary {
180 public:
181     ShaderCodeDictionary();
182 
183     struct Entry {
184     public:
uniqueIDEntry185         UniquePaintParamsID uniqueID() const {
186             SkASSERT(fUniqueID.isValid());
187             return fUniqueID;
188         }
paintParamsKeyEntry189         const PaintParamsKey& paintParamsKey() const { return fKey; }
blendInfoEntry190         const skgpu::BlendInfo& blendInfo() const { return fBlendInfo; }
191 
192     private:
193         friend class ShaderCodeDictionary;
194 
EntryEntry195         Entry(const PaintParamsKey& key, const skgpu::BlendInfo& blendInfo)
196                 : fKey(key.asSpan())
197                 , fBlendInfo(blendInfo) {
198         }
199 
setUniqueIDEntry200         void setUniqueID(uint32_t newID) {
201             SkASSERT(!fUniqueID.isValid());
202             fUniqueID = UniquePaintParamsID(newID);
203         }
204 
205         UniquePaintParamsID fUniqueID;  // fixed-size (uint32_t) unique ID assigned to a key
206         PaintParamsKey fKey; // variable-length paint key descriptor
207 
208         // The BlendInfo isn't used in the hash (that is the key's job) but it does directly vary
209         // with the key. It could, theoretically, be recreated from the key but that would add
210         // extra complexity.
211         skgpu::BlendInfo fBlendInfo;
212     };
213 
214     const Entry* findOrCreate(PaintParamsKeyBuilder*) SK_EXCLUDES(fSpinLock);
215 
216     const Entry* lookup(UniquePaintParamsID) const SK_EXCLUDES(fSpinLock);
217 
218     SkSpan<const Uniform> getUniforms(BuiltInCodeSnippetID) const;
getSnippetRequirementFlags(BuiltInCodeSnippetID id)219     SkEnumBitMask<SnippetRequirementFlags> getSnippetRequirementFlags(
220             BuiltInCodeSnippetID id) const {
221         return fBuiltInCodeSnippets[(int) id].fSnippetRequirementFlags;
222     }
223 
224     SkSpan<const PaintParamsKey::DataPayloadField> dataPayloadExpectations(int snippetID) const;
225 
226     bool isValidID(int snippetID) const;
227 
228     // This method can return nullptr
229     const ShaderSnippet* getEntry(int codeSnippetID) const;
getEntry(BuiltInCodeSnippetID codeSnippetID)230     const ShaderSnippet* getEntry(BuiltInCodeSnippetID codeSnippetID) const {
231         return this->getEntry(SkTo<int>(codeSnippetID));
232     }
233 
234     void getShaderInfo(UniquePaintParamsID, ShaderInfo*) const;
235 
236     int findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect);
237 
238     int addUserDefinedSnippet(const char* name,
239                               SkSpan<const PaintParamsKey::DataPayloadField> expectations);
240 
241 private:
242     Entry* makeEntry(const PaintParamsKey&, const skgpu::BlendInfo&);
243 
244     // TODO: this is still experimental but, most likely, it will need to be made thread-safe
245     // It returns the code snippet ID to use to identify the supplied user-defined code
246     int addUserDefinedSnippet(
247         const char* name,
248         SkSpan<const Uniform> uniforms,
249         SkEnumBitMask<SnippetRequirementFlags> snippetRequirementFlags,
250         SkSpan<const TextureAndSampler> texturesAndSamplers,
251         const char* functionName,
252         ShaderSnippet::GenerateExpressionForSnippetFn expressionGenerator,
253         ShaderSnippet::GeneratePreambleForSnippetFn preambleGenerator,
254         int numChildren,
255         SkSpan<const PaintParamsKey::DataPayloadField> dataPayloadExpectations);
256 
257     const char* addTextToArena(std::string_view text);
258 
259     SkSpan<const Uniform> convertUniforms(const SkRuntimeEffect* effect);
260 
261     std::array<ShaderSnippet, kBuiltInCodeSnippetIDCount> fBuiltInCodeSnippets;
262 
263     // The value returned from 'getEntry' must be stable so, hold the user-defined code snippet
264     // entries as pointers.
265     std::vector<std::unique_ptr<ShaderSnippet>> fUserDefinedCodeSnippets;
266 
267     // TODO: can we do something better given this should have write-seldom/read-often behavior?
268     mutable SkSpinlock fSpinLock;
269 
270     struct PaintParamsKeyPtr {
271         const PaintParamsKey* fKey;
272 
273         bool operator==(PaintParamsKeyPtr rhs) const {
274             return *fKey == *rhs.fKey;
275         }
276         struct Hash {
277             size_t operator()(PaintParamsKeyPtr) const;
278         };
279     };
280 
281     using PaintHashMap = SkTHashMap<PaintParamsKeyPtr, Entry*, PaintParamsKeyPtr::Hash>;
282 
283     PaintHashMap fHash SK_GUARDED_BY(fSpinLock);
284     std::vector<Entry*> fEntryVector SK_GUARDED_BY(fSpinLock);
285 
286     SK_BEGIN_REQUIRE_DENSE
287     struct RuntimeEffectKey {
288         uint32_t fHash;
289         uint32_t fUniformSize;
290 
291         bool operator==(RuntimeEffectKey rhs) const {
292             return fHash == rhs.fHash && fUniformSize == rhs.fUniformSize;
293         }
294         struct Hash {
295             size_t operator()(RuntimeEffectKey) const;
296         };
297     };
298     SK_END_REQUIRE_DENSE
299 
300     // A map from RuntimeEffectKeys (hash plus uniforms) to code-snippet IDs. RuntimeEffectKeys
301     // don't track the lifetime of a runtime effect at all; they live forever, and a newly-
302     // instantiated runtime effect with the same program as a previously-discarded effect will reuse
303     // an existing ID. Entries in the runtime-effect map are never removed; they only disappear when
304     // the context is discarded, which takes the ShaderCodeDictionary along with it. However, they
305     // are extremely small (< 20 bytes) so the memory footprint should be unnoticeable.
306     using RuntimeEffectMap = SkTHashMap<RuntimeEffectKey, int32_t>;
307     RuntimeEffectMap fRuntimeEffectMap SK_GUARDED_BY(fSpinLock);
308 
309     // This arena holds:
310     //   - the Entries held in `fHash` and `fEntryVector`
311     //   - Uniform data created by `findOrCreateRuntimeEffectSnippet`
312     // and in all cases is guarded by `fSpinLock`
313     SkArenaAlloc fArena{256};
314 };
315 
316 } // namespace skgpu::graphite
317 
318 #endif // skgpu_graphite_ShaderCodeDictionary_DEFINED
319