blob: f4800326d710f9cd9b625b420d8d1403f6b66cda [file] [log] [blame]
/*
* 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