src/gpu/graphite/precompile/PrecompileShader.cpp (977 lines of code) (raw):

/* * Copyright 2024 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/gpu/graphite/precompile/PrecompileShader.h" #include "include/core/SkColorSpace.h" #include "include/effects/SkRuntimeEffect.h" #include "include/gpu/graphite/precompile/PrecompileBlender.h" #include "include/gpu/graphite/precompile/PrecompileColorFilter.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkKnownRuntimeEffects.h" #include "src/gpu/Blend.h" #include "src/gpu/graphite/BuiltInCodeSnippetID.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/PrecompileInternal.h" #include "src/gpu/graphite/ReadSwizzle.h" #include "src/gpu/graphite/RecorderPriv.h" #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h" #include "src/gpu/graphite/precompile/PrecompileBasePriv.h" #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h" #include "src/gpu/graphite/precompile/PrecompileImageShader.h" #include "src/gpu/graphite/precompile/PrecompileShaderPriv.h" #include "src/gpu/graphite/precompile/PrecompileShadersPriv.h" #include "src/shaders/gradients/SkLinearGradient.h" #if defined(SK_DEBUG) #include "src/base/SkMathPriv.h" #endif namespace skgpu::graphite { SK_MAKE_BITMASK_OPS(PrecompileShaders::ImageShaderFlags) SK_MAKE_BITMASK_OPS(PrecompileShaders::YUVImageShaderFlags) using PrecompileShaders::GradientShaderFlags; using PrecompileShaders::ImageShaderFlags; using PrecompileShaders::YUVImageShaderFlags; PrecompileShader::~PrecompileShader() = default; sk_sp<PrecompileShader> PrecompileShader::makeWithColorFilter( sk_sp<PrecompileColorFilter> cf) const { if (!cf) { return sk_ref_sp(this); } return PrecompileShaders::ColorFilter({ sk_ref_sp(this) }, { std::move(cf) }); } sk_sp<PrecompileShader> PrecompileShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> cs) const { if (!cs) { return sk_ref_sp(this); } return PrecompileShaders::WorkingColorSpace({ sk_ref_sp(this) }, { std::move(cs) }); } //-------------------------------------------------------------------------------------------------- class PrecompileEmptyShader final : public PrecompileShader { private: void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination == 0); // The empty shader only ever has one combination builder->addBlock(BuiltInCodeSnippetID::kPriorOutput); } }; sk_sp<PrecompileShader> PrecompileShaders::Empty() { return sk_make_sp<PrecompileEmptyShader>(); } //-------------------------------------------------------------------------------------------------- class PrecompileColorShader final : public PrecompileShader { private: bool isConstant(int desiredCombination) const override { SkASSERT(desiredCombination == 0); // The color shader only ever has one combination return true; } 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>(); } // The colorSpace is safe to ignore - it is just applied to the color and doesn't modify the // generated program. sk_sp<PrecompileShader> PrecompileShaders::Color(sk_sp<SkColorSpace>) { return sk_make_sp<PrecompileColorShader>(); } //-------------------------------------------------------------------------------------------------- class PrecompileBlendShader final : public PrecompileShader { public: PrecompileBlendShader(PrecompileBlenderList&& blenders, SkSpan<const sk_sp<PrecompileShader>> dsts, SkSpan<const sk_sp<PrecompileShader>> srcs) : fBlenderOptions(std::move(blenders)) , fDstOptions(dsts.begin(), dsts.end()) , fSrcOptions(srcs.begin(), srcs.end()) { fNumDstCombos = 0; for (const auto& d : fDstOptions) { fNumDstCombos += d->priv().numCombinations(); } fNumSrcCombos = 0; for (const auto& s : fSrcOptions) { fNumSrcCombos += s->priv().numCombinations(); } } private: int numChildCombinations() const override { return fBlenderOptions.numCombinations() * 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 < fBlenderOptions.numCombinations()); auto [blender, blenderCombination] = fBlenderOptions.selectOption(desiredBlendCombination); if (blender->priv().asBlendMode()) { // Coefficient and HSLC blends, and other fixed SkBlendMode blenders use the // BlendCompose block to organize the children. BlendComposeBlock::BeginBlock(keyContext, builder, gatherer); } else { // Runtime blenders are wrapped in the kBlend runtime shader, although functionally // it is identical to the BlendCompose snippet. const SkRuntimeEffect* blendEffect = GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kBlend); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(blendEffect) }); } AddToKey<PrecompileShader>(keyContext, builder, gatherer, fSrcOptions, desiredSrcCombination); AddToKey<PrecompileShader>(keyContext, builder, gatherer, fDstOptions, desiredDstCombination); if (blender->priv().asBlendMode()) { SkASSERT(blenderCombination == 0); AddBlendMode(keyContext, builder, gatherer, *blender->priv().asBlendMode()); } else { blender->priv().addToKey(keyContext, builder, gatherer, blenderCombination); } builder->endBlock(); // BlendComposeBlock or RuntimeEffectBlock } PrecompileBlenderList fBlenderOptions; std::vector<sk_sp<PrecompileShader>> fDstOptions; std::vector<sk_sp<PrecompileShader>> fSrcOptions; int fNumDstCombos; int fNumSrcCombos; }; sk_sp<PrecompileShader> PrecompileShaders::Blend( SkSpan<const sk_sp<PrecompileBlender>> blenders, SkSpan<const sk_sp<PrecompileShader>> dsts, SkSpan<const sk_sp<PrecompileShader>> srcs) { return sk_make_sp<PrecompileBlendShader>(PrecompileBlenderList(blenders), dsts, srcs); } sk_sp<PrecompileShader> PrecompileShaders::Blend( SkSpan<const SkBlendMode> blendModes, SkSpan<const sk_sp<PrecompileShader>> dsts, SkSpan<const sk_sp<PrecompileShader>> srcs) { return sk_make_sp<PrecompileBlendShader>(PrecompileBlenderList(blendModes), dsts, srcs); } //-------------------------------------------------------------------------------------------------- class PrecompileCoordClampShader final : public PrecompileShader { public: PrecompileCoordClampShader(SkSpan<const sk_sp<PrecompileShader>> shaders) : fShaders(shaders.begin(), shaders.end()) { fNumShaderCombos = 0; for (const auto& s : fShaders) { fNumShaderCombos += s->priv().numCombinations(); } } private: int numChildCombinations() const override { return fNumShaderCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < fNumShaderCombos); constexpr SkRect kIgnored { 0, 0, 256, 256 }; // ignored bc we're precompiling // TODO: update CoordClampShaderBlock so this is optional CoordClampShaderBlock::CoordClampData data(kIgnored); CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data); AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders, desiredCombination); builder->endBlock(); } std::vector<sk_sp<PrecompileShader>> fShaders; int fNumShaderCombos; }; sk_sp<PrecompileShader> PrecompileShaders::CoordClamp(SkSpan<const sk_sp<PrecompileShader>> input) { return sk_make_sp<PrecompileCoordClampShader>(input); } //-------------------------------------------------------------------------------------------------- PrecompileImageShader::PrecompileImageShader(SkEnumBitMask<ImageShaderFlags> flags, SkSpan<const SkColorInfo> colorInfos, SkSpan<const SkTileMode> tileModes, bool raw) : fNumExtraSamplingTilingCombos((flags & ImageShaderFlags::kCubicSampling) ? kExtraNumSamplingTilingCombos : 1) // Just kHWTiled , fColorInfos(!colorInfos.empty() ? std::vector<SkColorInfo>(colorInfos.begin(), colorInfos.end()) : raw ? RawImageDefaultColorInfos() : (flags & ImageShaderFlags::kIncludeAlphaOnly) ? DefaultColorInfos() : NonAlphaOnlyDefaultColorInfos()) , fTileModes(std::vector<SkTileMode>(tileModes.begin(), tileModes.end())) , fUseDstColorSpace(!colorInfos.empty()) , fRaw(raw) {} void PrecompileImageShader::setImmutableSamplerInfo(const ImmutableSamplerInfo& samplerInfo) { fImmutableSamplerInfo = samplerInfo; } int PrecompileImageShader::numIntrinsicCombinations() const { // TODO(b/400682634) If color infos were provided by the client, and we're using the // destination color space to determine what color space transform shaders to use, we can // end up generating duplicate shaders, and the actual number of unique shaders generated // will be less than the number calculated here. return fColorInfos.size() * (fTileModes.size() + fNumExtraSamplingTilingCombos); } void PrecompileImageShader::addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const { SkASSERT(this->numChildCombinations() == 1); SkASSERT(desiredCombination < this->numIntrinsicCombinations()); const int numSamplingTilingCombos = fTileModes.size() + fNumExtraSamplingTilingCombos; const int desiredSamplingTilingCombo = desiredCombination % numSamplingTilingCombos; desiredCombination /= numSamplingTilingCombos; const int desiredColorInfo = desiredCombination; SkASSERT(desiredColorInfo < static_cast<int>(fColorInfos.size())); static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell()); // This is kLinear to work around b/417429187 static constexpr SkSamplingOptions kDefaultSampling(SkFilterMode::kLinear); // 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 kShaderTileableSize = SkISize::Make(2, 2); const int numTileModes = fTileModes.size(); const SkTileMode tileMode = (desiredSamplingTilingCombo < numTileModes) ? fTileModes[desiredSamplingTilingCombo] : SkTileMode::kClamp; const SkISize imgSize = (desiredSamplingTilingCombo >= numTileModes && desiredSamplingTilingCombo - numTileModes == kHWTiled) ? kHWTileableSize : kShaderTileableSize; const SkSamplingOptions sampling = (desiredSamplingTilingCombo >= numTileModes && desiredSamplingTilingCombo - numTileModes == kCubicSampled) ? kDefaultCubicSampling : kDefaultSampling; const ImageShaderBlock::ImageData imgData(sampling, tileMode, tileMode, imgSize, kSubset, fImmutableSamplerInfo); const SkColorInfo& colorInfo = fColorInfos[desiredColorInfo]; const bool alphaOnly = SkColorTypeIsAlphaOnly(colorInfo.colorType()); const Caps* caps = keyContext.caps(); Swizzle readSwizzle = caps->getReadSwizzle( colorInfo.colorType(), caps->getDefaultSampledTextureInfo( colorInfo.colorType(), Mipmapped::kNo, Protected::kNo, Renderable::kNo)); if (alphaOnly) { readSwizzle = Swizzle::Concat(readSwizzle, Swizzle("000a")); } ColorSpaceTransformBlock::ColorSpaceTransformData colorXformData( SwizzleClassToReadEnum(readSwizzle)); if (!fRaw) { const SkColorSpace* dstColorSpace = fUseDstColorSpace ? keyContext.dstColorInfo().colorSpace() : sk_srgb_singleton(); colorXformData.fSteps = SkColorSpaceXformSteps( colorInfo.colorSpace(), colorInfo.alphaType(), dstColorSpace, colorInfo.alphaType()); if (alphaOnly) { Blend(keyContext, builder, gatherer, /* addBlendToKey= */ [&] () -> void { AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kDstIn); }, /* addSrcToKey= */ [&] () -> void { Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData); }, /* addOuterToKey= */ [&]() -> void { ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, colorXformData); }); }, /* addDstToKey= */ [&]() -> void { RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer); }); return; } } Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData); }, /* addOuterToKey= */ [&]() -> void { ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, colorXformData); }); } sk_sp<PrecompileShader> PrecompileShaders::Image(ImageShaderFlags shaderFlags, SkSpan<const SkColorInfo> colorInfos, SkSpan<const SkTileMode> tileModes) { return PrecompileShaders::LocalMatrix( { sk_make_sp<PrecompileImageShader>(shaderFlags, colorInfos, tileModes, /* raw= */false) }); } sk_sp<PrecompileShader> PrecompileShaders::Image(SkSpan<const SkColorInfo> colorInfos, SkSpan<const SkTileMode> tileModes) { return Image(ImageShaderFlags::kAll, colorInfos, tileModes); } sk_sp<PrecompileShader> PrecompileShaders::RawImage(ImageShaderFlags shaderFlags, SkSpan<const SkColorInfo> colorInfos, SkSpan<const SkTileMode> tileModes) { SkEnumBitMask<ImageShaderFlags> newFlags = ~ImageShaderFlags::kCubicSampling & shaderFlags; return PrecompileShaders::LocalMatrix( { sk_make_sp<PrecompileImageShader>(newFlags, colorInfos, tileModes, /* raw= */true) }); } //-------------------------------------------------------------------------------------------------- class PrecompileYUVImageShader : public PrecompileShader { public: PrecompileYUVImageShader(SkEnumBitMask<YUVImageShaderFlags> shaderFlags, SkSpan<const SkColorInfo> colorInfos) : fColorInfos(!colorInfos.empty() ? std::vector<SkColorInfo>(colorInfos.begin(), colorInfos.end()) : PrecompileImageShader::NonAlphaOnlyDefaultColorInfos()) , fUseDstColorSpace(!colorInfos.empty()) { this->setupTilingModes(shaderFlags); } private: // There are 4 possible tiling modes: // non-cubic shader tiling // HW tiling w/o swizzle // HW tiling w/ swizzle // cubic shader tiling -- can be omitted inline static constexpr int kMaxTilingModes = 4; inline static constexpr int kShaderTiled = 0; inline static constexpr int kHWTiledNoSwizzle = 1; inline static constexpr int kHWTiledWithSwizzle = 2; inline static constexpr int kCubicShaderTiled = 3; void setupTilingModes(SkEnumBitMask<YUVImageShaderFlags> flags) { fNumTilingModes = 0; if (flags & YUVImageShaderFlags::kHardwareSamplingNoSwizzle) { fTilingModes[fNumTilingModes++] = kHWTiledNoSwizzle; } if (flags & YUVImageShaderFlags::kHardwareSampling) { fTilingModes[fNumTilingModes++] = kHWTiledWithSwizzle; } if (flags & YUVImageShaderFlags::kShaderBasedSampling) { fTilingModes[fNumTilingModes++] = kShaderTiled; } if (flags & YUVImageShaderFlags::kCubicSampling) { fTilingModes[fNumTilingModes++] = kCubicShaderTiled; } SkASSERT(fNumTilingModes == SkPopCount(flags.value())); SkASSERT(fNumTilingModes <= kMaxTilingModes); } int numIntrinsicCombinations() const override { return fNumTilingModes * fColorInfos.size(); } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numIntrinsicCombinations()); int desiredTiling = desiredCombination % fNumTilingModes; desiredCombination /= fNumTilingModes; int desiredColorInfo = desiredCombination; SkASSERT(desiredColorInfo < static_cast<int>(fColorInfos.size())); static constexpr SkSamplingOptions kDefaultCubicSampling(SkCubicResampler::Mitchell()); static constexpr SkSamplingOptions kDefaultSampling; YUVImageShaderBlock::ImageData imgData( fTilingModes[desiredTiling] == kCubicShaderTiled ? kDefaultCubicSampling : kDefaultSampling, SkTileMode::kClamp, fTilingModes[desiredTiling] == kShaderTiled ? SkTileMode::kRepeat : SkTileMode::kClamp, /* imgSize= */ { 1, 1 }, /* subset= */ fTilingModes[desiredTiling] == kShaderTiled ? SkRect::MakeEmpty() : SkRect::MakeWH(1, 1)); static constexpr SkV4 kRedChannel{ 1.f, 0.f, 0.f, 0.f }; imgData.fChannelSelect[0] = kRedChannel; imgData.fChannelSelect[1] = kRedChannel; if (fTilingModes[desiredTiling] == kHWTiledNoSwizzle) { imgData.fChannelSelect[2] = kRedChannel; } else { // Having a non-red channel selector forces a swizzle imgData.fChannelSelect[2] = { 0.f, 1.f, 0.f, 0.f}; } imgData.fChannelSelect[3] = kRedChannel; imgData.fYUVtoRGBMatrix.setAll(1, 0, 0, 0, 1, 0, 0, 0, 0); imgData.fYUVtoRGBTranslate = { 0, 0, 0 }; static sk_sp<SkColorSpace> srgbSpinColorSpace = sk_srgb_singleton()->makeColorSpin(); const SkColorInfo& colorInfo = fColorInfos[desiredColorInfo]; const Caps* caps = keyContext.caps(); Swizzle readSwizzle = caps->getReadSwizzle( colorInfo.colorType(), caps->getDefaultSampledTextureInfo( colorInfo.colorType(), Mipmapped::kNo, Protected::kNo, Renderable::kNo)); ColorSpaceTransformBlock::ColorSpaceTransformData colorXformData( SwizzleClassToReadEnum(readSwizzle)); const SkColorSpace* dstColorSpace = fUseDstColorSpace ? keyContext.dstColorInfo().colorSpace() : sk_srgb_singleton(); colorXformData.fSteps = SkColorSpaceXformSteps( colorInfo.colorSpace(), colorInfo.alphaType(), dstColorSpace, colorInfo.alphaType()); Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { YUVImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData); }, /* addOuterToKey= */ [&]() -> void { ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, colorXformData); }); } const std::vector<SkColorInfo> fColorInfos; // If true, use the destination color space from the KeyContext provided to addToKey. // This is true if and only if the client has provided a list of color infos. Otherwise, we // always use an sRGB destination per the default SkColorInfo lists defined in // PrecompileImageShader::DefaultColorInfos. const bool fUseDstColorSpace; int fNumTilingModes; int fTilingModes[kMaxTilingModes]; }; sk_sp<PrecompileShader> PrecompileShaders::YUVImage(YUVImageShaderFlags shaderFlags, SkSpan<const SkColorInfo> colorInfos) { return PrecompileShaders::LocalMatrix( { sk_make_sp<PrecompileYUVImageShader>(shaderFlags, colorInfos) }); } //-------------------------------------------------------------------------------------------------- class PrecompilePerlinNoiseShader final : public PrecompileShader { public: PrecompilePerlinNoiseShader() {} private: void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination == 0); // The Perlin noise shader only ever has one combination // TODO: update PerlinNoiseShaderBlock so the NoiseData is optional static const PerlinNoiseShaderBlock::PerlinNoiseData kIgnoredNoiseData( PerlinNoiseShaderBlock::Type::kFractalNoise, { 0.0f, 0.0f }, 2, {1, 1}); PerlinNoiseShaderBlock::AddBlock(keyContext, builder, gatherer, kIgnoredNoiseData); } }; sk_sp<PrecompileShader> PrecompileShaders::MakeFractalNoise() { return sk_make_sp<PrecompilePerlinNoiseShader>(); } sk_sp<PrecompileShader> PrecompileShaders::MakeTurbulence() { return sk_make_sp<PrecompilePerlinNoiseShader>(); } namespace { sk_sp<SkColorSpace> get_gradient_intermediate_cs(SkColorSpace* dstColorSpace, SkGradientShader::Interpolation interpolation) { // Any gradient shader will do, as long as it has the correct interpolation settings. constexpr SkPoint pts[2] = {{0.f, 0.f}, {1.f, 0.f}}; constexpr SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite}; constexpr float pos[2] = {0.f, 1.f}; SkLinearGradient shader(pts, {colors, nullptr, pos, 2, SkTileMode::kClamp, interpolation}); SkColor4fXformer xformedColors(&shader, dstColorSpace); return xformedColors.fIntermediateColorSpace; } } // anonymous namespace //-------------------------------------------------------------------------------------------------- class PrecompileGradientShader final : public PrecompileShader { public: PrecompileGradientShader(SkShaderBase::GradientType type, SkEnumBitMask<GradientShaderFlags> flags, const SkGradientShader::Interpolation& interpolation) : fType(type) , fInterpolation(interpolation) { this->setupStopVariants(flags); } private: /* * The gradients can have up to three specializations based on the number of stops. */ inline static constexpr int kMaxStopVariants = 3; void setupStopVariants(SkEnumBitMask<GradientShaderFlags> flags) { fNumStopVariants = 0; if (flags & GradientShaderFlags::kSmall) { fStopVariants[fNumStopVariants++] = 4; } if (flags & GradientShaderFlags::kMedium) { fStopVariants[fNumStopVariants++] = 8; } if (flags & GradientShaderFlags::kLarge) { fStopVariants[fNumStopVariants++] = GradientShaderBlocks::GradientData::kNumInternalStorageStops+1; } SkASSERT(fNumStopVariants == SkPopCount(flags.value())); SkASSERT(fNumStopVariants <= kMaxStopVariants); } int numIntrinsicCombinations() const override { return fNumStopVariants; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(this->numChildCombinations() == 1); SkASSERT(desiredCombination < fNumStopVariants); bool useStorageBuffer = keyContext.caps()->gradientBufferSupport(); GradientShaderBlocks::GradientData gradData(fType, fStopVariants[desiredCombination], useStorageBuffer); // The logic for setting up color spaces here should match that in the "add_gradient_to_key" // functions from src/gpu/graphite/KeyHelpers.cpp. sk_sp<SkColorSpace> intermediateCS = get_gradient_intermediate_cs( keyContext.dstColorInfo().colorSpace(), fInterpolation); const SkColorSpace* dstCS = keyContext.dstColorInfo().colorSpace() ? keyContext.dstColorInfo().colorSpace() : sk_srgb_singleton(); ColorSpaceTransformBlock::ColorSpaceTransformData csData( intermediateCS.get(), kPremul_SkAlphaType, dstCS, kPremul_SkAlphaType); Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData); }, /* addOuterToKey= */ [&]() -> void { ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData); }); } const SkShaderBase::GradientType fType; const SkGradientShader::Interpolation fInterpolation; int fNumStopVariants = 0; int fStopVariants[kMaxStopVariants]; }; sk_sp<PrecompileShader> PrecompileShaders::LinearGradient( GradientShaderFlags flags, SkGradientShader::Interpolation interpolation) { sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>( SkShaderBase::GradientType::kLinear, flags, interpolation); return PrecompileShaders::LocalMatrix({ std::move(s) }); } sk_sp<PrecompileShader> PrecompileShaders::RadialGradient( GradientShaderFlags flags, SkGradientShader::Interpolation interpolation) { sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>( SkShaderBase::GradientType::kRadial, flags, interpolation); return PrecompileShaders::LocalMatrix({ std::move(s) }); } sk_sp<PrecompileShader> PrecompileShaders::SweepGradient( GradientShaderFlags flags, SkGradientShader::Interpolation interpolation) { sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>( SkShaderBase::GradientType::kSweep, flags, interpolation); return PrecompileShaders::LocalMatrix({ std::move(s) }); } sk_sp<PrecompileShader> PrecompileShaders::TwoPointConicalGradient( GradientShaderFlags flags, SkGradientShader::Interpolation interpolation) { sk_sp<PrecompileShader> s = sk_make_sp<PrecompileGradientShader>( SkShaderBase::GradientType::kConical, flags, interpolation); return PrecompileShaders::LocalMatrix({ std::move(s) }); } //-------------------------------------------------------------------------------------------------- // The PictureShader ultimately turns into an SkImageShader optionally wrapped in a // LocalMatrixShader. // Note that this means each precompile PictureShader will add 24 combinations: // 2 (pictureshader LM) x 12 (imageShader variations) sk_sp<PrecompileShader> PrecompileShaders::Picture() { // Note: We don't need to consider the PrecompileYUVImageShader since the image // being drawn was created internally by Skia (as non-YUV). return PrecompileShadersPriv::LocalMatrixBothVariants({ PrecompileShaders::Image() }); } sk_sp<PrecompileShader> PrecompileShadersPriv::Picture(bool withLM) { sk_sp<PrecompileShader> s = PrecompileShaders::Image(); if (withLM) { return PrecompileShaders::LocalMatrix({ std::move(s) }); } return s; } //-------------------------------------------------------------------------------------------------- // In the main Skia API the SkLocalMatrixShader is optimized away when the LM is the identity // or omitted. The PrecompileLocalMatrixShader captures this by adding two intrinsic options. // One with the LMShader wrapping the child and one without the LMShader. class PrecompileLocalMatrixShader final : public PrecompileShader { public: enum class Flags { kNone = 0b00, kIsPerspective = 0b01, kIncludeWithOutVariant = 0b10, }; PrecompileLocalMatrixShader(SkSpan<const sk_sp<PrecompileShader>> wrapped, SkEnumBitMask<Flags> flags = Flags::kNone) : fWrapped(wrapped.begin(), wrapped.end()) , fFlags(flags) { fNumWrappedCombos = 0; for (const auto& s : fWrapped) { fNumWrappedCombos += s->priv().numCombinations(); } } bool isConstant(int desiredCombination) const override { SkASSERT(desiredCombination < this->numCombinations()); /* * Regardless of whether the LocalMatrixShader elides itself or not, we always want * the Constant-ness of the wrapped shader. */ int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations; SkASSERT(desiredWrappedCombination < fNumWrappedCombos); std::pair<sk_sp<PrecompileShader>, int> wrapped = PrecompileBase::SelectOption(SkSpan(fWrapped), desiredWrappedCombination); if (wrapped.first) { return wrapped.first->priv().isConstant(wrapped.second); } return false; } SkSpan<const sk_sp<PrecompileShader>> getWrapped() const { return fWrapped; } SkEnumBitMask<Flags> getFlags() const { return fFlags; } private: // The LocalMatrixShader has two potential variants: with and without the LocalMatrixShader // In the "with" variant, the kIsPerspective flag will determine if the shader performs // the perspective division or not. inline static constexpr int kNumIntrinsicCombinations = 2; inline static constexpr int kWithLocalMatrix = 1; inline static constexpr int kWithoutLocalMatrix = 0; bool isALocalMatrixShader() const override { return true; } int numIntrinsicCombinations() const override { if (!(fFlags & Flags::kIncludeWithOutVariant)) { return 1; // just kWithLocalMatrix } return kNumIntrinsicCombinations; } int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numCombinations()); int desiredLMCombination, desiredWrappedCombination; if (!(fFlags & Flags::kIncludeWithOutVariant)) { desiredLMCombination = kWithLocalMatrix; desiredWrappedCombination = desiredCombination; } else { desiredLMCombination = desiredCombination % kNumIntrinsicCombinations; desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations; } SkASSERT(desiredWrappedCombination < fNumWrappedCombos); if (desiredLMCombination == kWithLocalMatrix) { SkMatrix matrix = SkMatrix::I(); if (fFlags & Flags::kIsPerspective) { matrix.setPerspX(0.1f); } LocalMatrixShaderBlock::LMShaderData lmShaderData(matrix); LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, matrix); } AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped, desiredWrappedCombination); if (desiredLMCombination == kWithLocalMatrix) { builder->endBlock(); } } std::vector<sk_sp<PrecompileShader>> fWrapped; int fNumWrappedCombos; SkEnumBitMask<Flags> fFlags; }; sk_sp<PrecompileShader> PrecompileShaders::LocalMatrix( SkSpan<const sk_sp<PrecompileShader>> wrapped, bool isPerspective) { return sk_make_sp<PrecompileLocalMatrixShader>( std::move(wrapped), isPerspective ? PrecompileLocalMatrixShader::Flags::kIsPerspective : PrecompileLocalMatrixShader::Flags::kNone); } sk_sp<PrecompileShader> PrecompileShadersPriv::LocalMatrixBothVariants( SkSpan<const sk_sp<PrecompileShader>> wrapped) { return sk_make_sp<PrecompileLocalMatrixShader>( std::move(wrapped), PrecompileLocalMatrixShader::Flags::kIncludeWithOutVariant); } sk_sp<PrecompileShader> PrecompileShader::makeWithLocalMatrix(bool isPerspective) const { if (this->priv().isALocalMatrixShader()) { // SkShader::makeWithLocalMatrix collapses chains of localMatrix shaders so we need to // follow suit here, folding in any new perspective flag if needed. auto thisAsLMShader = static_cast<const PrecompileLocalMatrixShader*>(this); if (isPerspective && !(thisAsLMShader->getFlags() & PrecompileLocalMatrixShader::Flags::kIsPerspective)) { return sk_make_sp<PrecompileLocalMatrixShader>( thisAsLMShader->getWrapped(), thisAsLMShader->getFlags() | PrecompileLocalMatrixShader::Flags::kIsPerspective); } return sk_ref_sp(this); } return PrecompileShaders::LocalMatrix({ sk_ref_sp(this) }, isPerspective); } //-------------------------------------------------------------------------------------------------- class PrecompileColorFilterShader final : public PrecompileShader { public: PrecompileColorFilterShader(SkSpan<const sk_sp<PrecompileShader>> shaders, SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters) : fShaders(shaders.begin(), shaders.end()) , fColorFilters(colorFilters.begin(), colorFilters.end()) { fNumShaderCombos = 0; for (const auto& s : fShaders) { fNumShaderCombos += s->priv().numCombinations(); } fNumColorFilterCombos = 0; for (const auto& cf : fColorFilters) { fNumColorFilterCombos += cf->priv().numCombinations(); } } private: int numChildCombinations() const override { return fNumShaderCombos * fNumColorFilterCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numCombinations()); int desiredShaderCombination = desiredCombination % fNumShaderCombos; int desiredColorFilterCombination = desiredCombination / fNumShaderCombos; SkASSERT(desiredColorFilterCombination < fNumColorFilterCombos); Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders, desiredShaderCombination); }, /* addOuterToKey= */ [&]() -> void { AddToKey<PrecompileColorFilter>(keyContext, builder, gatherer, fColorFilters, desiredColorFilterCombination); }); } std::vector<sk_sp<PrecompileShader>> fShaders; std::vector<sk_sp<PrecompileColorFilter>> fColorFilters; int fNumShaderCombos; int fNumColorFilterCombos; }; sk_sp<PrecompileShader> PrecompileShaders::ColorFilter( SkSpan<const sk_sp<PrecompileShader>> shaders, SkSpan<const sk_sp<PrecompileColorFilter>> colorFilters) { return sk_make_sp<PrecompileColorFilterShader>(std::move(shaders), std::move(colorFilters)); } //-------------------------------------------------------------------------------------------------- class PrecompileWorkingColorSpaceShader final : public PrecompileShader { public: PrecompileWorkingColorSpaceShader(SkSpan<const sk_sp<PrecompileShader>> shaders, SkSpan<const sk_sp<SkColorSpace>> colorSpaces) : fShaders(shaders.begin(), shaders.end()) , fColorSpaces(colorSpaces.begin(), colorSpaces.end()) { fNumShaderCombos = 0; for (const auto& s : fShaders) { fNumShaderCombos += s->priv().numCombinations(); } } private: int numChildCombinations() const override { return fNumShaderCombos * fColorSpaces.size(); } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numCombinations()); int desiredShaderCombination = desiredCombination % fNumShaderCombos; int desiredColorSpaceCombination = desiredCombination / fNumShaderCombos; SkASSERT(desiredColorSpaceCombination < (int) fColorSpaces.size()); const SkColorInfo& dstInfo = keyContext.dstColorInfo(); const SkAlphaType dstAT = dstInfo.alphaType(); sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace(); if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); } sk_sp<SkColorSpace> workingCS = fColorSpaces[desiredColorSpaceCombination]; SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS); KeyContextWithColorInfo workingContext(keyContext, workingInfo); Compose(keyContext, builder, gatherer, /* addInnerToKey= */ [&]() -> void { AddToKey<PrecompileShader>(keyContext, builder, gatherer, fShaders, desiredShaderCombination); }, /* addOuterToKey= */ [&]() -> void { ColorSpaceTransformBlock::ColorSpaceTransformData data( workingCS.get(), dstAT, dstCS.get(), dstAT); ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data); }); } std::vector<sk_sp<PrecompileShader>> fShaders; std::vector<sk_sp<SkColorSpace>> fColorSpaces; int fNumShaderCombos; }; sk_sp<PrecompileShader> PrecompileShaders::WorkingColorSpace( SkSpan<const sk_sp<PrecompileShader>> shaders, SkSpan<const sk_sp<SkColorSpace>> colorSpaces) { return sk_make_sp<PrecompileWorkingColorSpaceShader>(std::move(shaders), std::move(colorSpaces)); } //-------------------------------------------------------------------------------------------------- // In Graphite this acts as a non-elidable LocalMatrixShader class PrecompileCTMShader final : public PrecompileShader { public: PrecompileCTMShader(SkSpan<const sk_sp<PrecompileShader>> wrapped) : fWrapped(wrapped.begin(), wrapped.end()) { fNumWrappedCombos = 0; for (const auto& s : fWrapped) { fNumWrappedCombos += s->priv().numCombinations(); } } bool isConstant(int desiredCombination) const override { SkASSERT(desiredCombination < fNumWrappedCombos); std::pair<sk_sp<PrecompileShader>, int> wrapped = PrecompileBase::SelectOption(SkSpan(fWrapped), desiredCombination); if (wrapped.first) { return wrapped.first->priv().isConstant(wrapped.second); } return false; } private: int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < fNumWrappedCombos); LocalMatrixShaderBlock::LMShaderData kIgnoredLMShaderData(SkMatrix::I()); LocalMatrixShaderBlock::BeginBlock(keyContext, builder, gatherer, kIgnoredLMShaderData); AddToKey<PrecompileShader>(keyContext, builder, gatherer, fWrapped, desiredCombination); builder->endBlock(); } std::vector<sk_sp<PrecompileShader>> fWrapped; int fNumWrappedCombos; }; sk_sp<PrecompileShader> PrecompileShadersPriv::CTM(SkSpan<const sk_sp<PrecompileShader>> wrapped) { return sk_make_sp<PrecompileCTMShader>(std::move(wrapped)); } //-------------------------------------------------------------------------------------------------- class PrecompileBlurShader final : public PrecompileShader { public: PrecompileBlurShader(sk_sp<PrecompileShader> wrapped) : fWrapped(std::move(wrapped)) { fNumWrappedCombos = fWrapped->priv().numCombinations(); } private: // 6 known 1D blur effects + 6 known 2D blur effects inline static constexpr int kNumIntrinsicCombinations = 12; int numIntrinsicCombinations() const override { return kNumIntrinsicCombinations; } int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numCombinations()); using namespace SkKnownRuntimeEffects; int desiredBlurCombination = desiredCombination % kNumIntrinsicCombinations; int desiredWrappedCombination = desiredCombination / kNumIntrinsicCombinations; SkASSERT(desiredWrappedCombination < fNumWrappedCombos); static const StableKey kIDs[kNumIntrinsicCombinations] = { StableKey::k1DBlur4, StableKey::k1DBlur8, StableKey::k1DBlur12, StableKey::k1DBlur16, StableKey::k1DBlur20, StableKey::k1DBlur28, StableKey::k2DBlur4, StableKey::k2DBlur8, StableKey::k2DBlur12, StableKey::k2DBlur16, StableKey::k2DBlur20, StableKey::k2DBlur28, }; const SkRuntimeEffect* effect = GetKnownRuntimeEffect(kIDs[desiredBlurCombination]); SkASSERT(effect->children().size() == 1); KeyContextForRuntimeEffect childContext(keyContext, effect, /*child=*/0); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) }); fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination); builder->endBlock(); } sk_sp<PrecompileShader> fWrapped; int fNumWrappedCombos; }; sk_sp<PrecompileShader> PrecompileShadersPriv::Blur(sk_sp<PrecompileShader> wrapped) { return sk_make_sp<PrecompileBlurShader>(std::move(wrapped)); } //-------------------------------------------------------------------------------------------------- class PrecompileMatrixConvolutionShader final : public PrecompileShader { public: PrecompileMatrixConvolutionShader(sk_sp<PrecompileShader> wrapped) : fWrapped(std::move(wrapped)) { fNumWrappedCombos = fWrapped->priv().numCombinations(); // When the matrix convolution ImageFilter uses a texture we know it will only ever // be SkFilterMode::kNearest and SkTileMode::kClamp. // TODO: add a PrecompileImageShaderFlags to further limit the raw image shader // combinations. Right now we're getting two combinations for the raw shader // (sk_image_shader and sk_hw_image_shader). fRawImageShader = PrecompileShaders::RawImage(); fNumRawImageShaderCombos = fRawImageShader->priv().numCombinations(); } private: int numIntrinsicCombinations() const override { // The uniform version only has one option but the two texture-based versions will // have as many combinations as the raw image shader. return 1 + 2 * fNumRawImageShaderCombos; } int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { int desiredTextureCombination = 0; const int desiredWrappedCombination = desiredCombination % fNumWrappedCombos; int remainingCombinations = desiredCombination / fNumWrappedCombos; SkKnownRuntimeEffects::StableKey stableKey = SkKnownRuntimeEffects::StableKey::kInvalid; if (remainingCombinations == 0) { stableKey = SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms; } else { static constexpr SkKnownRuntimeEffects::StableKey kTextureBasedStableKeys[] = { SkKnownRuntimeEffects::StableKey::kMatrixConvTexSm, SkKnownRuntimeEffects::StableKey::kMatrixConvTexLg, }; --remainingCombinations; stableKey = kTextureBasedStableKeys[remainingCombinations % 2]; desiredTextureCombination = remainingCombinations / 2; SkASSERT(desiredTextureCombination < fNumRawImageShaderCombos); } const SkRuntimeEffect* effect = GetKnownRuntimeEffect(stableKey); KeyContextForRuntimeEffect childContext(keyContext, effect, /*child=*/0); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) }); fWrapped->priv().addToKey(childContext, builder, gatherer, desiredWrappedCombination); if (stableKey != SkKnownRuntimeEffects::StableKey::kMatrixConvUniforms) { SkASSERT(effect->children().size() == 2); KeyContextForRuntimeEffect kernelContext(keyContext, effect, /*child=*/1); fRawImageShader->priv().addToKey(kernelContext, builder, gatherer, desiredTextureCombination); } else { SkASSERT(effect->children().size() == 1); } builder->endBlock(); } sk_sp<PrecompileShader> fWrapped; int fNumWrappedCombos; sk_sp<PrecompileShader> fRawImageShader; int fNumRawImageShaderCombos; }; sk_sp<PrecompileShader> PrecompileShadersPriv::MatrixConvolution( sk_sp<skgpu::graphite::PrecompileShader> wrapped) { return sk_make_sp<PrecompileMatrixConvolutionShader>(std::move(wrapped)); } //-------------------------------------------------------------------------------------------------- class PrecompileMorphologyShader final : public PrecompileShader { public: PrecompileMorphologyShader(sk_sp<PrecompileShader> wrapped, SkKnownRuntimeEffects::StableKey stableKey) : fWrapped(std::move(wrapped)) , fStableKey(stableKey) { fNumWrappedCombos = fWrapped->priv().numCombinations(); SkASSERT(stableKey == SkKnownRuntimeEffects::StableKey::kLinearMorphology || stableKey == SkKnownRuntimeEffects::StableKey::kSparseMorphology); } private: int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < fNumWrappedCombos); const SkRuntimeEffect* effect = GetKnownRuntimeEffect(fStableKey); SkASSERT(effect->children().size() == 1); KeyContextForRuntimeEffect childContext(keyContext, effect, /*child=*/0); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) }); fWrapped->priv().addToKey(childContext, builder, gatherer, desiredCombination); builder->endBlock(); } sk_sp<PrecompileShader> fWrapped; int fNumWrappedCombos; SkKnownRuntimeEffects::StableKey fStableKey; }; sk_sp<PrecompileShader> PrecompileShadersPriv::LinearMorphology(sk_sp<PrecompileShader> wrapped) { return sk_make_sp<PrecompileMorphologyShader>( std::move(wrapped), SkKnownRuntimeEffects::StableKey::kLinearMorphology); } sk_sp<PrecompileShader> PrecompileShadersPriv::SparseMorphology(sk_sp<PrecompileShader> wrapped) { return sk_make_sp<PrecompileMorphologyShader>( std::move(wrapped), SkKnownRuntimeEffects::StableKey::kSparseMorphology); } //-------------------------------------------------------------------------------------------------- class PrecompileDisplacementShader final : public PrecompileShader { public: PrecompileDisplacementShader(sk_sp<PrecompileShader> displacement, sk_sp<PrecompileShader> color) : fDisplacement(std::move(displacement)) , fColor(std::move(color)) { fNumDisplacementCombos = fDisplacement->priv().numCombinations(); fNumColorCombos = fColor->priv().numCombinations(); } private: int numChildCombinations() const override { return fNumDisplacementCombos * fNumColorCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < this->numChildCombinations()); const int desiredDisplacementCombination = desiredCombination % fNumDisplacementCombos; const int desiredColorCombination = desiredCombination / fNumDisplacementCombos; SkASSERT(desiredColorCombination < fNumColorCombos); const SkRuntimeEffect* effect = GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kDisplacement); SkASSERT(effect->children().size() == 2); KeyContextForRuntimeEffect displContext(keyContext, effect, /*child=*/0); KeyContextForRuntimeEffect colorContext(keyContext, effect, /*child=*/1); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(effect) }); fDisplacement->priv().addToKey(displContext, builder, gatherer, desiredDisplacementCombination); fColor->priv().addToKey(colorContext, builder, gatherer, desiredColorCombination); builder->endBlock(); } sk_sp<PrecompileShader> fDisplacement; int fNumDisplacementCombos; sk_sp<PrecompileShader> fColor; int fNumColorCombos; }; //-------------------------------------------------------------------------------------------------- sk_sp<PrecompileShader> PrecompileShadersPriv::Displacement(sk_sp<PrecompileShader> displacement, sk_sp<PrecompileShader> color) { return sk_make_sp<PrecompileDisplacementShader>(std::move(displacement), std::move(color)); } //-------------------------------------------------------------------------------------------------- class PrecompileLightingShader final : public PrecompileShader { public: PrecompileLightingShader(sk_sp<PrecompileShader> wrapped) : fWrapped(std::move(wrapped)) { fNumWrappedCombos = fWrapped->priv().numCombinations(); } private: int numChildCombinations() const override { return fNumWrappedCombos; } void addToKey(const KeyContext& keyContext, PaintParamsKeyBuilder* builder, PipelineDataGatherer* gatherer, int desiredCombination) const override { SkASSERT(desiredCombination < fNumWrappedCombos); const SkRuntimeEffect* normalEffect = GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kNormal); const SkRuntimeEffect* lightingEffect = GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLighting); SkASSERT(normalEffect->children().size() == 1 && lightingEffect->children().size() == 1); KeyContextForRuntimeEffect lightingContext(keyContext, lightingEffect, /*child=*/0); KeyContextForRuntimeEffect normalContext(lightingContext, normalEffect, /*child=*/0); RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { sk_ref_sp(lightingEffect) }); RuntimeEffectBlock::BeginBlock(lightingContext, builder, gatherer, { sk_ref_sp(normalEffect) }); fWrapped->priv().addToKey(normalContext, builder, gatherer, desiredCombination); builder->endBlock(); builder->endBlock(); } sk_sp<PrecompileShader> fWrapped; int fNumWrappedCombos; }; sk_sp<PrecompileShader> PrecompileShadersPriv::Lighting(sk_sp<PrecompileShader> wrapped) { return sk_make_sp<PrecompileLightingShader>(std::move(wrapped)); } //-------------------------------------------------------------------------------------------------- } // namespace skgpu::graphite