| /* | |
| * 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/SkColorSpacePriv.h" | |
| #include "src/core/SkRuntimeEffectPriv.h" | |
| #include "src/gpu/Blend.h" | |
| #include "src/gpu/graphite/KeyContext.h" | |
| #include "src/gpu/graphite/KeyHelpers.h" | |
| #include "src/gpu/graphite/PaintParams.h" | |
| #include "src/gpu/graphite/PaintParamsKey.h" | |
| #include "src/gpu/graphite/Precompile.h" | |
| #include "src/gpu/graphite/PrecompileBasePriv.h" | |
| #include "src/gpu/graphite/ReadSwizzle.h" | |
| #include "src/shaders/SkShaderBase.h" | |
| namespace skgpu::graphite { | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileBlendModeBlender : public PrecompileBlender { | |
| public: | |
| PrecompileBlendModeBlender(SkBlendMode blendMode) : fBlendMode(blendMode) {} | |
| std::optional<SkBlendMode> asBlendMode() const final { return fBlendMode; } | |
| private: | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); // The blend mode blender only ever has one combination | |
| AddModeBlend(keyContext, builder, gatherer, fBlendMode); | |
| } | |
| SkBlendMode fBlendMode; | |
| }; | |
| sk_sp<PrecompileBlender> PrecompileBlender::Mode(SkBlendMode blendMode) { | |
| return sk_make_sp<PrecompileBlendModeBlender>(blendMode); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileColorShader : public PrecompileShader { | |
| public: | |
| PrecompileColorShader() {} | |
| bool isConstant() const override { return true; } | |
| private: | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); // The color shader only ever has one combination | |
| // The white PMColor is just a placeholder for the actual paint params color | |
| SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, SK_PMColor4fWHITE); | |
| } | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::Color() { | |
| return sk_make_sp<PrecompileColorShader>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileBlendShader : public PrecompileShader { | |
| public: | |
| PrecompileBlendShader(SkSpan<const sk_sp<PrecompileBlender>> runtimeBlendEffects, | |
| SkSpan<const sk_sp<PrecompileShader>> dsts, | |
| SkSpan<const sk_sp<PrecompileShader>> srcs, | |
| bool needsPorterDuffBased, | |
| bool needsSeparableMode) | |
| : fRuntimeBlendEffects(runtimeBlendEffects.begin(), runtimeBlendEffects.end()) | |
| , fDstOptions(dsts.begin(), dsts.end()) | |
| , fSrcOptions(srcs.begin(), srcs.end()) { | |
| fNumBlenderCombos = 0; | |
| for (const auto& rt : fRuntimeBlendEffects) { | |
| fNumBlenderCombos += rt->numCombinations(); | |
| } | |
| if (needsPorterDuffBased) { | |
| ++fNumBlenderCombos; | |
| } | |
| if (needsSeparableMode) { | |
| ++fNumBlenderCombos; | |
| } | |
| SkASSERT(fNumBlenderCombos >= 1); | |
| fNumDstCombos = 0; | |
| for (const auto& d : fDstOptions) { | |
| fNumDstCombos += d->numCombinations(); | |
| } | |
| fNumSrcCombos = 0; | |
| for (const 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, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) 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 || | |
| desiredBlendCombination == fSeparableModeIndex) { | |
| BlendShaderBlock::BeginBlock(keyContext, builder, gatherer); | |
| } 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 shader s, d;" | |
| "uniform blender b;" | |
| "half4 main(float2 xy) {" | |
| "return b.eval(s.eval(xy), d.eval(xy));" | |
| "}" | |
| ); | |
| RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, | |
| { sk_ref_sp(sBlendEffect) }); | |
| SkASSERT(desiredBlendCombination >= fBlenderIndex); | |
| desiredBlendCombination -= fBlenderIndex; | |
| } | |
| AddToKey<PrecompileShader>(keyContext, builder, gatherer, fSrcOptions, | |
| desiredSrcCombination); | |
| AddToKey<PrecompileShader>(keyContext, builder, gatherer, fDstOptions, | |
| desiredDstCombination); | |
| if (desiredBlendCombination == fPorterDuffIndex) { | |
| CoeffBlenderBlock::AddBlock(keyContext, builder, gatherer, | |
| { 0.0f, 0.0f, 0.0f, 0.0f }); // coeffs aren't used | |
| } else if (desiredBlendCombination == fSeparableModeIndex) { | |
| BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer, | |
| SkBlendMode::kOverlay); // the blendmode is unused | |
| } else { | |
| AddToKey<PrecompileBlender>(keyContext, builder, gatherer, fRuntimeBlendEffects, | |
| desiredBlendCombination); | |
| } | |
| builder->endBlock(); // BlendShaderBlock or RuntimeEffectBlock | |
| } | |
| std::vector<sk_sp<PrecompileBlender>> fRuntimeBlendEffects; | |
| std::vector<sk_sp<PrecompileShader>> fDstOptions; | |
| std::vector<sk_sp<PrecompileShader>> fSrcOptions; | |
| int fNumBlenderCombos; | |
| int fNumDstCombos; | |
| int fNumSrcCombos; | |
| int fPorterDuffIndex = -1; | |
| int fSeparableModeIndex = -1; | |
| int fBlenderIndex = -1; | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::Blend( | |
| SkSpan<const sk_sp<PrecompileBlender>> blenders, | |
| SkSpan<const sk_sp<PrecompileShader>> dsts, | |
| SkSpan<const sk_sp<PrecompileShader>> srcs) { | |
| std::vector<sk_sp<PrecompileBlender>> tmp; | |
| tmp.reserve(blenders.size()); | |
| bool needsPorterDuffBased = false; | |
| bool needsBlendModeBased = false; | |
| for (const auto& b : blenders) { | |
| if (!b) { | |
| needsPorterDuffBased = true; // fall back to kSrcOver | |
| } else if (b->asBlendMode().has_value()) { | |
| SkBlendMode bm = b->asBlendMode().value(); | |
| SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm); | |
| if (!coeffs.empty()) { | |
| needsPorterDuffBased = true; | |
| } else { | |
| needsBlendModeBased = true; | |
| } | |
| } else { | |
| tmp.push_back(b); | |
| } | |
| } | |
| if (!needsPorterDuffBased && !needsBlendModeBased && tmp.empty()) { | |
| needsPorterDuffBased = true; // fallback to kSrcOver | |
| } | |
| return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(tmp), | |
| dsts, srcs, | |
| needsPorterDuffBased, needsBlendModeBased); | |
| } | |
| sk_sp<PrecompileShader> PrecompileShaders::Blend( | |
| SkSpan<SkBlendMode> blendModes, | |
| SkSpan<const sk_sp<PrecompileShader>> dsts, | |
| SkSpan<const sk_sp<PrecompileShader>> srcs) { | |
| bool needsPorterDuffBased = false; | |
| bool needsBlendModeBased = false; | |
| for (SkBlendMode bm : blendModes) { | |
| SkSpan<const float> porterDuffConstants = skgpu::GetPorterDuffBlendConstants(bm); | |
| if (!porterDuffConstants.empty()) { | |
| needsPorterDuffBased = true; | |
| } else { | |
| needsBlendModeBased = true; | |
| } | |
| } | |
| if (!needsPorterDuffBased && !needsBlendModeBased) { | |
| needsPorterDuffBased = true; // fallback to kSrcOver | |
| } | |
| return sk_make_sp<PrecompileBlendShader>(SkSpan<const sk_sp<PrecompileBlender>>(), | |
| dsts, srcs, | |
| needsPorterDuffBased, needsBlendModeBased); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileImageShader : public PrecompileShader { | |
| public: | |
| PrecompileImageShader() {} | |
| private: | |
| // hardware-tiled, shader-tiled and cubic sampling | |
| inline static constexpr int kNumIntrinsicCombinations = 3; | |
| int numIntrinsicCombinations() const override { return kNumIntrinsicCombinations; } | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination < kNumIntrinsicCombinations); | |
| static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell()); | |
| static constexpr SkSamplingOptions kDefaultSampling; | |
| // ImageShaderBlock will use hardware tiling when the subset covers the entire image, so we | |
| // create subset + image size combinations where subset == imgSize (for a shader that uses | |
| // hardware tiling) and subset < imgSize (for a shader that does shader-based tiling). | |
| static constexpr SkRect kSubset = SkRect::MakeWH(1.0f, 1.0f); | |
| static constexpr SkISize kHwTileableSize = SkISize::Make(1, 1); | |
| static constexpr SkISize kNonHwTileableSize = SkISize::Make(2, 2); | |
| ImageShaderBlock::ImageData imgData(desiredCombination == 2 ? kDefaultCubicSampling | |
| : kDefaultSampling, | |
| SkTileMode::kClamp, SkTileMode::kClamp, | |
| desiredCombination == 1 ? kHwTileableSize | |
| : kNonHwTileableSize, | |
| kSubset, ReadSwizzle::kRGBA); | |
| ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData); | |
| } | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::Image() { | |
| return sk_make_sp<PrecompileImageShader>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| 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, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| const int intrinsicCombination = desiredCombination / this->numChildCombinations(); | |
| SkDEBUGCODE(int childCombination = desiredCombination % this->numChildCombinations();) | |
| SkASSERT(intrinsicCombination < kNumStopVariants); | |
| SkASSERT(childCombination == 0); | |
| GradientShaderBlocks::GradientData gradData(fType, kStopVariants[intrinsicCombination]); | |
| constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType; | |
| ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType, | |
| sk_srgb_singleton(), kAlphaType); | |
| // TODO: we may need SkLocalMatrixShader-wrapped versions too | |
| Compose(keyContext, builder, gatherer, | |
| /* addInnerToKey= */ [&]() -> void { | |
| GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData); | |
| }, | |
| /* addOuterToKey= */ [&]() -> void { | |
| ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData); | |
| }); | |
| } | |
| SkShaderBase::GradientType fType; | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::LinearGradient() { | |
| return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kLinear); | |
| } | |
| sk_sp<PrecompileShader> PrecompileShaders::RadialGradient() { | |
| return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kRadial); | |
| } | |
| sk_sp<PrecompileShader> PrecompileShaders::SweepGradient() { | |
| return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kSweep); | |
| } | |
| sk_sp<PrecompileShader> PrecompileShaders::TwoPointConicalGradient() { | |
| return sk_make_sp<PrecompileGradientShader>(SkShaderBase::GradientType::kConical); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileLocalMatrixShader : public PrecompileShader { | |
| public: | |
| PrecompileLocalMatrixShader(sk_sp<PrecompileShader> wrapped) : fWrapped(std::move(wrapped)) {} | |
| private: | |
| bool isALocalMatrixShader() const override { return true; } | |
| int numChildCombinations() const override { | |
| return fWrapped->numCombinations(); | |
| } | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination < fWrapped->numCombinations()); | |
| LocalMatrixShaderBlock::LMShaderData lmShaderData(SkMatrix::I()); | |
| LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, lmShaderData); | |
| fWrapped->priv().addToKey(keyContext, builder, gatherer, desiredCombination); | |
| builder->endBlock(); | |
| } | |
| sk_sp<PrecompileShader> fWrapped; | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::LocalMatrix(sk_sp<PrecompileShader> wrapped) { | |
| return sk_make_sp<PrecompileLocalMatrixShader>(std::move(wrapped)); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileColorFilterShader : public PrecompileShader { | |
| public: | |
| PrecompileColorFilterShader(sk_sp<PrecompileShader> shader, sk_sp<PrecompileColorFilter> 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, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) 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); | |
| Compose(keyContext, builder, gatherer, | |
| /* addInnerToKey= */ [&]() -> void { | |
| fShader->priv().addToKey(keyContext, builder, gatherer, | |
| desiredShaderCombination); | |
| }, | |
| /* addOuterToKey= */ [&]() -> void { | |
| fColorFilter->priv().addToKey(keyContext, builder, gatherer, | |
| desiredColorFilterCombination); | |
| }); | |
| } | |
| sk_sp<PrecompileShader> fShader; | |
| sk_sp<PrecompileColorFilter> fColorFilter; | |
| }; | |
| sk_sp<PrecompileShader> PrecompileShaders::ColorFilter(sk_sp<PrecompileShader> shader, | |
| sk_sp<PrecompileColorFilter> cf) { | |
| return sk_make_sp<PrecompileColorFilterShader>(std::move(shader), std::move(cf)); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileBlurMaskFilter : public PrecompileMaskFilter { | |
| public: | |
| PrecompileBlurMaskFilter() {} | |
| private: | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) 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<PrecompileMaskFilter> PrecompileMaskFilters::Blur() { | |
| return sk_make_sp<PrecompileBlurMaskFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileBlendModeColorFilter : public PrecompileColorFilter { | |
| public: | |
| PrecompileBlendModeColorFilter() {} | |
| private: | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); | |
| // Here, kSrcOver and the white color are just a stand-ins for some later blend mode | |
| // and color. | |
| AddBlendModeColorFilter(keyContext, builder, gatherer, | |
| SkBlendMode::kSrcOver, SK_PMColor4fWHITE); | |
| } | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Blend() { | |
| return sk_make_sp<PrecompileBlendModeColorFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileColorSpaceXformColorFilter : public PrecompileColorFilter { | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); | |
| constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType; | |
| ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType, | |
| sk_srgb_singleton(), kAlphaType); | |
| ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData); | |
| } | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::LinearToSRGBGamma() { | |
| return sk_make_sp<PrecompileColorSpaceXformColorFilter>(); | |
| } | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::SRGBToLinearGamma() { | |
| return sk_make_sp<PrecompileColorSpaceXformColorFilter>(); | |
| } | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::ColorSpaceXform() { | |
| return sk_make_sp<PrecompileColorSpaceXformColorFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileComposeColorFilter : public PrecompileColorFilter { | |
| public: | |
| PrecompileComposeColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions, | |
| SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions) | |
| : fOuterOptions(outerOptions.begin(), outerOptions.end()) | |
| , fInnerOptions(innerOptions.begin(), innerOptions.end()) { | |
| fNumOuterCombos = 0; | |
| for (const auto& outerOption : fOuterOptions) { | |
| fNumOuterCombos += outerOption ? outerOption->numCombinations() : 1; | |
| } | |
| fNumInnerCombos = 0; | |
| for (const auto& innerOption : fInnerOptions) { | |
| fNumInnerCombos += innerOption ? innerOption->numCombinations() : 1; | |
| } | |
| } | |
| private: | |
| int numChildCombinations() const override { return fNumOuterCombos * fNumInnerCombos; } | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination < this->numCombinations()); | |
| const int desiredOuterCombination = desiredCombination % fNumOuterCombos; | |
| int remainingCombinations = desiredCombination / fNumOuterCombos; | |
| const int desiredInnerCombination = remainingCombinations % fNumInnerCombos; | |
| remainingCombinations /= fNumInnerCombos; | |
| SkASSERT(!remainingCombinations); | |
| sk_sp<PrecompileColorFilter> inner, outer; | |
| int innerChildOptions, outerChildOptions; | |
| std::tie(outer, outerChildOptions) = SelectOption<PrecompileColorFilter>( | |
| fOuterOptions, desiredOuterCombination); | |
| std::tie(inner, innerChildOptions) = SelectOption<PrecompileColorFilter>( | |
| fInnerOptions, desiredInnerCombination); | |
| if (!inner && !outer) { | |
| // A "passthrough" color filter returns the input color as-is. | |
| builder->addBlock(BuiltInCodeSnippetID::kPriorOutput); | |
| } else if (!inner) { | |
| outer->priv().addToKey(keyContext, builder, gatherer, outerChildOptions); | |
| } else if (!outer) { | |
| inner->priv().addToKey(keyContext, builder, gatherer, innerChildOptions); | |
| } else { | |
| Compose(keyContext, builder, gatherer, | |
| /* addInnerToKey= */ [&]() -> void { | |
| inner->priv().addToKey(keyContext, builder, gatherer, innerChildOptions); | |
| }, | |
| /* addOuterToKey= */ [&]() -> void { | |
| outer->priv().addToKey(keyContext, builder, gatherer, outerChildOptions); | |
| }); | |
| } | |
| } | |
| std::vector<sk_sp<PrecompileColorFilter>> fOuterOptions; | |
| std::vector<sk_sp<PrecompileColorFilter>> fInnerOptions; | |
| int fNumOuterCombos; | |
| int fNumInnerCombos; | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Compose( | |
| SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions, | |
| SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions) { | |
| if (outerOptions.empty() && innerOptions.empty()) { | |
| return nullptr; | |
| } | |
| return sk_make_sp<PrecompileComposeColorFilter>(outerOptions, innerOptions); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileGaussianColorFilter : public PrecompileColorFilter { | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); | |
| builder->addBlock(BuiltInCodeSnippetID::kGaussianColorFilter); | |
| } | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Gaussian() { | |
| return sk_make_sp<PrecompileGaussianColorFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileMatrixColorFilter : public PrecompileColorFilter { | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); | |
| static constexpr float kIdentity[20] = { 1, 0, 0, 0, 0, | |
| 0, 1, 0, 0, 0, | |
| 0, 0, 1, 0, 0, | |
| 0, 0, 0, 1, 0 }; | |
| MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(kIdentity, /* inHSLA= */ false); | |
| MatrixColorFilterBlock::AddBlock(keyContext, builder, gatherer, matrixCFData); | |
| } | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Matrix() { | |
| return sk_make_sp<PrecompileMatrixColorFilter>(); | |
| } | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::HSLAMatrix() { | |
| return sk_make_sp<PrecompileMatrixColorFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileTableColorFilter : public PrecompileColorFilter { | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination == 0); | |
| TableColorFilterBlock::TableColorFilterData data(/* proxy= */ nullptr); | |
| TableColorFilterBlock::AddBlock(keyContext, builder, gatherer, data); | |
| } | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Table() { | |
| return sk_make_sp<PrecompileTableColorFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::Lighting() { | |
| return PrecompileColorFilters::Matrix(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| class PrecompileWithWorkingFormatColorFilter : public PrecompileColorFilter { | |
| public: | |
| PrecompileWithWorkingFormatColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> childOptions) | |
| : fChildOptions(childOptions.begin(), childOptions.end()) { | |
| fNumChildCombos = 0; | |
| for (const auto& childOption : fChildOptions) { | |
| fNumChildCombos += childOption->numCombinations(); | |
| } | |
| } | |
| private: | |
| int numChildCombinations() const override { return fNumChildCombos; } | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination < fNumChildCombos); | |
| constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType; | |
| ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType, | |
| sk_srgb_singleton(), kAlphaType); | |
| // Use two nested compose blocks to chain (dst->working), child, and (working->dst) together | |
| // while appearing as one block to the parent node. | |
| Compose(keyContext, builder, gatherer, | |
| /* addInnerToKey= */ [&]() -> void { | |
| // Inner compose | |
| Compose(keyContext, builder, gatherer, | |
| /* addInnerToKey= */ [&]() -> void { | |
| // Innermost (inner of inner compose) | |
| ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, | |
| csData); | |
| }, | |
| /* addOuterToKey= */ [&]() -> void { | |
| // Middle (outer of inner compose) | |
| AddToKey<PrecompileColorFilter>(keyContext, builder, gatherer, | |
| fChildOptions, desiredCombination); | |
| }); | |
| }, | |
| /* addOuterToKey= */ [&]() -> void { | |
| // Outermost (outer of outer compose) | |
| ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData); | |
| }); | |
| } | |
| std::vector<sk_sp<PrecompileColorFilter>> fChildOptions; | |
| int fNumChildCombos; | |
| }; | |
| sk_sp<PrecompileColorFilter> PrecompileColorFilters::WithWorkingFormat( | |
| SkSpan<const sk_sp<PrecompileColorFilter>> childOptions) { | |
| return sk_make_sp<PrecompileWithWorkingFormatColorFilter>(childOptions); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| //-------------------------------------------------------------------------------------------------- | |
| // TODO: need to figure out how we're going to decompose ImageFilters | |
| sk_sp<PrecompileImageFilter> PrecompileImageFilters::Blur() { | |
| return nullptr; // sk_make_sp<PrecompileImageFilter>(); | |
| } | |
| sk_sp<PrecompileImageFilter> PrecompileImageFilters::Image() { | |
| return nullptr; // sk_make_sp<PrecompileImageFilter>(); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileShader> s) : fChild(std::move(s)) {} | |
| PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileColorFilter> cf) | |
| : fChild(std::move(cf)) { | |
| } | |
| PrecompileChildPtr::PrecompileChildPtr(sk_sp<PrecompileBlender> 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<PrecompileBase> child) | |
| : fChild(std::move(child)) { | |
| SkASSERT(precompilebase_is_valid_as_child(fChild.get())); | |
| } | |
| std::optional<SkRuntimeEffect::ChildType> 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<PrecompileShader*>(fChild.get()) | |
| : nullptr; | |
| } | |
| PrecompileColorFilter* PrecompileChildPtr::colorFilter() const { | |
| return (fChild && fChild->type() == PrecompileBase::Type::kColorFilter) | |
| ? static_cast<PrecompileColorFilter*>(fChild.get()) | |
| : nullptr; | |
| } | |
| PrecompileBlender* PrecompileChildPtr::blender() const { | |
| return (fChild && fChild->type() == PrecompileBase::Type::kBlender) | |
| ? static_cast<PrecompileBlender*>(fChild.get()) | |
| : nullptr; | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| namespace { | |
| int num_options_in_set(const std::vector<PrecompileChildPtr>& 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 KeyHelper.cpp's add_children_to_key | |
| void add_children_to_key(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination, | |
| const std::vector<PrecompileChildPtr>& optionSet, | |
| SkSpan<const SkRuntimeEffect::Child> childInfo) { | |
| using ChildType = SkRuntimeEffect::ChildType; | |
| SkASSERT(optionSet.size() == childInfo.size()); | |
| KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect); | |
| 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<ChildType> type = childOption.type(); | |
| if (type == ChildType::kShader) { | |
| childOption.shader()->priv().addToKey(childContext, builder, gatherer, curCombo); | |
| } else if (type == ChildType::kColorFilter) { | |
| childOption.colorFilter()->priv().addToKey(childContext, builder, gatherer, curCombo); | |
| } else if (type == ChildType::kBlender) { | |
| childOption.blender()->priv().addToKey(childContext, builder, gatherer, curCombo); | |
| } else { | |
| SkASSERT(curCombo == 0); | |
| // We don't have a child effect. Substitute in a no-op effect. | |
| switch (childInfo[index].type) { | |
| case ChildType::kShader: | |
| // A missing shader returns transparent black | |
| SolidColorShaderBlock::AddBlock(childContext, builder, gatherer, | |
| SK_PMColor4fTRANSPARENT); | |
| break; | |
| case ChildType::kColorFilter: | |
| // A "passthrough" shader returns the input color as-is. | |
| builder->addBlock(BuiltInCodeSnippetID::kPriorOutput); | |
| break; | |
| case ChildType::kBlender: | |
| // A "passthrough" blender performs `blend_src_over(src, dest)`. | |
| AddKnownModeBlend(childContext, builder, gatherer, SkBlendMode::kSrcOver); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } // anonymous namespace | |
| template<typename T> | |
| class PrecompileRTEffect : public T { | |
| public: | |
| PrecompileRTEffect(sk_sp<SkRuntimeEffect> effect, | |
| SkSpan<const PrecompileChildOptions> 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<PrecompileChildPtr>& optionSet : fChildOptions) { | |
| numOptions += num_options_in_set(optionSet); | |
| } | |
| return numOptions ? numOptions : 1; | |
| } | |
| void addToKey(const KeyContext& keyContext, | |
| PaintParamsKeyBuilder* builder, | |
| PipelineDataGatherer* gatherer, | |
| int desiredCombination) const override { | |
| SkASSERT(desiredCombination < this->numCombinations()); | |
| SkSpan<const SkRuntimeEffect::Child> childInfo = fEffect->children(); | |
| RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { fEffect }); | |
| for (const std::vector<PrecompileChildPtr>& optionSet : fChildOptions) { | |
| int numOptionsInSet = num_options_in_set(optionSet); | |
| if (desiredCombination < numOptionsInSet) { | |
| add_children_to_key(keyContext, builder, gatherer, desiredCombination, optionSet, | |
| childInfo); | |
| break; | |
| } | |
| desiredCombination -= numOptionsInSet; | |
| } | |
| builder->endBlock(); | |
| } | |
| sk_sp<SkRuntimeEffect> fEffect; | |
| std::vector<std::vector<PrecompileChildPtr>> fChildOptions; | |
| }; | |
| sk_sp<PrecompileShader> MakePrecompileShader( | |
| sk_sp<SkRuntimeEffect> effect, | |
| SkSpan<const PrecompileChildOptions> 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<PrecompileRTEffect<PrecompileShader>>(std::move(effect), childOptions); | |
| } | |
| sk_sp<PrecompileColorFilter> MakePrecompileColorFilter( | |
| sk_sp<SkRuntimeEffect> effect, | |
| SkSpan<const PrecompileChildOptions> 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<PrecompileRTEffect<PrecompileColorFilter>>(std::move(effect), childOptions); | |
| } | |
| sk_sp<PrecompileBlender> MakePrecompileBlender( | |
| sk_sp<SkRuntimeEffect> effect, | |
| SkSpan<const PrecompileChildOptions> 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<PrecompileRTEffect<PrecompileBlender>>(std::move(effect), childOptions); | |
| } | |
| } // namespace skgpu::graphite | |
| //-------------------------------------------------------------------------------------------------- |