blob: 2569e9fa28e90a5f7f91bc860f37a7e701339adc [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/KeyHelpers.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkImageInfo.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/Surface.h"
#include "src/base/SkHalf.h"
#include "src/core/SkBlendModeBlender.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkDebugUtils.h"
#include "src/core/SkRuntimeBlender.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkYUVMath.h"
#include "src/effects/colorfilters/SkBlendModeColorFilter.h"
#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/effects/colorfilters/SkColorSpaceXformColorFilter.h"
#include "src/effects/colorfilters/SkComposeColorFilter.h"
#include "src/effects/colorfilters/SkGaussianColorFilter.h"
#include "src/effects/colorfilters/SkMatrixColorFilter.h"
#include "src/effects/colorfilters/SkRuntimeColorFilter.h"
#include "src/effects/colorfilters/SkTableColorFilter.h"
#include "src/effects/colorfilters/SkWorkingFormatColorFilter.h"
#include "src/gpu/Blend.h"
#include "src/gpu/DitherUtils.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/Image_Graphite.h"
#include "src/gpu/graphite/Image_YUVA_Graphite.h"
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/PaintParams.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/ReadSwizzle.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/TextureProxy.h"
#include "src/gpu/graphite/TextureProxyView.h"
#include "src/gpu/graphite/TextureUtils.h"
#include "src/gpu/graphite/Uniform.h"
#include "src/gpu/graphite/UniformManager.h"
#include "src/gpu/graphite/YUVATextureProxies.h"
#include "src/image/SkImage_Base.h"
#include "src/shaders/SkBlendShader.h"
#include "src/shaders/SkColorFilterShader.h"
#include "src/shaders/SkColorShader.h"
#include "src/shaders/SkCoordClampShader.h"
#include "src/shaders/SkEmptyShader.h"
#include "src/shaders/SkImageShader.h"
#include "src/shaders/SkLocalMatrixShader.h"
#include "src/shaders/SkPerlinNoiseShaderImpl.h"
#include "src/shaders/SkPictureShader.h"
#include "src/shaders/SkRuntimeShader.h"
#include "src/shaders/SkShaderBase.h"
#include "src/shaders/SkTransformShader.h"
#include "src/shaders/SkTriColorShader.h"
#include "src/shaders/SkWorkingColorSpaceShader.h"
#include "src/shaders/gradients/SkConicalGradient.h"
#include "src/shaders/gradients/SkGradientBaseShader.h"
#include "src/shaders/gradients/SkLinearGradient.h"
#include "src/shaders/gradients/SkRadialGradient.h"
#include "src/shaders/gradients/SkSweepGradient.h"
constexpr SkPMColor4f kErrorColor = { 1, 0, 0, 1 };
#define VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID) \
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, dict->getUniforms(codeSnippetID));)
namespace skgpu::graphite {
//--------------------------------------------------------------------------------------------------
namespace {
void add_solid_uniform_data(const ShaderCodeDictionary* dict,
const SkPMColor4f& premulColor,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kSolidColorShader)
gatherer->write(premulColor);
}
} // anonymous namespace
void SolidColorShaderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkPMColor4f& premulColor) {
add_solid_uniform_data(keyContext.dict(), premulColor, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kSolidColorShader);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_rgb_paint_color_uniform_data(const ShaderCodeDictionary* dict,
const SkPMColor4f& premulColor,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kRGBPaintColor)
gatherer->writePaintColor(premulColor);
}
void add_alpha_only_paint_color_uniform_data(const ShaderCodeDictionary* dict,
const SkPMColor4f& premulColor,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAlphaOnlyPaintColor)
gatherer->writePaintColor(premulColor);
}
} // anonymous namespace
void RGBPaintColorBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer) {
add_rgb_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
builder->addBlock(BuiltInCodeSnippetID::kRGBPaintColor);
}
void AlphaOnlyPaintColorBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer) {
add_alpha_only_paint_color_uniform_data(keyContext.dict(), keyContext.paintColor(), gatherer);
builder->addBlock(BuiltInCodeSnippetID::kAlphaOnlyPaintColor);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_dst_read_sample_uniform_data(const ShaderCodeDictionary* dict,
PipelineDataGatherer* gatherer,
sk_sp<TextureProxy> dstTexture,
SkIPoint dstOffset) {
static const SkTileMode kTileModes[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
gatherer->add(SkSamplingOptions(), kTileModes, dstTexture);
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kDstReadSample)
SkV4 coords{static_cast<float>(dstOffset.x()),
static_cast<float>(dstOffset.y()),
dstTexture ? 1.0f / dstTexture->dimensions().width() : 1.0f,
dstTexture ? 1.0f / dstTexture->dimensions().height() : 1.0f };
gatherer->write(coords);
}
} // anonymous namespace
void DstReadSampleBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
sk_sp<TextureProxy> dstTexture,
SkIPoint dstOffset) {
add_dst_read_sample_uniform_data(keyContext.dict(), gatherer, std::move(dstTexture), dstOffset);
builder->addBlock(BuiltInCodeSnippetID::kDstReadSample);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_gradient_preamble(const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
constexpr int kInternalStopLimit = GradientShaderBlocks::GradientData::kNumInternalStorageStops;
if (gradData.fNumStops <= kInternalStopLimit) {
if (gradData.fNumStops <= 4) {
// Round up to 4 stops.
gatherer->writeArray({gradData.fColors, 4});
// The offsets are packed into a single float4 to save space.
gatherer->write(SkSLType::kFloat4, &gradData.fOffsets);
} else if (gradData.fNumStops <= 8) {
// Round up to 8 stops.
gatherer->writeArray({gradData.fColors, 8});
// The offsets are packed into a float4 array to save space.
gatherer->writeArray(SkSLType::kFloat4, &gradData.fOffsets, 2);
} else {
// Did kNumInternalStorageStops change?
SkUNREACHABLE;
}
}
}
// All the gradients share a common postamble of:
// numStops - for texture-based gradients
// tilemode
// colorSpace
// doUnPremul
void add_gradient_postamble(const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
constexpr int kInternalStopLimit = GradientShaderBlocks::GradientData::kNumInternalStorageStops;
static_assert(static_cast<int>(ColorSpace::kLab) == 2);
static_assert(static_cast<int>(ColorSpace::kOKLab) == 3);
static_assert(static_cast<int>(ColorSpace::kLCH) == 4);
static_assert(static_cast<int>(ColorSpace::kOKLCH) == 5);
static_assert(static_cast<int>(ColorSpace::kHSL) == 7);
static_assert(static_cast<int>(ColorSpace::kHWB) == 8);
bool inputPremul = static_cast<bool>(gradData.fInterpolation.fInPremul);
if (gradData.fNumStops > kInternalStopLimit) {
gatherer->write(gradData.fNumStops);
}
gatherer->write(static_cast<int>(gradData.fTM));
gatherer->write(static_cast<int>(gradData.fInterpolation.fColorSpace));
gatherer->write(static_cast<int>(inputPremul));
}
void add_linear_gradient_uniform_data(const ShaderCodeDictionary* dict,
BuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
add_gradient_preamble(gradData, gatherer);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fPoints[1]);
add_gradient_postamble(gradData, gatherer);
};
void add_radial_gradient_uniform_data(const ShaderCodeDictionary* dict,
BuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
add_gradient_preamble(gradData, gatherer);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fRadii[0]);
add_gradient_postamble(gradData, gatherer);
};
void add_sweep_gradient_uniform_data(const ShaderCodeDictionary* dict,
BuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
add_gradient_preamble(gradData, gatherer);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fBias);
gatherer->write(gradData.fScale);
add_gradient_postamble(gradData, gatherer);
};
void add_conical_gradient_uniform_data(const ShaderCodeDictionary* dict,
BuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
add_gradient_preamble(gradData, gatherer);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fPoints[1]);
gatherer->write(gradData.fRadii[0]);
gatherer->write(gradData.fRadii[1]);
add_gradient_postamble(gradData, gatherer);
};
} // anonymous namespace
GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type, int numStops)
: fType(type)
, fPoints{{0.0f, 0.0f}, {0.0f, 0.0f}}
, fRadii{0.0f, 0.0f}
, fBias(0.0f)
, fScale(0.0f)
, fTM(SkTileMode::kClamp)
, fNumStops(numStops) {
sk_bzero(fColors, sizeof(fColors));
sk_bzero(fOffsets, sizeof(fOffsets));
}
GradientShaderBlocks::GradientData::GradientData(SkShaderBase::GradientType type,
SkPoint point0, SkPoint point1,
float radius0, float radius1,
float bias, float scale,
SkTileMode tm,
int numStops,
const SkPMColor4f* colors,
const float* offsets,
sk_sp<TextureProxy> colorsAndOffsetsProxy,
const SkGradientShader::Interpolation& interp)
: fType(type)
, fBias(bias)
, fScale(scale)
, fTM(tm)
, fNumStops(numStops)
, fInterpolation(interp) {
SkASSERT(fNumStops >= 1);
fPoints[0] = point0;
fPoints[1] = point1;
fRadii[0] = radius0;
fRadii[1] = radius1;
if (fNumStops <= kNumInternalStorageStops) {
memcpy(fColors, colors, fNumStops * sizeof(SkColor4f));
if (offsets) {
memcpy(fOffsets, offsets, fNumStops * sizeof(float));
} else {
for (int i = 0; i < fNumStops; ++i) {
fOffsets[i] = SkIntToFloat(i) / (fNumStops-1);
}
}
// Extend the colors and offset, if necessary, to fill out the arrays
// TODO: this should be done later when the actual code snippet has been selected!!
for (int i = fNumStops ; i < kNumInternalStorageStops; ++i) {
fColors[i] = fColors[fNumStops-1];
fOffsets[i] = fOffsets[fNumStops-1];
}
} else {
fColorsAndOffsetsProxy = std::move(colorsAndOffsetsProxy);
SkASSERT(fColorsAndOffsetsProxy);
}
}
void GradientShaderBlocks::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder *builder,
PipelineDataGatherer* gatherer,
const GradientData& gradData) {
auto dict = keyContext.dict();
if (gradData.fNumStops > GradientData::kNumInternalStorageStops && gatherer) {
SkASSERT(gradData.fColorsAndOffsetsProxy);
static constexpr SkSamplingOptions kNearest(SkFilterMode::kNearest, SkMipmapMode::kNone);
static constexpr SkTileMode kClampTiling[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
gatherer->add(kNearest, kClampTiling, gradData.fColorsAndOffsetsProxy);
}
BuiltInCodeSnippetID codeSnippetID = BuiltInCodeSnippetID::kSolidColorShader;
switch (gradData.fType) {
case SkShaderBase::GradientType::kLinear:
codeSnippetID =
gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kLinearGradientShader4
: gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kLinearGradientShader8
: BuiltInCodeSnippetID::kLinearGradientShaderTexture;
add_linear_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
break;
case SkShaderBase::GradientType::kRadial:
codeSnippetID =
gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kRadialGradientShader4
: gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kRadialGradientShader8
: BuiltInCodeSnippetID::kRadialGradientShaderTexture;
add_radial_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
break;
case SkShaderBase::GradientType::kSweep:
codeSnippetID =
gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kSweepGradientShader4
: gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kSweepGradientShader8
: BuiltInCodeSnippetID::kSweepGradientShaderTexture;
add_sweep_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
break;
case SkShaderBase::GradientType::kConical:
codeSnippetID =
gradData.fNumStops <= 4 ? BuiltInCodeSnippetID::kConicalGradientShader4
: gradData.fNumStops <= 8 ? BuiltInCodeSnippetID::kConicalGradientShader8
: BuiltInCodeSnippetID::kConicalGradientShaderTexture;
add_conical_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
break;
case SkShaderBase::GradientType::kNone:
default:
SkDEBUGFAIL("Expected a gradient shader, but it wasn't one.");
break;
}
builder->addBlock(codeSnippetID);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_localmatrixshader_uniform_data(const ShaderCodeDictionary* dict,
const SkM44& localMatrix,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kLocalMatrixShader)
SkM44 lmInverse;
bool wasInverted = localMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
}
} // anonymous namespace
void LocalMatrixShaderBlock::BeginBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const LMShaderData& lmShaderData) {
add_localmatrixshader_uniform_data(keyContext.dict(), lmShaderData.fLocalMatrix, gatherer);
builder->beginBlock(BuiltInCodeSnippetID::kLocalMatrixShader);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_color_space_uniforms(const SkColorSpaceXformSteps& steps, PipelineDataGatherer* gatherer) {
// We have 7 source coefficients and 7 destination coefficients. We pass them via a 4x4 matrix;
// the first two columns hold the source values, and the second two hold the destination.
// (The final value of each 8-element group is ignored.)
// In std140, this arrangement is much more efficient than a simple array of scalars.
SkM44 coeffs;
gatherer->write(SkTo<int>(steps.flags.mask()));
if (steps.flags.linearize) {
gatherer->write(SkTo<int>(skcms_TransferFunction_getType(&steps.srcTF)));
coeffs.setCol(0, {steps.srcTF.g, steps.srcTF.a, steps.srcTF.b, steps.srcTF.c});
coeffs.setCol(1, {steps.srcTF.d, steps.srcTF.e, steps.srcTF.f, 0.0f});
} else {
gatherer->write(SkTo<int>(skcms_TFType::skcms_TFType_Invalid));
}
SkMatrix gamutTransform;
if (steps.flags.gamut_transform) {
// TODO: it seems odd to copy this into an SkMatrix just to write it to the gatherer
// src_to_dst_matrix is column-major, SkMatrix is row-major.
const float* m = steps.src_to_dst_matrix;
gamutTransform.setAll(m[0], m[3], m[6],
m[1], m[4], m[7],
m[2], m[5], m[8]);
}
gatherer->writeHalf(gamutTransform);
if (steps.flags.encode) {
gatherer->write(SkTo<int>(skcms_TransferFunction_getType(&steps.dstTFInv)));
coeffs.setCol(2, {steps.dstTFInv.g, steps.dstTFInv.a, steps.dstTFInv.b, steps.dstTFInv.c});
coeffs.setCol(3, {steps.dstTFInv.d, steps.dstTFInv.e, steps.dstTFInv.f, 0.0f});
} else {
gatherer->write(SkTo<int>(skcms_TFType::skcms_TFType_Invalid));
}
gatherer->writeHalf(coeffs);
}
void add_image_uniform_data(const ShaderCodeDictionary* dict,
const ImageShaderBlock::ImageData& imgData,
PipelineDataGatherer* gatherer) {
SkASSERT(!imgData.fSampling.useCubic);
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kImageShader)
gatherer->write(SkSize::Make(imgData.fImgSize));
gatherer->write(imgData.fSubset);
gatherer->write(SkTo<int>(imgData.fTileModes[0]));
gatherer->write(SkTo<int>(imgData.fTileModes[1]));
gatherer->write(SkTo<int>(imgData.fSampling.filter));
gatherer->write(SkTo<int>(imgData.fReadSwizzle));
add_color_space_uniforms(imgData.fSteps, gatherer);
}
void add_cubic_image_uniform_data(const ShaderCodeDictionary* dict,
const ImageShaderBlock::ImageData& imgData,
PipelineDataGatherer* gatherer) {
SkASSERT(imgData.fSampling.useCubic);
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCubicImageShader)
gatherer->write(SkSize::Make(imgData.fImgSize));
gatherer->write(imgData.fSubset);
gatherer->write(SkTo<int>(imgData.fTileModes[0]));
gatherer->write(SkTo<int>(imgData.fTileModes[1]));
const SkCubicResampler& cubic = imgData.fSampling.cubic;
gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
gatherer->write(SkTo<int>(imgData.fReadSwizzle));
add_color_space_uniforms(imgData.fSteps, gatherer);
}
void add_hw_image_uniform_data(const ShaderCodeDictionary* dict,
const ImageShaderBlock::ImageData& imgData,
PipelineDataGatherer* gatherer) {
SkASSERT(!imgData.fSampling.useCubic);
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kHWImageShader)
gatherer->write(SkSize::Make(imgData.fImgSize));
gatherer->write(SkTo<int>(imgData.fReadSwizzle));
add_color_space_uniforms(imgData.fSteps, gatherer);
}
} // anonymous namespace
ImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
SkTileMode tileModeX,
SkTileMode tileModeY,
SkISize imgSize,
SkRect subset,
ReadSwizzle readSwizzle)
: fSampling(sampling)
, fTileModes{tileModeX, tileModeY}
, fImgSize(imgSize)
, fSubset(subset)
, fReadSwizzle(readSwizzle) {
SkASSERT(fSteps.flags.mask() == 0); // By default, the colorspace should have no effect
}
static bool can_do_tiling_in_hw(const Caps* caps, const ImageShaderBlock::ImageData& imgData) {
if (!caps->clampToBorderSupport() && (imgData.fTileModes[0] == SkTileMode::kDecal ||
imgData.fTileModes[1] == SkTileMode::kDecal)) {
return false;
}
return imgData.fSubset.contains(SkRect::Make(imgData.fImgSize));
}
void ImageShaderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const ImageData& imgData) {
if (keyContext.recorder() && !imgData.fTextureProxy) {
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
const Caps* caps = keyContext.caps();
const bool doTilingInHw = !imgData.fSampling.useCubic && can_do_tiling_in_hw(caps, imgData);
static constexpr SkTileMode kDefaultTileModes[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
gatherer->add(imgData.fSampling,
doTilingInHw ? imgData.fTileModes : kDefaultTileModes,
imgData.fTextureProxy);
if (doTilingInHw) {
add_hw_image_uniform_data(keyContext.dict(), imgData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kHWImageShader);
} else if (imgData.fSampling.useCubic) {
add_cubic_image_uniform_data(keyContext.dict(), imgData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kCubicImageShader);
} else {
add_image_uniform_data(keyContext.dict(), imgData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kImageShader);
}
}
//--------------------------------------------------------------------------------------------------
// makes use of ImageShader functions, above
namespace {
void add_yuv_image_uniform_data(const ShaderCodeDictionary* dict,
const YUVImageShaderBlock::ImageData& imgData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kYUVImageShader)
gatherer->write(SkSize::Make(imgData.fImgSize));
gatherer->write(imgData.fSubset);
gatherer->write(SkTo<int>(imgData.fTileModes[0]));
gatherer->write(SkTo<int>(imgData.fTileModes[1]));
gatherer->write(SkTo<int>(imgData.fSampling.filter));
gatherer->write(imgData.fSampling.useCubic);
if (imgData.fSampling.useCubic) {
const SkCubicResampler& cubic = imgData.fSampling.cubic;
gatherer->writeHalf(SkImageShader::CubicResamplerMatrix(cubic.B, cubic.C));
} else {
gatherer->writeHalf(SkM44());
}
for (int i = 0; i < 4; ++i) {
gatherer->writeHalf(imgData.fChannelSelect[i]);
}
gatherer->writeHalf(imgData.fYUVtoRGBMatrix);
gatherer->write(imgData.fYUVtoRGBTranslate);
add_color_space_uniforms(imgData.fSteps, gatherer);
}
} // anonymous namespace
YUVImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
SkTileMode tileModeX,
SkTileMode tileModeY,
SkISize imgSize,
SkRect subset)
: fSampling(sampling)
, fTileModes{tileModeX, tileModeY}
, fImgSize(imgSize)
, fSubset(subset) {
SkASSERT(fSteps.flags.mask() == 0); // By default, the colorspace should have no effect
}
void YUVImageShaderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const ImageData& imgData) {
if (keyContext.recorder() &&
(!imgData.fTextureProxies[0] || !imgData.fTextureProxies[1] ||
!imgData.fTextureProxies[2] || !imgData.fTextureProxies[3])) {
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
for (int i = 0; i < 4; ++i) {
gatherer->add(imgData.fSampling, imgData.fTileModes, imgData.fTextureProxies[i]);
}
add_yuv_image_uniform_data(keyContext.dict(), imgData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kYUVImageShader);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_coordclamp_uniform_data(const ShaderCodeDictionary* dict,
const CoordClampShaderBlock::CoordClampData& clampData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCoordClampShader)
gatherer->write(clampData.fSubset);
}
} // anonymous namespace
void CoordClampShaderBlock::BeginBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const CoordClampData& clampData) {
add_coordclamp_uniform_data(keyContext.dict(), clampData, gatherer);
builder->beginBlock(BuiltInCodeSnippetID::kCoordClampShader);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_dither_uniform_data(const ShaderCodeDictionary* dict,
const DitherShaderBlock::DitherData& ditherData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kDitherShader)
gatherer->writeHalf(ditherData.fRange);
}
} // anonymous namespace
void DitherShaderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const DitherData& data) {
add_dither_uniform_data(keyContext.dict(), data, gatherer);
static constexpr SkSamplingOptions kNearest(SkFilterMode::kNearest, SkMipmapMode::kNone);
static constexpr SkTileMode kRepeatTiling[2] = { SkTileMode::kRepeat, SkTileMode::kRepeat };
SkASSERT(data.fLUTProxy || !keyContext.recorder());
gatherer->add(kNearest, kRepeatTiling, data.fLUTProxy);
builder->addBlock(BuiltInCodeSnippetID::kDitherShader);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_perlin_noise_uniform_data(const ShaderCodeDictionary* dict,
const PerlinNoiseShaderBlock::PerlinNoiseData& noiseData,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kPerlinNoiseShader)
gatherer->write(noiseData.fBaseFrequency);
gatherer->write(noiseData.fStitchData);
gatherer->write(static_cast<int>(noiseData.fType));
gatherer->write(noiseData.fNumOctaves);
gatherer->write(static_cast<int>(noiseData.stitching()));
static const SkTileMode kRepeatXTileModes[2] = { SkTileMode::kRepeat, SkTileMode::kClamp };
static const SkSamplingOptions kNearestSampling { SkFilterMode::kNearest };
gatherer->add(kNearestSampling, kRepeatXTileModes, noiseData.fPermutationsProxy);
gatherer->add(kNearestSampling, kRepeatXTileModes, noiseData.fNoiseProxy);
}
} // anonymous namespace
void PerlinNoiseShaderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const PerlinNoiseData& noiseData) {
add_perlin_noise_uniform_data(keyContext.dict(), noiseData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kPerlinNoiseShader);
}
//--------------------------------------------------------------------------------------------------
void BlendShaderBlock::BeginBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kBlendShader)
builder->beginBlock(BuiltInCodeSnippetID::kBlendShader);
}
//--------------------------------------------------------------------------------------------------
void BlendModeBlenderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
SkBlendMode blendMode) {
VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kBlendModeBlender)
gatherer->write(SkTo<int>(blendMode));
builder->addBlock(BuiltInCodeSnippetID::kBlendModeBlender);
}
//--------------------------------------------------------------------------------------------------
void CoeffBlenderBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
SkSpan<const float> coeffs) {
VALIDATE_UNIFORMS(gatherer, keyContext.dict(), BuiltInCodeSnippetID::kCoeffBlender)
SkASSERT(coeffs.size() == 4);
gatherer->write(SkSLType::kHalf4, coeffs.data());
builder->addBlock(BuiltInCodeSnippetID::kCoeffBlender);
}
//--------------------------------------------------------------------------------------------------
void ComposeBlock::BeginBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer) {
builder->beginBlock(BuiltInCodeSnippetID::kCompose);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_matrix_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
const MatrixColorFilterBlock::MatrixColorFilterData& data,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kMatrixColorFilter)
gatherer->write(data.fMatrix);
gatherer->write(data.fTranslate);
gatherer->write(static_cast<int>(data.fInHSLA));
}
} // anonymous namespace
void MatrixColorFilterBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const MatrixColorFilterData& matrixCFData) {
add_matrix_colorfilter_uniform_data(keyContext.dict(), matrixCFData, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kMatrixColorFilter);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_table_colorfilter_uniform_data(const ShaderCodeDictionary* dict,
const TableColorFilterBlock::TableColorFilterData& data,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kTableColorFilter)
static const SkTileMode kTileModes[2] = { SkTileMode::kClamp, SkTileMode::kClamp };
gatherer->add(SkSamplingOptions(), kTileModes, data.fTextureProxy);
}
} // anonymous namespace
void TableColorFilterBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const TableColorFilterData& data) {
SkASSERT(data.fTextureProxy || !keyContext.recorder());
add_table_colorfilter_uniform_data(keyContext.dict(), data, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kTableColorFilter);
}
//--------------------------------------------------------------------------------------------------
namespace {
void add_color_space_xform_uniform_data(
const ShaderCodeDictionary* dict,
const ColorSpaceTransformBlock::ColorSpaceTransformData& data,
PipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kColorSpaceXformColorFilter)
add_color_space_uniforms(data.fSteps, gatherer);
}
} // anonymous namespace
ColorSpaceTransformBlock::ColorSpaceTransformData::ColorSpaceTransformData(const SkColorSpace* src,
SkAlphaType srcAT,
const SkColorSpace* dst,
SkAlphaType dstAT)
: fSteps(src, srcAT, dst, dstAT) {}
void ColorSpaceTransformBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const ColorSpaceTransformData& data) {
add_color_space_xform_uniform_data(keyContext.dict(), data, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kColorSpaceXformColorFilter);
}
//--------------------------------------------------------------------------------------------------
void AddBlendModeColorFilter(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
SkBlendMode bm,
const SkPMColor4f& srcColor) {
Blend(keyContext, builder, gatherer,
/* addBlendToKey= */ [&] () -> void {
// Note, we're playing a bit of a game here. By explicitly adding a
// BlendModeBlenderBlock we're always forcing the SkSL to call 'sk_blend'
// rather than allowing it to sometimes call 'blend_porter_duff'. This reduces
// the number of shader combinations and allows the pre-compilation system to more
// easily match the rendering path.
BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer, bm);
},
/* addSrcToKey= */ [&]() -> void {
SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, srcColor);
},
/* addDstToKey= */ [&]() -> void {
builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
});
}
RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect)
: fEffect(std::move(effect)) {}
RuntimeEffectBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect,
sk_sp<const SkData> uniforms)
: fEffect(std::move(effect))
, fUniforms(std::move(uniforms)) {}
static bool skdata_matches(const SkData* a, const SkData* b) {
// Returns true if both SkData objects hold the same contents, or if they are both null.
// (SkData::equals supports passing null, and returns false.)
return a ? a->equals(b) : (a == b);
}
bool RuntimeEffectBlock::ShaderData::operator==(const ShaderData& rhs) const {
return fEffect == rhs.fEffect && skdata_matches(fUniforms.get(), rhs.fUniforms.get());
}
static void gather_runtime_effect_uniforms(SkSpan<const SkRuntimeEffect::Uniform> rtsUniforms,
SkSpan<const Uniform> graphiteUniforms,
const SkData* uniformData,
PipelineDataGatherer* gatherer) {
if (!rtsUniforms.empty() && uniformData) {
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, graphiteUniforms);)
// Collect all the other uniforms from the provided SkData.
const uint8_t* uniformBase = uniformData->bytes();
for (size_t index = 0; index < rtsUniforms.size(); ++index) {
const Uniform& uniform = graphiteUniforms[index];
// Get a pointer to the offset in our data for this uniform.
const uint8_t* uniformPtr = uniformBase + rtsUniforms[index].offset;
// Pass the uniform data to the gatherer.
gatherer->write(uniform, uniformPtr);
}
}
}
void RuntimeEffectBlock::BeginBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const ShaderData& shaderData) {
ShaderCodeDictionary* dict = keyContext.dict();
int codeSnippetID = dict->findOrCreateRuntimeEffectSnippet(shaderData.fEffect.get());
keyContext.rtEffectDict()->set(codeSnippetID, shaderData.fEffect);
const ShaderSnippet* entry = dict->getEntry(codeSnippetID);
SkASSERT(entry);
gather_runtime_effect_uniforms(shaderData.fEffect->uniforms(),
entry->fUniforms,
shaderData.fUniforms.get(),
gatherer);
builder->beginBlock(codeSnippetID);
}
// ==================================================================
namespace {
void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkBlendModeBlender* blender) {
SkASSERT(blender);
AddModeBlend(keyContext, builder, gatherer, blender->mode());
}
// Be sure to keep this function in sync w/ its correlate in FactoryFunctions.cpp
void add_children_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
SkSpan<const SkRuntimeEffect::ChildPtr> children,
SkSpan<const SkRuntimeEffect::Child> childInfo) {
SkASSERT(children.size() == childInfo.size());
using ChildType = SkRuntimeEffect::ChildType;
KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
for (size_t index = 0; index < children.size(); ++index) {
const SkRuntimeEffect::ChildPtr& child = children[index];
std::optional<ChildType> type = child.type();
if (type == ChildType::kShader) {
AddToKey(childContext, builder, gatherer, child.shader());
} else if (type == ChildType::kColorFilter) {
AddToKey(childContext, builder, gatherer, child.colorFilter());
} else if (type == ChildType::kBlender) {
AddToKey(childContext, builder, gatherer, child.blender());
} else {
// We don't have a child effect. Substitute in a no-op effect.
switch (childInfo[index].type) {
case ChildType::kShader:
// A missing shader returns transparent black
SolidColorShaderBlock::AddBlock(childContext, builder, gatherer,
SK_PMColor4fTRANSPARENT);
break;
case ChildType::kColorFilter:
// A "passthrough" color filter returns the input color as-is.
builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
break;
case ChildType::kBlender:
// A "passthrough" blender performs `blend_src_over(src, dest)`.
AddKnownModeBlend(childContext, builder, gatherer, SkBlendMode::kSrcOver);
break;
}
}
}
}
void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkRuntimeBlender* blender) {
SkASSERT(blender);
sk_sp<SkRuntimeEffect> effect = blender->effect();
SkASSERT(effect);
sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
effect->uniforms(),
blender->uniforms(),
keyContext.dstColorInfo().colorSpace());
SkASSERT(uniforms);
RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
{ effect, std::move(uniforms) });
add_children_to_key(keyContext, builder, gatherer,
blender->children(), effect->children());
builder->endBlock();
}
} // anonymous namespace
void AddToKey(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkBlender* blender) {
if (!blender) {
return;
}
switch (as_BB(blender)->type()) {
#define M(type) \
case SkBlenderBase::BlenderType::k##type: \
add_to_key(keyContext, \
builder, \
gatherer, \
static_cast<const Sk##type##Blender*>(blender)); \
return;
SK_ALL_BLENDERS(M)
#undef M
}
SkUNREACHABLE;
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
static SkPMColor4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
SkPMColor4f color = {c.fR, c.fG, c.fB, c.fA};
SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kPremul_SkAlphaType).apply(color.vec());
return color;
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkBlendModeColorFilter* filter) {
SkASSERT(filter);
SkPMColor4f color = map_color(filter->color(), sk_srgb_singleton(),
keyContext.dstColorInfo().colorSpace());
AddBlendModeColorFilter(keyContext, builder, gatherer, filter->mode(), color);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkColorSpaceXformColorFilter* filter) {
SkASSERT(filter);
constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
ColorSpaceTransformBlock::ColorSpaceTransformData csData(filter->src().get(), kAlphaType,
filter->dst().get(), kAlphaType);
ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* keyBuilder,
PipelineDataGatherer* gatherer,
const SkComposeColorFilter* filter) {
SkASSERT(filter);
Compose(keyContext, keyBuilder, gatherer,
/* addInnerToKey= */ [&]() -> void {
AddToKey(keyContext, keyBuilder, gatherer, filter->inner().get());
},
/* addOuterToKey= */ [&]() -> void {
AddToKey(keyContext, keyBuilder, gatherer, filter->outer().get());
});
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkGaussianColorFilter*) {
builder->addBlock(BuiltInCodeSnippetID::kGaussianColorFilter);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkMatrixColorFilter* filter) {
SkASSERT(filter);
bool inHSLA = filter->domain() == SkMatrixColorFilter::Domain::kHSLA;
MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(filter->matrix(), inHSLA);
MatrixColorFilterBlock::AddBlock(keyContext, builder, gatherer, matrixCFData);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkRuntimeColorFilter* filter) {
SkASSERT(filter);
sk_sp<SkRuntimeEffect> effect = filter->effect();
sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
effect->uniforms(), filter->uniforms(), keyContext.dstColorInfo().colorSpace());
SkASSERT(uniforms);
RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, {effect, std::move(uniforms)});
add_children_to_key(keyContext, builder, gatherer,
filter->children(), effect->children());
builder->endBlock();
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkTableColorFilter* filter) {
SkASSERT(filter);
sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(),
filter->bitmap());
if (!proxy) {
SKGPU_LOG_W("Couldn't create TableColorFilter's table");
// Return the input color as-is.
builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
return;
}
TableColorFilterBlock::TableColorFilterData data(std::move(proxy));
TableColorFilterBlock::AddBlock(keyContext, builder, gatherer, data);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkWorkingFormatColorFilter* filter) {
SkASSERT(filter);
const SkColorInfo& dstInfo = keyContext.dstColorInfo();
const SkAlphaType dstAT = dstInfo.alphaType();
sk_sp<SkColorSpace> dstCS = dstInfo.refColorSpace();
if (!dstCS) {
dstCS = SkColorSpace::MakeSRGB();
}
SkAlphaType workingAT;
sk_sp<SkColorSpace> workingCS = filter->workingFormat(dstCS, &workingAT);
SkColorInfo workingInfo(dstInfo.colorType(), workingAT, workingCS);
KeyContextWithColorInfo workingContext(keyContext, workingInfo);
// Use two nested compose blocks to chain (dst->working), child, and (working->dst) together
// while appearing as one block to the parent node.
Compose(keyContext, builder, gatherer,
/* addInnerToKey= */ [&]() -> void {
// Inner compose
Compose(keyContext, builder, gatherer,
/* addInnerToKey= */ [&]() -> void {
// Innermost (inner of inner compose)
ColorSpaceTransformBlock::ColorSpaceTransformData data1(
dstCS.get(), dstAT, workingCS.get(), workingAT);
ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
data1);
},
/* addOuterToKey= */ [&]() -> void {
// Middle (outer of inner compose)
AddToKey(workingContext, builder, gatherer, filter->child().get());
});
},
/* addOuterToKey= */ [&]() -> void {
// Outermost (outer of outer compose)
ColorSpaceTransformBlock::ColorSpaceTransformData data2(
workingCS.get(), workingAT, dstCS.get(), dstAT);
ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data2);
});
}
void AddToKey(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkColorFilter* filter) {
if (!filter) {
return;
}
switch (as_CFB(filter)->type()) {
case SkColorFilterBase::Type::kNoop:
// Return the input color as-is.
builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
return;
#define M(type) \
case SkColorFilterBase::Type::k##type: \
add_to_key(keyContext, \
builder, \
gatherer, \
static_cast<const Sk##type##ColorFilter*>(filter)); \
return;
SK_ALL_COLOR_FILTERS(M)
#undef M
}
SkUNREACHABLE;
}
// ==================================================================
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkBlendShader* shader) {
SkASSERT(shader);
Blend(keyContext, builder, gatherer,
/* addBlendToKey= */ [&] () -> void {
AddModeBlend(keyContext, builder, gatherer, shader->mode());
},
/* addSrcToKey= */ [&]() -> void {
AddToKey(keyContext, builder, gatherer, shader->src().get());
},
/* addDstToKey= */ [&]() -> void {
AddToKey(keyContext, builder, gatherer, shader->dst().get());
});
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkCTMShader*) {
// TODO(michaelludwig) implement this when clipShader() is implemented
SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, kErrorColor);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkColorShader* shader) {
SkASSERT(shader);
SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer,
SkColor4f::FromColor(shader->color()).premul());
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkColor4Shader* shader) {
SkASSERT(shader);
SolidColorShaderBlock::AddBlock(keyContext, builder, gatherer, shader->color().premul());
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkColorFilterShader* shader) {
SkASSERT(shader);
Compose(keyContext, builder, gatherer,
/* addInnerToKey= */ [&]() -> void {
AddToKey(keyContext, builder, gatherer, shader->shader().get());
},
/* addOuterToKey= */ [&]() -> void {
AddToKey(keyContext, builder, gatherer, shader->filter().get());
});
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkCoordClampShader* shader) {
SkASSERT(shader);
CoordClampShaderBlock::CoordClampData data(shader->subset());
CoordClampShaderBlock::BeginBlock(keyContext, builder, gatherer, data);
AddToKey(keyContext, builder, gatherer, shader->shader().get());
builder->endBlock();
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkEmptyShader*) {
builder->addBlock(BuiltInCodeSnippetID::kError);
}
static void add_yuv_image_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkImageShader* origShader,
sk_sp<const SkImage> imageToDraw,
SkSamplingOptions sampling) {
SkASSERT(!imageToDraw->isAlphaOnly());
const YUVATextureProxies& yuvaProxies =
static_cast<const Image_YUVA*>(imageToDraw.get())->yuvaProxies();
const SkYUVAInfo& yuvaInfo = yuvaProxies.yuvaInfo();
YUVImageShaderBlock::ImageData imgData(sampling,
origShader->tileModeX(),
origShader->tileModeY(),
imageToDraw->dimensions(),
origShader->subset());
for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
memset(&imgData.fChannelSelect[i], 0, sizeof(SkColor4f));
}
int textureCount = 0;
SkYUVAInfo::YUVALocations yuvaLocations = yuvaProxies.yuvaLocations();
for (int locIndex = 0; locIndex < SkYUVAInfo::kYUVAChannelCount; ++locIndex) {
auto [yuvPlane, yuvChannel] = yuvaLocations[locIndex];
if (yuvPlane >= 0) {
SkASSERT(locIndex == textureCount);
TextureProxyView view = yuvaProxies.makeView(yuvPlane);
imgData.fTextureProxies[locIndex] = view.refProxy();
imgData.fChannelSelect[locIndex][static_cast<int>(yuvChannel)] = 1.0f;
++textureCount;
}
}
SkASSERT(textureCount == 3 || textureCount == 4);
// If the format has no alpha, we still need to set the proxy to something
if (textureCount == 3) {
imgData.fTextureProxies[3] = imgData.fTextureProxies[0];
}
float yuvM[20];
SkColorMatrix_YUV2RGB(yuvaInfo.yuvColorSpace(), yuvM);
// We drop the fourth column entirely since the transformation
// should not depend on alpha. The fifth column is sent as a separate
// vector. The fourth row is also dropped entirely because alpha should
// never be modified.
SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
imgData.fYUVtoRGBMatrix.setAll(
yuvM[ 0], yuvM[ 1], yuvM[ 2],
yuvM[ 5], yuvM[ 6], yuvM[ 7],
yuvM[10], yuvM[11], yuvM[12]
);
imgData.fYUVtoRGBTranslate = {yuvM[4], yuvM[9], yuvM[14]};
if (!origShader->isRaw()) {
imgData.fSteps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
imageToDraw->alphaType(),
keyContext.dstColorInfo().colorSpace(),
keyContext.dstColorInfo().alphaType());
}
// The YUV formats can encode their own origin including reflection and rotation,
// so we need to wrap our block in an additional local matrix transform.
SkMatrix originMatrix = yuvaInfo.originMatrix();
LocalMatrixShaderBlock::LMShaderData lmShaderData(originMatrix);
KeyContextWithLocalMatrix newContext(keyContext, originMatrix);
LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
YUVImageShaderBlock::AddBlock(newContext, builder, gatherer, imgData);
builder->endBlock();
}
static skgpu::graphite::ReadSwizzle swizzle_class_to_read_enum(const skgpu::Swizzle& swizzle) {
if (swizzle == skgpu::Swizzle::RGBA()) {
return skgpu::graphite::ReadSwizzle::kRGBA;
} else if (swizzle == skgpu::Swizzle::RGB1()) {
return skgpu::graphite::ReadSwizzle::kRGB1;
} else if (swizzle == skgpu::Swizzle("rrrr")) {
return skgpu::graphite::ReadSwizzle::kRRRR;
} else if (swizzle == skgpu::Swizzle("rrr1")) {
return skgpu::graphite::ReadSwizzle::kRRR1;
} else if (swizzle == skgpu::Swizzle::BGRA()) {
return skgpu::graphite::ReadSwizzle::kBGRA;
} else if (swizzle == skgpu::Swizzle("000r")) {
return skgpu::graphite::ReadSwizzle::k000R;
} else {
SKGPU_LOG_W("%s is an unsupported read swizzle. Defaulting to RGBA.\n",
swizzle.asString().data());
return skgpu::graphite::ReadSwizzle::kRGBA;
}
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkImageShader* shader) {
SkASSERT(shader);
auto [ imageToDraw, newSampling ] = GetGraphiteBacked(keyContext.recorder(),
shader->image().get(),
shader->sampling());
if (!imageToDraw) {
SKGPU_LOG_W("Couldn't convert ImageShader's image to a Graphite-backed image");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
if (as_IB(imageToDraw)->isYUVA()) {
return add_yuv_image_to_key(keyContext,
builder,
gatherer,
shader,
std::move(imageToDraw),
newSampling);
}
skgpu::Mipmapped mipmapped = (newSampling.mipmap != SkMipmapMode::kNone)
? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
auto [view, _] = AsView(keyContext.recorder(), imageToDraw.get(), mipmapped);
ImageShaderBlock::ImageData imgData(shader->sampling(),
shader->tileModeX(),
shader->tileModeY(),
view.proxy()->dimensions(),
shader->subset(),
ReadSwizzle::kRGBA);
imgData.fSampling = newSampling;
imgData.fTextureProxy = view.refProxy();
skgpu::Swizzle readSwizzle = view.swizzle();
// If the color type is alpha-only, propagate the alpha value to the other channels.
if (imageToDraw->isAlphaOnly()) {
readSwizzle = skgpu::Swizzle::Concat(readSwizzle, skgpu::Swizzle("000a"));
}
imgData.fReadSwizzle = swizzle_class_to_read_enum(readSwizzle);
if (!shader->isRaw()) {
imgData.fSteps = SkColorSpaceXformSteps(imageToDraw->colorSpace(),
imageToDraw->alphaType(),
keyContext.dstColorInfo().colorSpace(),
keyContext.dstColorInfo().alphaType());
if (imageToDraw->isAlphaOnly() && keyContext.scope() != KeyContext::Scope::kRuntimeEffect) {
Blend(keyContext, builder, gatherer,
/* addBlendToKey= */ [&] () -> void {
AddKnownModeBlend(keyContext, builder, gatherer, SkBlendMode::kDstIn);
},
/* addSrcToKey= */ [&] () -> void {
ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
},
/* addDstToKey= */ [&]() -> void {
RGBPaintColorBlock::AddBlock(keyContext, builder, gatherer);
});
return;
}
}
ImageShaderBlock::AddBlock(keyContext, builder, gatherer, imgData);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkLocalMatrixShader* shader) {
SkASSERT(shader);
auto wrappedShader = shader->wrappedShader().get();
// Fold the texture's origin flip into the local matrix so that the image shader doesn't need
// additional state
SkMatrix matrix;
if (as_SB(wrappedShader)->type() == SkShaderBase::ShaderType::kImage) {
auto imgShader = static_cast<const SkImageShader*>(wrappedShader);
// If the image is not graphite backed then we can assume the origin will be TopLeft as we
// require that in the ImageProvider utility. Also Graphite YUV images are assumed to be
// TopLeft origin.
auto imgBase = as_IB(imgShader->image());
if (imgBase->isGraphiteBacked() && !imgBase->isYUVA()) {
auto imgGraphite = static_cast<Image*>(imgBase);
SkASSERT(imgGraphite);
const auto& view = imgGraphite->textureProxyView();
if (view.origin() == Origin::kBottomLeft) {
matrix.setScaleY(-1);
matrix.setTranslateY(view.height());
}
}
}
matrix.postConcat(shader->localMatrix());
if (!matrix.isIdentity()) {
LocalMatrixShaderBlock::LMShaderData lmShaderData(matrix);
KeyContextWithLocalMatrix newContext(keyContext, matrix);
LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
AddToKey(newContext, builder, gatherer, wrappedShader);
builder->endBlock();
} else {
AddToKey(keyContext, builder, gatherer, wrappedShader);
}
}
// If either of these change then the corresponding change must also be made in the SkSL
// perlin_noise_shader function.
static_assert((int)SkPerlinNoiseShader::kFractalNoise_Type ==
(int)PerlinNoiseShaderBlock::Type::kFractalNoise);
static_assert((int)SkPerlinNoiseShader::kTurbulence_Type ==
(int)PerlinNoiseShaderBlock::Type::kTurbulence);
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkPerlinNoiseShader* shader) {
SkASSERT(shader);
SkASSERT(shader->numOctaves());
SkMatrix totalMatrix = keyContext.local2Dev().asM33();
if (keyContext.localMatrix()) {
totalMatrix.preConcat(*keyContext.localMatrix());
}
SkMatrix invTotal;
bool result = totalMatrix.invert(&invTotal);
if (!result) {
SKGPU_LOG_W("Couldn't invert totalMatrix for PerlinNoiseShader");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
std::unique_ptr<SkPerlinNoiseShader::PaintingData> paintingData =
shader->getPaintingData(totalMatrix);
paintingData->generateBitmaps();
sk_sp<TextureProxy> perm = RecorderPriv::CreateCachedProxy(
keyContext.recorder(), paintingData->getPermutationsBitmap());
sk_sp<TextureProxy> noise =
RecorderPriv::CreateCachedProxy(keyContext.recorder(), paintingData->getNoiseBitmap());
if (!perm || !noise) {
SKGPU_LOG_W("Couldn't create tables for PerlinNoiseShader");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
PerlinNoiseShaderBlock::PerlinNoiseData perlinData(
static_cast<PerlinNoiseShaderBlock::Type>(shader->noiseType()),
paintingData->fBaseFrequency,
shader->numOctaves(),
{paintingData->fStitchDataInit.fWidth, paintingData->fStitchDataInit.fHeight});
perlinData.fPermutationsProxy = std::move(perm);
perlinData.fNoiseProxy = std::move(noise);
// This (1,1) translation is due to WebKit's 1 based coordinates for the noise
// (as opposed to 0 based, usually). Remember: this matrix (shader2World) is going to be
// inverted before being applied.
SkMatrix shader2Local =
SkMatrix::Translate(-1 + totalMatrix.getTranslateX(), -1 + totalMatrix.getTranslateY());
shader2Local.postConcat(invTotal);
LocalMatrixShaderBlock::LMShaderData lmShaderData(shader2Local);
KeyContextWithLocalMatrix newContext(keyContext, shader2Local);
LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, lmShaderData);
PerlinNoiseShaderBlock::AddBlock(newContext, builder, gatherer, perlinData);
builder->endBlock();
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkPictureShader* shader) {
SkASSERT(shader);
Recorder* recorder = keyContext.recorder();
const Caps* caps = recorder->priv().caps();
// TODO: We'll need additional plumbing to get the correct props from our callers. In
// particular we'll need to expand the keyContext to have the surfaceProps, the dstColorType
// and dstColorSpace.
SkSurfaceProps props{};
SkMatrix totalM = keyContext.local2Dev().asM33();
if (keyContext.localMatrix()) {
totalM.preConcat(*keyContext.localMatrix());
}
auto info = SkPictureShader::CachedImageInfo::Make(shader->tile(),
totalM,
/* dstColorType= */ kRGBA_8888_SkColorType,
/* dstColorSpace= */ nullptr,
caps->maxTextureSize(),
props);
if (!info.success) {
SKGPU_LOG_W("Couldn't access PictureShaders' Image info");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
// TODO: right now we're explicitly not caching here. We could expand the ImageProvider
// API to include already Graphite-backed images, add a Recorder-local cache or add
// rendered-picture images to the global cache.
sk_sp<SkImage> img = info.makeImage(
SkSurfaces::RenderTarget(recorder, info.imageInfo, skgpu::Mipmapped::kNo, &info.props),
shader->picture().get());
if (!img) {
SKGPU_LOG_W("Couldn't create SkImage for PictureShader");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
const auto shaderLM = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
sk_sp<SkShader> imgShader = img->makeShader(shader->tileModeX(), shader->tileModeY(),
SkSamplingOptions(shader->filter()), &shaderLM);
if (!imgShader) {
SKGPU_LOG_W("Couldn't create SkImageShader for PictureShader");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
AddToKey(keyContext, builder, gatherer, imgShader.get());
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkRuntimeShader* shader) {
SkASSERT(shader);
sk_sp<SkRuntimeEffect> effect = shader->effect();
sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
effect->uniforms(),
shader->uniformData(keyContext.dstColorInfo().colorSpace()),
keyContext.dstColorInfo().colorSpace());
SkASSERT(uniforms);
RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
{effect, std::move(uniforms)});
add_children_to_key(keyContext, builder, gatherer,
shader->children(), effect->children());
builder->endBlock();
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkTransformShader* shader) {
SKGPU_LOG_W("Raster-only SkShader (SkTransformShader) encountered");
builder->addBlock(BuiltInCodeSnippetID::kError);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkTriColorShader* shader) {
SKGPU_LOG_W("Raster-only SkShader (SkTriColorShader) encountered");
builder->addBlock(BuiltInCodeSnippetID::kError);
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkWorkingColorSpaceShader* shader) {
SkASSERT(shader);
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 = shader->workingSpace();
SkColorInfo workingInfo(dstInfo.colorType(), dstAT, workingCS);
KeyContextWithColorInfo workingContext(keyContext, workingInfo);
// Compose the inner shader (in the working space) with a (working->dst) transform:
Compose(keyContext, builder, gatherer,
/* addInnerToKey= */ [&]() -> void {
AddToKey(workingContext, builder, gatherer, shader->shader().get());
},
/* addOuterToKey= */ [&]() -> void {
ColorSpaceTransformBlock::ColorSpaceTransformData data(
workingCS.get(), dstAT, dstCS.get(), dstAT);
ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
});
}
static SkBitmap create_color_and_offset_bitmap(int numStops,
const SkPMColor4f* colors,
const float* offsets) {
SkBitmap colorsAndOffsetsBitmap;
colorsAndOffsetsBitmap.allocPixels(
SkImageInfo::Make(numStops, 2, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
for (int i = 0; i < numStops; i++) {
// TODO: there should be a way to directly set a premul pixel in a bitmap with
// a premul color.
SkColor4f unpremulColor = colors[i].unpremul();
colorsAndOffsetsBitmap.erase(unpremulColor, SkIRect::MakeXYWH(i, 0, 1, 1));
float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
SkASSERT(offset >= 0.0f && offset <= 1.0f);
int exponent;
float mantissa = frexp(offset, &exponent);
SkHalf halfE = SkFloatToHalf(exponent);
if ((int)SkHalfToFloat(halfE) != exponent) {
SKGPU_LOG_W("Encoding gradient to f16 failed");
return {};
}
#if defined(SK_DEBUG)
SkHalf halfM = SkFloatToHalf(mantissa);
float restored = ldexp(SkHalfToFloat(halfM), (int)SkHalfToFloat(halfE));
float error = abs(restored - offset);
SkASSERT(error < 0.001f);
#endif
// TODO: we're only using 2 of the f16s here. The encoding could be altered to better
// preserve precision. This encoding yields < 0.001f error for 2^20 evenly spaced stops.
colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, (float)exponent, 0, 1},
SkIRect::MakeXYWH(i, 1, 1, 1));
}
return colorsAndOffsetsBitmap;
}
// Please see GrGradientShader.cpp::make_interpolated_to_dst for substantial comments
// as to why this code is structured this way.
static void make_interpolated_to_dst(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const GradientShaderBlocks::GradientData& gradData,
const SkGradientShader::Interpolation& interp,
SkColorSpace* intermediateCS) {
using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
bool inputPremul = static_cast<bool>(interp.fInPremul);
switch (interp.fColorSpace) {
case ColorSpace::kLab:
case ColorSpace::kOKLab:
case ColorSpace::kLCH:
case ColorSpace::kOKLCH:
case ColorSpace::kHSL:
case ColorSpace::kHWB:
inputPremul = false;
break;
default:
break;
}
const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
SkColorSpace* dstColorSpace =
dstColorInfo.colorSpace() ? dstColorInfo.colorSpace() : sk_srgb_singleton();
SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
ColorSpaceTransformBlock::ColorSpaceTransformData data(
intermediateCS, intermediateAlphaType, dstColorSpace, dstColorInfo.alphaType());
// The gradient block and colorSpace conversion block need to be combined
// (via the Compose block) so that the localMatrix block can treat them as
// one child.
Compose(keyContext, builder, gatherer,
/* addInnerToKey= */ [&]() -> void {
GradientShaderBlocks::AddBlock(keyContext, builder, gatherer, gradData);
},
/* addOuterToKey= */ [&]() -> void {
ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, data);
});
}
static void add_gradient_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkGradientBaseShader* shader,
SkPoint point0,
SkPoint point1,
float radius0,
float radius1,
float bias,
float scale) {
SkColor4fXformer xformedColors(shader, keyContext.dstColorInfo().colorSpace());
const SkPMColor4f* colors = xformedColors.fColors.begin();
sk_sp<TextureProxy> proxy;
if (shader->getColorCount() > GradientShaderBlocks::GradientData::kNumInternalStorageStops) {
if (shader->cachedBitmap().empty()) {
SkBitmap colorsAndOffsetsBitmap = create_color_and_offset_bitmap(
shader->getColorCount(), colors, shader->getPositions());
if (colorsAndOffsetsBitmap.empty()) {
SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
shader->setCachedBitmap(colorsAndOffsetsBitmap);
}
proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), shader->cachedBitmap());
if (!proxy) {
SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap proxy");
builder->addBlock(BuiltInCodeSnippetID::kError);
return;
}
}
GradientShaderBlocks::GradientData data(shader->asGradient(),
point0,
point1,
radius0,
radius1,
bias,
scale,
shader->getTileMode(),
shader->getColorCount(),
colors,
shader->getPositions(),
std::move(proxy),
shader->getInterpolation());
make_interpolated_to_dst(keyContext,
builder,
gatherer,
data,
shader->getInterpolation(),
xformedColors.fIntermediateColorSpace.get());
}
static void add_gradient_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkConicalGradient* shader) {
add_gradient_to_key(keyContext,
builder,
gatherer,
shader,
shader->getStartCenter(),
shader->getEndCenter(),
shader->getStartRadius(),
shader->getEndRadius(),
0.0f,
0.0f);
}
static void add_gradient_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkLinearGradient* shader) {
add_gradient_to_key(keyContext,
builder,
gatherer,
shader,
shader->start(),
shader->end(),
0.0f,
0.0f,
0.0f,
0.0f);
}
static void add_gradient_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkRadialGradient* shader) {
add_gradient_to_key(keyContext,
builder,
gatherer,
shader,
shader->center(),
{ 0.0f, 0.0f },
shader->radius(),
0.0f,
0.0f,
0.0f);
}
static void add_gradient_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkSweepGradient* shader) {
add_gradient_to_key(keyContext,
builder,
gatherer,
shader,
shader->center(),
{ 0.0f, 0.0f },
0.0f,
0.0f,
shader->tBias(),
shader->tScale());
}
static void add_to_key(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkGradientBaseShader* shader) {
SkASSERT(shader);
switch (shader->asGradient()) {
#define M(type) \
case SkShaderBase::GradientType::k##type: \
add_gradient_to_key(keyContext, \
builder, \
gatherer, \
static_cast<const Sk##type##Gradient*>(shader)); \
return;
SK_ALL_GRADIENTS(M)
#undef M
case SkShaderBase::GradientType::kNone:
SkDEBUGFAIL("Gradient shader says its type is none");
return;
}
SkUNREACHABLE;
}
void AddToKey(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const SkShader* shader) {
if (!shader) {
return;
}
switch (as_SB(shader)->type()) {
#define M(type) \
case SkShaderBase::ShaderType::k##type: \
add_to_key(keyContext, \
builder, \
gatherer, \
static_cast<const Sk##type##Shader*>(shader)); \
return;
SK_ALL_SHADERS(M)
#undef M
}
SkUNREACHABLE;
}
} // namespace skgpu::graphite