/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/ShaderCodeDictionary.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkTileMode.h" #include "include/effects/SkRuntimeEffect.h" #include "include/gpu/graphite/Context.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkRuntimeEffectPriv.h" #include "src/core/SkSLTypeShared.h" #include "src/gpu/BlendFormula.h" #include "src/gpu/Swizzle.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/ContextUtils.h" #include "src/gpu/graphite/ReadSwizzle.h" #include "src/gpu/graphite/Renderer.h" #include "src/gpu/graphite/RuntimeEffectDictionary.h" #include "src/sksl/SkSLString.h" #include "src/sksl/SkSLUtil.h" #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h" #include "src/sksl/ir/SkSLVarDeclarations.h" #include <new> usingnamespaceskia_private; usingnamespaceSkKnownRuntimeEffects; namespace skgpu::graphite { static_assert …; namespace { const char* get_known_rte_name(StableKey key) { … } std::string get_mangled_name(const std::string& baseName, int manglingSuffix) { … } std::string get_mangled_uniform_name(const ShaderInfo& shaderInfo, const Uniform& uniform, int manglingSuffix) { … } std::string get_mangled_sampler_name(const TextureAndSampler& tex, int manglingSuffix) { … } std::string get_mangled_struct_reference(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } std::string stitch_csv(SkSpan<const std::string> args) { … } static const ShaderSnippet::Args kDefaultArgs{ … }; // If 'args' is null, the generated list is assumed to be for parameter declarations. If it's non // null, it is assumed to be the expressions to invoke the default signature. void append_defaults(TArray<std::string>* list, const ShaderNode* node, const ShaderSnippet::Args* args) { … } static const char* kGradientBufferName = …; void append_uniforms(TArray<std::string>* list, const ShaderInfo& shaderInfo, const ShaderNode* node, SkSpan<const std::string> childOutputs) { … } // If we have no children, the default expression just calls a built-in snippet with the signature: // half4 BuiltinFunctionName(/* required variable inputs (e.g. float2 pos) */, // /* all uniforms as parameters (bound to node's values) */) { ... } // If we do have children, we will have created a glue function in the preamble and that is called // instead. Its signature looks like this: // half4 SnippetName_N(/* required variable inputs (e.g. float2 pos) */) { ... } std::string invoke_node(const ShaderInfo& shaderInfo, const ShaderNode* node, const ShaderSnippet::Args& args) { … } // Emit the glue code needed to invoke a single static helper isolated within its own scope. // Glue code will assign the resulting color into a variable `half4 outColor%d`, where the %d is // filled in with 'node->keyIndex()'. std::string invoke_and_assign_node(const ShaderInfo& shaderInfo, const ShaderNode* node, const ShaderSnippet::Args& args, std::string* funcBody) { … } // Emit a declaration for a helper function that represents the ShaderNode (named using the node's // mangled name). The dynamic parameters are declared to match kDefaultArgs. The returned string // can either be followed by a "{ body }" to fully define it or a ";" for a forward declaration. std::string emit_helper_declaration(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } // If we have no children, we don't need to add anything into the preamble. // If we have child entries, we create a function in the preamble with a signature of: // half4 SnippetName_N(/* required variable inputs (e.g. float2 pos) */) { ... } // This function invokes each child in sequence, and then calls the built-in function, passing all // uniforms and child outputs along: // half4 BuiltinFunctionName(/* required variable inputs (e.g. float2 pos) */, // /* all uniforms as parameters */, // /* all child output variable names as parameters */); std::string generate_default_preamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } // Walk the node tree and generate all preambles, accumulating into 'preamble'. void emit_preambles(const ShaderInfo& shaderInfo, SkSpan<const ShaderNode*> nodes, std::string treeLabel, std::string* preamble) { … } constexpr skgpu::BlendInfo make_simple_blendInfo(skgpu::BlendCoeff srcCoeff, skgpu::BlendCoeff dstCoeff) { … } static constexpr int kNumCoeffModes = …; static constexpr skgpu::BlendInfo gBlendTable[kNumCoeffModes] = …; } // anonymous namespace //-------------------------------------------------------------------------------------------------- // ShaderInfo ShaderInfo::ShaderInfo(UniquePaintParamsID id, const ShaderCodeDictionary* dict, const RuntimeEffectDictionary* rteDict, const char* ssboIndex) : … { … } void ShaderInfo::aggregateSnippetData(const ShaderNode* node) { … } void append_color_output(std::string* mainBody, BlendFormula::OutputType outputType, const char* outColor, const char* inColor) { … } // The current, incomplete, model for shader construction is: // - Static code snippets (which can have an arbitrary signature) live in the Graphite // pre-compiled modules, which are located at `src/sksl/sksl_graphite_frag.sksl` and // `src/sksl/sksl_graphite_frag_es2.sksl`. // - Glue code is generated in a `main` method which calls these static code snippets. // The glue code is responsible for: // 1) gathering the correct (mangled) uniforms // 2) passing the uniforms and any other parameters to the helper method // - The result of the final code snippet is then copied into "sk_FragColor". // Note: each entry's 'fStaticFunctionName' field is expected to match the name of a function // in the Graphite pre-compiled module, or be null if the preamble and expression generators are // overridden to not use a static function. std::string ShaderInfo::toSkSL(const Caps* caps, const RenderStep* step, bool useStorageBuffers, int* numTexturesAndSamplersUsed, bool* hasPaintUniforms, bool* hasGradientBuffer, Swizzle writeSwizzle) { … } //-------------------------------------------------------------------------------------------------- // ShaderCodeDictionary UniquePaintParamsID ShaderCodeDictionary::findOrCreate(PaintParamsKeyBuilder* builder) { … } PaintParamsKey ShaderCodeDictionary::lookup(UniquePaintParamsID codeID) const { … } const ShaderSnippet* ShaderCodeDictionary::getEntry(int codeSnippetID) const { … } //-------------------------------------------------------------------------------------------------- namespace { // NOTE: The dst-read snippets have 0 children and could be described by a static module function // except that for now they need to stash the read surfaceColor in a global variable. Instead of // generating a mangled preamble helper function, these preambles just add a "static" function // that can be called with the default expression generator. Since there should only ever be one // dst-read snippet in a paint, the lack of mangling will detect if that property is violated. std::string GenerateDstReadSamplePreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } std::string GenerateDstReadFetchPreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } //-------------------------------------------------------------------------------------------------- std::string GenerateClipShaderPreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } //-------------------------------------------------------------------------------------------------- static constexpr int kNumCoordinateManipulateChildren = …; // Create a helper function that manipulates the coordinates passed into a child. The specific // manipulation is pre-determined by the code id (local matrix or clamp). This helper function meets // the requirements for use with GenerateDefaultExpression, so there's no need to have a separate // special GenerateLocalMatrixExpression. // TODO: This is effectively GenerateComposePreamble except that 'node' is counting as the inner. std::string GenerateCoordManipulationPreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } //-------------------------------------------------------------------------------------------------- // Compose N-1 children into the Nth child, must have at least two children. The ith child provides // the value for the ith enabled ShaderSnippet::Arg. std::string GenerateComposePreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } //-------------------------------------------------------------------------------------------------- class GraphitePipelineCallbacks : public SkSL::PipelineStage::Callbacks { … }; std::string GenerateRuntimeShaderPreamble(const ShaderInfo& shaderInfo, const ShaderNode* node) { … } } // anonymous namespace #if defined(SK_DEBUG) bool ShaderCodeDictionary::isValidID(int snippetID) const { … } void ShaderCodeDictionary::dump(UniquePaintParamsID id) const { … } #endif static SkSLType uniform_type_to_sksl_type(const SkRuntimeEffect::Uniform& u) { … } const char* ShaderCodeDictionary::addTextToArena(std::string_view text) { … } SkSpan<const Uniform> ShaderCodeDictionary::convertUniforms(const SkRuntimeEffect* effect) { … } ShaderSnippet ShaderCodeDictionary::convertRuntimeEffect(const SkRuntimeEffect* effect, const char* name) { … } int ShaderCodeDictionary::findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect) { … } ShaderCodeDictionary::ShaderCodeDictionary(Layout layout) : … { … } // clang-format off // Verify that the built-in code IDs for fixed function blending are consistent with SkBlendMode. static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; // Verify enum constants match values expected by static module SkSL functions static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; // TODO: We can meaningfully check these when we can use C++20 features. // static_assert(0x1 == SkColorSpaceXformSteps::Flags{.unpremul = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags"); // static_assert(0x2 == SkColorSpaceXformSteps::Flags{.linearize = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags"); // static_assert(0x4 == SkColorSpaceXformSteps::Flags{.gamut_transform = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags"); // static_assert(0x8 == SkColorSpaceXformSteps::Flags{.encode = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags"); // static_assert(0x10 == SkColorSpaceXformSteps::Flags{.premul = true}.mask(), "ColorSpaceTransform code depends on SkColorSpaceXformSteps::Flags"); static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; static_assert …; // clang-format on } // namespace skgpu::graphite