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