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