blob: d4c6d860cb398f8fd5374c781ce6986f3ed6d1e0 [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/core/SkKeyHelpers.h"
#include "include/core/SkData.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/core/SkDebugUtils.h"
#include "src/core/SkKeyContext.h"
#include "src/core/SkPaintParamsKey.h"
#include "src/core/SkPipelineData.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkShaderCodeDictionary.h"
#include "src/core/SkUniform.h"
#ifdef SK_GRAPHITE_ENABLED
#include "src/gpu/Blend.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/TextureProxy.h"
#include "src/gpu/graphite/UniformManager.h"
#endif
#define VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID) \
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, dict->getUniforms(codeSnippetID));)
constexpr SkPMColor4f kErrorColor = { 1, 0, 0, 1 };
//--------------------------------------------------------------------------------------------------
void PassthroughShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
builder->beginBlock(SkBuiltInCodeSnippetID::kPassthroughShader);
return;
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation of other backends
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_solid_uniform_data(const SkShaderCodeDictionary* dict,
const SkPMColor4f& premulColor,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kSolidColorShader)
gatherer->write(premulColor);
gatherer->addFlags(dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kSolidColorShader));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void SolidColorShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const SkPMColor4f& premulColor) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
if (gatherer) {
auto dict = keyContext.dict();
add_solid_uniform_data(dict, premulColor, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kSolidColorShader);
return;
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation of other backends
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_linear_gradient_uniform_data(const SkShaderCodeDictionary* dict,
SkBuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
int stops = codeSnippetID == SkBuiltInCodeSnippetID::kLinearGradientShader4 ? 4 : 8;
SkM44 lmInverse;
bool wasInverted = gradData.fLocalMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
gatherer->write(gradData.fColor4fs, stops);
gatherer->write(gradData.fOffsets, stops);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fPoints[1]);
gatherer->write(static_cast<int>(gradData.fTM));
gatherer->addFlags(dict->getSnippetRequirementFlags(codeSnippetID));
};
void add_radial_gradient_uniform_data(const SkShaderCodeDictionary* dict,
SkBuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
int stops = codeSnippetID == SkBuiltInCodeSnippetID::kRadialGradientShader4 ? 4 : 8;
SkM44 lmInverse;
bool wasInverted = gradData.fLocalMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
gatherer->write(gradData.fColor4fs, stops);
gatherer->write(gradData.fOffsets, stops);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fRadii[0]);
gatherer->write(static_cast<int>(gradData.fTM));
gatherer->addFlags(dict->getSnippetRequirementFlags(codeSnippetID));
};
void add_sweep_gradient_uniform_data(const SkShaderCodeDictionary* dict,
SkBuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
int stops = codeSnippetID == SkBuiltInCodeSnippetID::kSweepGradientShader4 ? 4 : 8;
SkM44 lmInverse;
bool wasInverted = gradData.fLocalMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
gatherer->write(gradData.fColor4fs, stops);
gatherer->write(gradData.fOffsets, stops);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fBias);
gatherer->write(gradData.fScale);
gatherer->write(static_cast<int>(gradData.fTM));
gatherer->addFlags(dict->getSnippetRequirementFlags(codeSnippetID));
};
void add_conical_gradient_uniform_data(const SkShaderCodeDictionary* dict,
SkBuiltInCodeSnippetID codeSnippetID,
const GradientShaderBlocks::GradientData& gradData,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, codeSnippetID)
int stops = codeSnippetID == SkBuiltInCodeSnippetID::kConicalGradientShader4 ? 4 : 8;
SkM44 lmInverse;
bool wasInverted = gradData.fLocalMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
gatherer->write(gradData.fColor4fs, stops);
gatherer->write(gradData.fOffsets, stops);
gatherer->write(gradData.fPoints[0]);
gatherer->write(gradData.fPoints[1]);
gatherer->write(gradData.fRadii[0]);
gatherer->write(gradData.fRadii[1]);
gatherer->write(static_cast<int>(gradData.fTM));
gatherer->addFlags(dict->getSnippetRequirementFlags(codeSnippetID));
};
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
GradientShaderBlocks::GradientData::GradientData(SkShader::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(fColor4fs, sizeof(fColor4fs));
sk_bzero(fOffsets, sizeof(fOffsets));
}
GradientShaderBlocks::GradientData::GradientData(SkShader::GradientType type,
const SkM44& localMatrix,
SkPoint point0, SkPoint point1,
float radius0, float radius1,
float bias, float scale,
SkTileMode tm,
int numStops,
SkColor4f* color4fs,
float* offsets)
: fType(type)
, fLocalMatrix(localMatrix)
, fBias(bias)
, fScale(scale)
, fTM(tm)
, fNumStops(std::min(numStops, kMaxStops)) {
SkASSERT(fNumStops >= 1);
fPoints[0] = point0;
fPoints[1] = point1;
fRadii[0] = radius0;
fRadii[1] = radius1;
memcpy(fColor4fs, color4fs, 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 < kMaxStops; ++i) {
fColor4fs[i] = fColor4fs[fNumStops-1];
fOffsets[i] = fOffsets[fNumStops-1];
}
}
void GradientShaderBlocks::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder *builder,
SkPipelineDataGatherer* gatherer,
const GradientData& gradData) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
SkBuiltInCodeSnippetID codeSnippetID = SkBuiltInCodeSnippetID::kSolidColorShader;
switch (gradData.fType) {
case SkShader::kLinear_GradientType:
codeSnippetID = gradData.fNumStops <= 4
? SkBuiltInCodeSnippetID::kLinearGradientShader4
: SkBuiltInCodeSnippetID::kLinearGradientShader8;
if (gatherer) {
add_linear_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
}
break;
case SkShader::kRadial_GradientType:
codeSnippetID = gradData.fNumStops <= 4
? SkBuiltInCodeSnippetID::kRadialGradientShader4
: SkBuiltInCodeSnippetID::kRadialGradientShader8;
if (gatherer) {
add_radial_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
}
break;
case SkShader::kSweep_GradientType:
codeSnippetID = gradData.fNumStops <= 4
? SkBuiltInCodeSnippetID::kSweepGradientShader4
: SkBuiltInCodeSnippetID::kSweepGradientShader8;
if (gatherer) {
add_sweep_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
}
break;
case SkShader::GradientType::kConical_GradientType:
codeSnippetID = gradData.fNumStops <= 4
? SkBuiltInCodeSnippetID::kConicalGradientShader4
: SkBuiltInCodeSnippetID::kConicalGradientShader8;
if (gatherer) {
add_conical_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
}
break;
case SkShader::GradientType::kColor_GradientType:
case SkShader::GradientType::kNone_GradientType:
default:
SkASSERT(0);
break;
}
builder->beginBlock(codeSnippetID);
return;
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation of other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_localmatrixshader_uniform_data(const SkShaderCodeDictionary* dict,
const SkM44& localMatrix,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kLocalMatrixShader)
SkM44 lmInverse;
bool wasInverted = localMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(lmInverse);
gatherer->addFlags(
dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kLocalMatrixShader));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void LocalMatrixShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const LMShaderData& lmShaderData) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
// When extracted into SkShaderInfo::SnippetEntries the children will appear after their
// parent. Thus, the parent's uniform data must appear in the uniform block before the
// uniform data of the children.
if (gatherer) {
add_localmatrixshader_uniform_data(dict, lmShaderData.fLocalMatrix, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kLocalMatrixShader);
return;
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_image_uniform_data(const SkShaderCodeDictionary* dict,
const ImageShaderBlock::ImageData& imgData,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kImageShader)
SkMatrix lmInverse;
bool wasInverted = imgData.fLocalMatrix.invert(&lmInverse); // TODO: handle failure up stack
if (!wasInverted) {
lmInverse.setIdentity();
}
gatherer->write(SkM44(lmInverse));
gatherer->write(imgData.fSubset);
gatherer->write(static_cast<int>(imgData.fTileModes[0]));
gatherer->write(static_cast<int>(imgData.fTileModes[1]));
gatherer->write(imgData.fTextureProxy->dimensions().fWidth);
gatherer->write(imgData.fTextureProxy->dimensions().fHeight);
gatherer->addFlags(dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kImageShader));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
ImageShaderBlock::ImageData::ImageData(const SkSamplingOptions& sampling,
SkTileMode tileModeX,
SkTileMode tileModeY,
SkRect subset,
const SkMatrix& localMatrix)
: fSampling(sampling)
, fTileModes{tileModeX, tileModeY}
, fSubset(subset)
, fLocalMatrix(localMatrix) {
}
void ImageShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const ImageData& imgData) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
// TODO: allow through lazy proxies
if (gatherer && !imgData.fTextureProxy) {
// We're dropping the ImageShader here. This could be an instance of trying to draw
// a raster-backed image w/ a Graphite-backed canvas.
// TODO: At some point the pre-compile path should also be creating a texture
// proxy (i.e., we can remove the 'pipelineData' in the above test).
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
return;
}
auto dict = keyContext.dict();
if (gatherer) {
gatherer->add(imgData.fSampling,
imgData.fTileModes,
imgData.fTextureProxy);
add_image_uniform_data(dict, imgData, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kImageShader);
return;
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_blendshader_uniform_data(const SkShaderCodeDictionary* dict,
SkBlendMode bm,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kBlendShader)
gatherer->write(SkTo<int>(bm));
gatherer->addFlags(dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kBlendShader));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void BlendShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder *builder,
SkPipelineDataGatherer* gatherer,
const BlendShaderData& blendData) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
// When extracted into SkShaderInfo::SnippetEntries the children will appear after their
// parent. Thus, the parent's uniform data must appear in the uniform block before the
// uniform data of the children.
if (gatherer) {
add_blendshader_uniform_data(dict, blendData.fBM, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kBlendShader);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_matrix_colorfilter_uniform_data(const SkShaderCodeDictionary* dict,
const MatrixColorFilterBlock::MatrixColorFilterData& data,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kMatrixColorFilter)
gatherer->write(data.fMatrix);
gatherer->write(data.fTranslate);
gatherer->write(static_cast<int>(data.fInHSLA));
gatherer->addFlags(
dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kMatrixColorFilter));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void MatrixColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const MatrixColorFilterData& matrixCFData) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
if (gatherer) {
add_matrix_colorfilter_uniform_data(dict, matrixCFData, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kMatrixColorFilter);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_blend_colorfilter_uniform_data(const SkShaderCodeDictionary* dict,
const BlendColorFilterBlock::BlendColorFilterData& data,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kBlendColorFilter)
gatherer->write(SkTo<int>(data.fBlendMode));
gatherer->write(data.fSrcColor);
gatherer->addFlags(dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kBlendColorFilter));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void BlendColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const BlendColorFilterData& data) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
if (gatherer) {
add_blend_colorfilter_uniform_data(dict, data, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kBlendColorFilter);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
}
}
//--------------------------------------------------------------------------------------------------
void ComposeColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
builder->beginBlock(SkBuiltInCodeSnippetID::kComposeColorFilter);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
}
}
//--------------------------------------------------------------------------------------------------
void GaussianColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
builder->beginBlock(SkBuiltInCodeSnippetID::kGaussianColorFilter);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
void add_table_colorfilter_uniform_data(const SkShaderCodeDictionary* dict,
const TableColorFilterBlock::TableColorFilterData& data,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kTableColorFilter)
gatherer->addFlags(dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kTableColorFilter));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
TableColorFilterBlock::TableColorFilterData::TableColorFilterData() {}
void TableColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const TableColorFilterData& data) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
if (gatherer) {
if (!data.fTextureProxy) {
// We're dropping the color filter here!
PassthroughShaderBlock::BeginBlock(keyContext, builder, gatherer);
return;
}
static const SkTileMode kTileModes[2] = { SkTileMode::kClamp, SkTileMode::kClamp };
gatherer->add(SkSamplingOptions(), kTileModes, data.fTextureProxy);
add_table_colorfilter_uniform_data(dict, data, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kTableColorFilter);
}
#endif // SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
}
}
//--------------------------------------------------------------------------------------------------
#ifdef SK_GRAPHITE_ENABLED
namespace {
constexpr skgpu::BlendInfo make_simple_blendInfo(skgpu::BlendCoeff srcCoeff,
skgpu::BlendCoeff dstCoeff) {
return { skgpu::BlendEquation::kAdd,
srcCoeff,
dstCoeff,
SK_PMColor4fTRANSPARENT,
skgpu::BlendModifiesDst(skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff) };
}
static constexpr int kNumCoeffModes = (int)SkBlendMode::kLastCoeffMode + 1;
/*>> No coverage, input color unknown <<*/
static constexpr skgpu::BlendInfo gBlendTable[kNumCoeffModes] = {
/* clear */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
/* src */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
/* dst */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-over */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
/* dst-over */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* src-in */ make_simple_blendInfo(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
/* dst-in */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA),
/* src-out */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
/* dst-out */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
/* src-atop */ make_simple_blendInfo(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
/* dst-atop */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA),
/* xor */ make_simple_blendInfo(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
/* plus */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
/* modulate */ make_simple_blendInfo(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
/* screen */ make_simple_blendInfo(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC)
};
const skgpu::BlendInfo& get_blend_info(SkBlendMode bm) {
if (bm <= SkBlendMode::kLastCoeffMode) {
return gBlendTable[(int) bm];
}
return gBlendTable[(int) SkBlendMode::kSrc];
}
void add_shaderbasedblender_uniform_data(const SkShaderCodeDictionary* dict,
SkBlendMode bm,
SkPipelineDataGatherer* gatherer) {
VALIDATE_UNIFORMS(gatherer, dict, SkBuiltInCodeSnippetID::kShaderBasedBlender)
gatherer->write(SkTo<int>(bm));
gatherer->addFlags(
dict->getSnippetRequirementFlags(SkBuiltInCodeSnippetID::kShaderBasedBlender));
}
} // anonymous namespace
#endif // SK_GRAPHITE_ENABLED
void BlendModeBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder *builder,
SkPipelineDataGatherer* gatherer,
SkBlendMode bm) {
#ifdef SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kGraphite) {
auto dict = keyContext.dict();
if (bm <= SkBlendMode::kLastCoeffMode) {
builder->setBlendInfo(get_blend_info(bm));
builder->beginBlock(SkBuiltInCodeSnippetID::kFixedFunctionBlender);
} else {
// TODO: set up the correct blend info
builder->setBlendInfo({});
if (gatherer) {
add_shaderbasedblender_uniform_data(dict, bm, gatherer);
}
builder->beginBlock(SkBuiltInCodeSnippetID::kShaderBasedBlender);
}
return;
}
#endif// SK_GRAPHITE_ENABLED
if (builder->backend() == SkBackend::kSkVM || builder->backend() == SkBackend::kGanesh) {
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
}
}
RuntimeShaderBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect)
: fEffect(std::move(effect)) {}
RuntimeShaderBlock::ShaderData::ShaderData(sk_sp<const SkRuntimeEffect> effect,
const SkMatrix& localMatrix,
sk_sp<const SkData> uniforms)
: fEffect(std::move(effect))
, fLocalMatrix(localMatrix)
, 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 RuntimeShaderBlock::ShaderData::operator==(const ShaderData& rhs) const {
return fEffect == rhs.fEffect &&
fLocalMatrix == rhs.fLocalMatrix &&
skdata_matches(fUniforms.get(), rhs.fUniforms.get());
}
#ifdef SK_GRAPHITE_ENABLED
static void add_effect_to_recorder(skgpu::graphite::Recorder* recorder,
int codeSnippetID,
sk_sp<const SkRuntimeEffect> effect) {
recorder->priv().resourceProvider()->runtimeEffectDictionary()->set(codeSnippetID,
std::move(effect));
}
static void gather_runtime_effect_uniforms(SkSpan<const SkRuntimeEffect::Uniform> rtsUniforms,
SkSpan<const SkUniform> graphiteUniforms,
int graphiteStartingIndex,
const SkData* uniformData,
SkPipelineDataGatherer* gatherer) {
// Collect all the other uniforms from the provided SkData.
const uint8_t* uniformBase = uniformData->bytes();
for (size_t index = 0; index < rtsUniforms.size(); ++index) {
// The runtime shader SkShaderSnippet burns index 0 on the local matrix, so adjust our index
// to compensate. (Color filters and blenders don't need any adjustment and pass zero.)
int graphiteIndex = index + graphiteStartingIndex;
const SkUniform& skUniform = graphiteUniforms[graphiteIndex];
// 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(skUniform.type(), skUniform.count(), uniformPtr);
}
}
#endif
void RuntimeShaderBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const ShaderData& shaderData) {
switch (builder->backend()) {
case SkBackend::kGraphite: {
#ifdef SK_GRAPHITE_ENABLED
// TODO(skia:13508): add support for child effects
SkShaderCodeDictionary* dict = keyContext.dict();
int codeSnippetID = dict->findOrCreateRuntimeEffectSnippet(shaderData.fEffect.get());
add_effect_to_recorder(keyContext.recorder(), codeSnippetID, shaderData.fEffect);
if (gatherer) {
const SkShaderSnippet* entry = dict->getEntry(codeSnippetID);
SkASSERT(entry);
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, entry->fUniforms);)
gatherer->addFlags(entry->fSnippetRequirementFlags);
// Pass the local matrix inverse so we can use local coordinates.
SkMatrix inverseLocalMatrix;
if (!shaderData.fLocalMatrix.invert(&inverseLocalMatrix)) {
inverseLocalMatrix.setIdentity();
}
gatherer->write(SkM44(inverseLocalMatrix));
gather_runtime_effect_uniforms(shaderData.fEffect->uniforms(),
entry->fUniforms,
/*graphiteStartingIndex=*/1,
shaderData.fUniforms.get(),
gatherer);
}
builder->beginBlock(codeSnippetID);
#endif // SK_GRAPHITE_ENABLED
break;
}
case SkBackend::kSkVM:
case SkBackend::kGanesh:
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
break;
}
}
RuntimeColorFilterBlock::ColorFilterData::ColorFilterData(sk_sp<const SkRuntimeEffect> effect)
: fEffect(std::move(effect)) {}
RuntimeColorFilterBlock::ColorFilterData::ColorFilterData(sk_sp<const SkRuntimeEffect> effect,
sk_sp<const SkData> uniforms)
: fEffect(std::move(effect))
, fUniforms(std::move(uniforms)) {}
bool RuntimeColorFilterBlock::ColorFilterData::operator==(const ColorFilterData& rhs) const {
return fEffect == rhs.fEffect &&
skdata_matches(fUniforms.get(), rhs.fUniforms.get());
}
void RuntimeColorFilterBlock::BeginBlock(const SkKeyContext& keyContext,
SkPaintParamsKeyBuilder* builder,
SkPipelineDataGatherer* gatherer,
const ColorFilterData& filterData) {
switch (builder->backend()) {
case SkBackend::kGraphite: {
#ifdef SK_GRAPHITE_ENABLED
SkShaderCodeDictionary* dict = keyContext.dict();
int codeSnippetID = dict->findOrCreateRuntimeEffectSnippet(filterData.fEffect.get());
add_effect_to_recorder(keyContext.recorder(), codeSnippetID, filterData.fEffect);
if (gatherer) {
const SkShaderSnippet* entry = dict->getEntry(codeSnippetID);
SkASSERT(entry);
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, entry->fUniforms);)
gatherer->addFlags(entry->fSnippetRequirementFlags);
gather_runtime_effect_uniforms(filterData.fEffect->uniforms(),
entry->fUniforms,
/*graphiteStartingIndex=*/0,
filterData.fUniforms.get(),
gatherer);
}
builder->beginBlock(codeSnippetID);
#endif // SK_GRAPHITE_ENABLED
break;
}
case SkBackend::kSkVM:
case SkBackend::kGanesh:
// TODO: add implementation for other backends
SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, kErrorColor);
break;
}
}