/* * 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/FactoryFunctions.h" #include "src/core/SkRuntimeEffectPriv.h" #include "src/gpu/graphite/KeyContext.h" #include "src/gpu/graphite/KeyHelpers.h" #include "src/gpu/graphite/PaintParamsKey.h" #include "src/gpu/graphite/Precompile.h" #include "src/gpu/graphite/PrecompileBasePriv.h" #include "src/shaders/SkShaderBase.h" namespace skgpu::graphite { //-------------------------------------------------------------------------------------------------- class PrecompileBlendModeBlender : public PrecompileBlender { public: PrecompileBlendModeBlender(SkBlendMode blendMode) : fBlendMode(blendMode) {} std::optional asBlendMode() const final { return fBlendMode; } private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); // The blend mode blender only ever has one combination // The blend mode is used in this BeginBlock! It is used to choose between fixed function // and shader-based blending BlendModeBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, fBlendMode); builder->endBlock(); } SkBlendMode fBlendMode; }; sk_sp PrecompileBlender::Mode(SkBlendMode blendMode) { return sk_make_sp(blendMode); } //-------------------------------------------------------------------------------------------------- class PrecompileColorShader : public PrecompileShader { public: PrecompileColorShader() {} private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); // The color shader only ever has one combination constexpr SkPMColor4f kUnusedColor = { 1, 0, 0, 1 }; SolidColorShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, kUnusedColor); // color isn't used w/o a gatherer builder->endBlock(); } }; sk_sp PrecompileShaders::Color() { return sk_make_sp(); } //-------------------------------------------------------------------------------------------------- class PrecompileBlendShader : public PrecompileShader { public: PrecompileBlendShader(SkSpan> runtimeBlendEffects, SkSpan> dsts, SkSpan> srcs, bool needsPorterDuffBased, bool needsSeparableMode) : fRuntimeBlendEffects(runtimeBlendEffects.begin(), runtimeBlendEffects.end()) , fDstOptions(dsts.begin(), dsts.end()) , fSrcOptions(srcs.begin(), srcs.end()) { fNumBlenderCombos = 0; for (auto rt : fRuntimeBlendEffects) { fNumBlenderCombos += rt->numCombinations(); } if (needsPorterDuffBased) { ++fNumBlenderCombos; } if (needsSeparableMode) { ++fNumBlenderCombos; } SkASSERT(fNumBlenderCombos >= 1); fNumDstCombos = 0; for (auto d : fDstOptions) { fNumDstCombos += d->numCombinations(); } fNumSrcCombos = 0; for (auto s : fSrcOptions) { fNumSrcCombos += s->numCombinations(); } if (needsPorterDuffBased) { fPorterDuffIndex = 0; if (needsSeparableMode) { fSeparableModeIndex = 1; if (!fRuntimeBlendEffects.empty()) { fBlenderIndex = 2; } } else if (!fRuntimeBlendEffects.empty()) { fBlenderIndex = 1; } } else if (needsSeparableMode) { fSeparableModeIndex = 0; if (!fRuntimeBlendEffects.empty()) { fBlenderIndex = 1; } } else { SkASSERT(!fRuntimeBlendEffects.empty()); fBlenderIndex = 0; } } private: int numChildCombinations() const override { return fNumBlenderCombos * fNumDstCombos * fNumSrcCombos; } void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination < this->numCombinations()); const int desiredDstCombination = desiredCombination % fNumDstCombos; int remainingCombinations = desiredCombination / fNumDstCombos; const int desiredSrcCombination = remainingCombinations % fNumSrcCombos; remainingCombinations /= fNumSrcCombos; int desiredBlendCombination = remainingCombinations; SkASSERT(desiredBlendCombination < fNumBlenderCombos); if (desiredBlendCombination == fPorterDuffIndex) { PorterDuffBlendShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, {}); // Porter/Duff coeffs aren't used } else if (desiredBlendCombination == fSeparableModeIndex) { BlendShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, { SkBlendMode::kOverlay }); // the blendmode is unused } else { // TODO: share this with the copy over in SkComposeShader.cpp. For now, the block ID is // determined by a hash of the code so both copies will generate the same key and // the SkPaint vs. PaintOptions key match testing will trigger if they get out of // sync. static SkRuntimeEffect* sBlendEffect = SkMakeRuntimeEffect( SkRuntimeEffect::MakeForShader, "uniform blender b;" "uniform shader d, s;" "half4 main(float2 xy) {" "return b.eval(s.eval(xy), d.eval(xy));" "}" ); RuntimeEffectBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, { sk_ref_sp(sBlendEffect) }); SkASSERT(desiredBlendCombination >= fBlenderIndex); desiredBlendCombination -= fBlenderIndex; AddToKey(keyContext, builder, fRuntimeBlendEffects, desiredBlendCombination); } AddToKey(keyContext, builder, fDstOptions, desiredDstCombination); AddToKey(keyContext, builder, fSrcOptions, desiredSrcCombination); builder->endBlock(); } std::vector> fRuntimeBlendEffects; std::vector> fDstOptions; std::vector> fSrcOptions; int fNumBlenderCombos; int fNumDstCombos; int fNumSrcCombos; int fPorterDuffIndex = -1; int fSeparableModeIndex = -1; int fBlenderIndex = -1; }; sk_sp PrecompileShaders::Blend( SkSpan> blenders, SkSpan> dsts, SkSpan> srcs) { std::vector> tmp; tmp.reserve(blenders.size()); bool needsPorterDuffBased = false; bool needsBlendModeBased = false; for (auto b : blenders) { if (!b) { needsPorterDuffBased = true; // fall back to kSrcOver } else if (b->asBlendMode().has_value()) { SkBlendMode bm = b->asBlendMode().value(); if (bm <= SkBlendMode::kLastCoeffMode) { needsPorterDuffBased = true; } else { needsBlendModeBased = true; } } else { tmp.push_back(b); } } if (!needsPorterDuffBased && !needsBlendModeBased && tmp.empty()) { needsPorterDuffBased = true; // fallback to kSrcOver } return sk_make_sp(SkSpan>(tmp), dsts, srcs, needsPorterDuffBased, needsBlendModeBased); } sk_sp PrecompileShaders::Blend( SkSpan blendModes, SkSpan> dsts, SkSpan> srcs) { bool needsPorterDuffBased = false; bool needsBlendModeBased = false; for (SkBlendMode bm : blendModes) { SkSpan porterDuffConstants = skgpu::GetPorterDuffBlendConstants(bm); if (!porterDuffConstants.empty()) { needsPorterDuffBased = true; } else { needsBlendModeBased = true; } } if (!needsPorterDuffBased && !needsBlendModeBased) { needsPorterDuffBased = true; // fallback to kSrcOver } return sk_make_sp(SkSpan>(), dsts, srcs, needsPorterDuffBased, needsBlendModeBased); } //-------------------------------------------------------------------------------------------------- class PrecompileImageShader : public PrecompileShader { public: PrecompileImageShader() {} private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); ImageShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, /* imgData= */ nullptr); builder->endBlock(); } }; sk_sp PrecompileShaders::Image() { return sk_make_sp(); } //-------------------------------------------------------------------------------------------------- class PrecompileGradientShader : public PrecompileShader { public: PrecompileGradientShader(SkShaderBase::GradientType type) : fType(type) {} private: /* * The gradients currently have two specializations based on the number of stops. */ inline static constexpr int kNumStopVariants = 2; inline static constexpr int kStopVariants[kNumStopVariants] = { 4, 8 }; int numIntrinsicCombinations() const override { return kNumStopVariants; } void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { const int intrinsicCombination = desiredCombination / this->numChildCombinations(); SkDEBUGCODE(int childCombination = desiredCombination % this->numChildCombinations();) SkASSERT(intrinsicCombination < kNumStopVariants); SkASSERT(childCombination == 0); // Only the type and number of stops are accessed when there is no gatherer GradientShaderBlocks::GradientData gradData(fType, kStopVariants[intrinsicCombination]); // TODO: we may need SkLocalMatrixShader-wrapped versions too ColorFilterShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr); GradientShaderBlocks::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, gradData); builder->endBlock(); ColorSpaceTransformBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, /* data= */ nullptr); builder->endBlock(); builder->endBlock(); } SkShaderBase::GradientType fType; }; sk_sp PrecompileShaders::LinearGradient() { return sk_make_sp(SkShaderBase::GradientType::kLinear); } sk_sp PrecompileShaders::RadialGradient() { return sk_make_sp(SkShaderBase::GradientType::kRadial); } sk_sp PrecompileShaders::SweepGradient() { return sk_make_sp(SkShaderBase::GradientType::kSweep); } sk_sp PrecompileShaders::TwoPointConicalGradient() { return sk_make_sp(SkShaderBase::GradientType::kConical); } //-------------------------------------------------------------------------------------------------- class PrecompileLocalMatrixShader : public PrecompileShader { public: PrecompileLocalMatrixShader(sk_sp wrapped) : fWrapped(std::move(wrapped)) {} private: bool isALocalMatrixShader() const override { return true; } int numChildCombinations() const override { return fWrapped->numChildCombinations(); } void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination < fWrapped->numCombinations()); LocalMatrixShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, /* lmShaderData= */ nullptr); fWrapped->priv().addToKey(keyContext, desiredCombination, builder); builder->endBlock(); } sk_sp fWrapped; }; sk_sp PrecompileShaders::LocalMatrix(sk_sp wrapped) { return sk_make_sp(std::move(wrapped)); } //-------------------------------------------------------------------------------------------------- class PrecompileColorFilterShader : public PrecompileShader { public: PrecompileColorFilterShader(sk_sp shader, sk_sp cf) : fShader(std::move(shader)) , fColorFilter(std::move(cf)) {} private: int numChildCombinations() const override { const int numShaderCombos = fShader->numCombinations(); const int numColorFilterCombos = fColorFilter->numCombinations(); return numShaderCombos * numColorFilterCombos; } void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination < this->numCombinations()); const int numShaderCombos = fShader->numCombinations(); SkDEBUGCODE(int numColorFilterCombos = fColorFilter->numCombinations();) int desiredShaderCombination = desiredCombination % numShaderCombos; int desiredColorFilterCombination = desiredCombination / numShaderCombos; SkASSERT(desiredColorFilterCombination < numColorFilterCombos); ColorFilterShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr); fShader->priv().addToKey(keyContext, desiredShaderCombination, builder); fColorFilter->priv().addToKey(keyContext, desiredColorFilterCombination, builder); builder->endBlock(); } sk_sp fShader; sk_sp fColorFilter; }; sk_sp PrecompileShaders::ColorFilter(sk_sp shader, sk_sp cf) { return sk_make_sp(std::move(shader), std::move(cf)); } //-------------------------------------------------------------------------------------------------- class PrecompileBlurMaskFilter : public PrecompileMaskFilter { public: PrecompileBlurMaskFilter() {} private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); // TODO: need to add a BlurMaskFilter Block. This is somewhat blocked on figuring out // what we're going to do with the Blur system. } }; sk_sp PrecompileMaskFilters::Blur() { return sk_make_sp(); } //-------------------------------------------------------------------------------------------------- class PrecompileBlendColorFilter : public PrecompileColorFilter { public: PrecompileBlendColorFilter() {} private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); BlendColorFilterBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, /* blendCFData= */ nullptr); builder->endBlock(); } }; sk_sp PrecompileColorFilters::Blend() { return sk_make_sp(); } //-------------------------------------------------------------------------------------------------- class PrecompileMatrixColorFilter : public PrecompileColorFilter { public: PrecompileMatrixColorFilter() {} private: void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination == 0); MatrixColorFilterBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, /* matrixCFData= */ nullptr); builder->endBlock(); } }; sk_sp PrecompileColorFilters::Matrix() { return sk_make_sp(); } sk_sp PrecompileColorFilters::HSLAMatrix() { return sk_make_sp(); } //-------------------------------------------------------------------------------------------------- // TODO: need to figure out how we're going to decompose ImageFilters sk_sp PrecompileImageFilters::Blur() { return nullptr; // sk_make_sp(); } sk_sp PrecompileImageFilters::Image() { return nullptr; // sk_make_sp(); } //-------------------------------------------------------------------------------------------------- PrecompileChildPtr::PrecompileChildPtr(sk_sp s) : fChild(std::move(s)) {} PrecompileChildPtr::PrecompileChildPtr(sk_sp cf) : fChild(std::move(cf)) { } PrecompileChildPtr::PrecompileChildPtr(sk_sp b) : fChild(std::move(b)) {} namespace { #ifdef SK_DEBUG bool precompilebase_is_valid_as_child(const PrecompileBase *child) { if (!child) { return true; } switch (child->type()) { case PrecompileBase::Type::kShader: case PrecompileBase::Type::kColorFilter: case PrecompileBase::Type::kBlender: return true; default: return false; } } #endif // SK_DEBUG } // anonymous namespace PrecompileChildPtr::PrecompileChildPtr(sk_sp child) : fChild(std::move(child)) { SkASSERT(precompilebase_is_valid_as_child(fChild.get())); } std::optional PrecompileChildPtr::type() const { if (fChild) { switch (fChild->type()) { case PrecompileBase::Type::kShader: return SkRuntimeEffect::ChildType::kShader; case PrecompileBase::Type::kColorFilter: return SkRuntimeEffect::ChildType::kColorFilter; case PrecompileBase::Type::kBlender: return SkRuntimeEffect::ChildType::kBlender; default: break; } } return std::nullopt; } PrecompileShader* PrecompileChildPtr::shader() const { return (fChild && fChild->type() == PrecompileBase::Type::kShader) ? static_cast(fChild.get()) : nullptr; } PrecompileColorFilter* PrecompileChildPtr::colorFilter() const { return (fChild && fChild->type() == PrecompileBase::Type::kColorFilter) ? static_cast(fChild.get()) : nullptr; } PrecompileBlender* PrecompileChildPtr::blender() const { return (fChild && fChild->type() == PrecompileBase::Type::kBlender) ? static_cast(fChild.get()) : nullptr; } //-------------------------------------------------------------------------------------------------- namespace { int num_options_in_set(const std::vector& optionSet) { int numOptions = 1; for (const PrecompileChildPtr& childOption : optionSet) { // A missing child will fall back to a passthrough object if (childOption.base()) { numOptions *= childOption.base()->numCombinations(); } } return numOptions; } // This is the precompile correlate to SkRuntimeEffect.cpp's add_children_to_key void add_children_to_key(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder, const std::vector& optionSet, SkSpan childInfo) { using ChildType = SkRuntimeEffect::ChildType; SkASSERT(optionSet.size() == childInfo.size()); int remainingCombinations = desiredCombination; for (size_t index = 0; index < optionSet.size(); ++index) { const PrecompileChildPtr& childOption = optionSet[index]; const int numChildCombos = childOption.base() ? childOption.base()->numCombinations() : 1; const int curCombo = remainingCombinations % numChildCombos; remainingCombinations /= numChildCombos; std::optional type = childOption.type(); if (type == ChildType::kShader) { childOption.shader()->priv().addToKey(keyContext, curCombo, builder); } else if (type == ChildType::kColorFilter) { childOption.colorFilter()->priv().addToKey(keyContext, curCombo, builder); } else if (type == ChildType::kBlender) { childOption.blender()->priv().addToKey(keyContext, curCombo, builder); } else { SkASSERT(curCombo == 0); // We don't have a child effect. Substitute in a no-op effect. switch (childInfo[index].type) { case ChildType::kShader: case ChildType::kColorFilter: // A "passthrough" shader returns the input color as-is. PassthroughShaderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr); builder->endBlock(); break; case ChildType::kBlender: // A "passthrough" blender performs `blend_src_over(src, dest)`. PassthroughBlenderBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr); builder->endBlock(); break; } } } } } // anonymous namespace template class PrecompileRTEffect : public T { public: PrecompileRTEffect(sk_sp effect, SkSpan childOptions) : fEffect(std::move(effect)) { fChildOptions.reserve(childOptions.size()); for (PrecompileChildOptions c : childOptions) { fChildOptions.push_back({ c.begin(), c.end() }); } } private: int numChildCombinations() const override { int numOptions = 0; for (const std::vector& optionSet : fChildOptions) { numOptions += num_options_in_set(optionSet); } return numOptions ? numOptions : 1; } void addToKey(const KeyContext& keyContext, int desiredCombination, PaintParamsKeyBuilder* builder) const override { SkASSERT(desiredCombination < this->numCombinations()); SkSpan childInfo = fEffect->children(); RuntimeEffectBlock::BeginBlock(keyContext, builder, /* gatherer= */ nullptr, { fEffect }); for (const std::vector& optionSet : fChildOptions) { int numOptionsInSet = num_options_in_set(optionSet); if (desiredCombination < numOptionsInSet) { add_children_to_key(keyContext, desiredCombination, builder, optionSet, childInfo); break; } desiredCombination -= numOptionsInSet; } builder->endBlock(); } sk_sp fEffect; std::vector> fChildOptions; }; sk_sp MakePrecompileShader( sk_sp effect, SkSpan childOptions) { // TODO: check that 'effect' has the kAllowShader_Flag bit set and: // for each entry in childOptions: // all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect return sk_make_sp>(std::move(effect), childOptions); } sk_sp MakePrecompileColorFilter( sk_sp effect, SkSpan childOptions) { // TODO: check that 'effect' has the kAllowColorFilter_Flag bit set and: // for each entry in childOptions: // all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect return sk_make_sp>(std::move(effect), childOptions); } sk_sp MakePrecompileBlender( sk_sp effect, SkSpan childOptions) { // TODO: check that 'effect' has the kAllowBlender_Flag bit set and: // for each entry in childOptions: // all the SkPrecompileChildPtrs have the same type as the corresponding child in the effect return sk_make_sp>(std::move(effect), childOptions); } } // namespace skgpu::graphite //--------------------------------------------------------------------------------------------------