blob: 649088793e5fa5d1b5bacd1e8a88bd4eb7666aa9 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#if defined(SK_GRAPHITE)
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkM44.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPathBuilder.h"
#include "include/core/SkShader.h"
#include "include/core/SkTextBlob.h"
#include "include/core/SkVertices.h"
#include "include/effects/SkColorMatrix.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/Recorder.h"
#include "src/base/SkRandom.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/FactoryFunctions.h"
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/PaintOptionsPriv.h"
#include "src/gpu/graphite/PaintParams.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/Precompile.h"
#include "src/gpu/graphite/PublicPrecompile.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/UniquePaintParamsID.h"
#include "src/shaders/SkImageShader.h"
#include "tools/ToolUtils.h"
using namespace skgpu::graphite;
namespace {
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_random_shader(SkRandom*, Recorder*);
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_random_blender(SkRandom*);
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_random_colorfilter(SkRandom*);
enum class ShaderType {
kNone,
kSolidColor,
kLinearGradient,
kRadialGradient,
kSweepGradient,
kConicalGradient,
kLocalMatrix,
kColorFilter,
kImage,
kBlend,
kLast = kBlend
};
static constexpr int kShaderTypeCount = static_cast<int>(ShaderType::kLast) + 1;
// TODO: do we need to add a separable category and/or a category for dstRead requiring blends?
enum class BlenderType {
kNone,
kPorterDuff,
kShaderBased,
kRuntime,
kLast = kRuntime
};
static constexpr int kBlenderTypeCount = static_cast<int>(BlenderType::kLast) + 1;
enum class ColorFilterType {
kNone,
kBlend,
kMatrix,
kHSLAMatrix,
// TODO: add more color filters
kLast = kHSLAMatrix
};
static constexpr int kColorFilterTypeCount = static_cast<int>(ColorFilterType::kLast) + 1;
static constexpr skcms_TransferFunction gTransferFunctions[] = {
SkNamedTransferFn::kSRGB,
SkNamedTransferFn::k2Dot2,
SkNamedTransferFn::kLinear,
SkNamedTransferFn::kRec2020,
SkNamedTransferFn::kPQ,
SkNamedTransferFn::kHLG,
};
static constexpr int kTransferFunctionCount = std::size(gTransferFunctions);
static constexpr skcms_Matrix3x3 gGamuts[] = {
SkNamedGamut::kSRGB,
SkNamedGamut::kAdobeRGB,
SkNamedGamut::kDisplayP3,
SkNamedGamut::kRec2020,
SkNamedGamut::kXYZ,
};
static constexpr int kGamutCount = std::size(gGamuts);
enum class ColorSpaceType {
kNone,
kSRGB,
kSRGBLinear,
kRGB,
kLast = kRGB
};
static constexpr int kColorSpaceTypeCount = static_cast<int>(ColorSpaceType::kLast) + 1;
ColorSpaceType random_colorspacetype(SkRandom* rand) {
return static_cast<ColorSpaceType>(rand->nextULessThan(kColorSpaceTypeCount));
}
sk_sp<SkColorSpace> random_colorspace(SkRandom* rand) {
ColorSpaceType cs = random_colorspacetype(rand);
switch (cs) {
case ColorSpaceType::kNone:
return nullptr;
case ColorSpaceType::kSRGB:
return SkColorSpace::MakeSRGB();
case ColorSpaceType::kSRGBLinear:
return SkColorSpace::MakeSRGBLinear();
case ColorSpaceType::kRGB:
return SkColorSpace::MakeRGB(
gTransferFunctions[rand->nextULessThan(kTransferFunctionCount)],
gGamuts[rand->nextULessThan(kGamutCount)]);
}
SkUNREACHABLE;
}
SkColor random_opaque_color(SkRandom* rand) {
return 0xff000000 | rand->nextU();
}
SkColor4f random_color(SkRandom* rand) {
SkColor4f result = { rand->nextRangeF(0.0f, 1.0f),
rand->nextRangeF(0.0f, 1.0f),
rand->nextRangeF(0.0f, 1.0f),
rand->nextRangeF(0.0f, 1.0f) };
if (rand->nextBool()) {
result.fA = 1.0f;
}
return result;
}
SkTileMode random_tilemode(SkRandom* rand) {
return static_cast<SkTileMode>(rand->nextULessThan(kSkTileModeCount));
}
ShaderType random_shadertype(SkRandom* rand) {
return static_cast<ShaderType>(rand->nextULessThan(kShaderTypeCount));
}
SkBlendMode random_porter_duff_bm(SkRandom* rand) {
return static_cast<SkBlendMode>(rand->nextRangeU((unsigned int) SkBlendMode::kClear,
(unsigned int) SkBlendMode::kLastCoeffMode));
}
SkBlendMode random_complex_bm(SkRandom* rand) {
return static_cast<SkBlendMode>(rand->nextRangeU((unsigned int) SkBlendMode::kLastCoeffMode,
(unsigned int) SkBlendMode::kLastMode));
}
SkBlendMode random_blend_mode(SkRandom* rand) {
return static_cast<SkBlendMode>(rand->nextULessThan(kSkBlendModeCount));
}
BlenderType random_blendertype(SkRandom* rand) {
return static_cast<BlenderType>(rand->nextULessThan(kBlenderTypeCount));
}
ColorFilterType random_colorfiltertype(SkRandom* rand) {
return static_cast<ColorFilterType>(rand->nextULessThan(kColorFilterTypeCount));
}
sk_sp<SkImage> make_image(SkRandom* rand, Recorder* recorder) {
// TODO: add alpha-only images too
SkImageInfo info = SkImageInfo::Make(32, 32,
SkColorType::kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
random_colorspace(rand));
SkBitmap bitmap;
bitmap.allocPixels(info);
bitmap.eraseColor(SK_ColorBLACK);
sk_sp<SkImage> img = bitmap.asImage();
// TODO: fuzz mipmappedness
return img->makeTextureImage(recorder, { skgpu::Mipmapped::kNo });
}
//--------------------------------------------------------------------------------------------------
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_solid_shader(SkRandom* rand) {
sk_sp<SkShader> s = SkShaders::Color(random_opaque_color(rand));
sk_sp<PrecompileShader> o = PrecompileShaders::Color();
return { s, o };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_gradient_shader(
SkRandom* rand,
SkShaderBase::GradientType type) {
// TODO: fuzz the gradient parameters - esp. the number of stops & hard stops
SkPoint pts[2] = {{-100, -100},
{100, 100}};
SkColor colors[2] = {SK_ColorRED, SK_ColorGREEN};
SkScalar offsets[2] = {0.0f, 1.0f};
sk_sp<SkShader> s;
sk_sp<PrecompileShader> o;
SkTileMode tm = random_tilemode(rand);
switch (type) {
case SkShaderBase::GradientType::kLinear:
s = SkGradientShader::MakeLinear(pts, colors, offsets, 2, tm);
o = PrecompileShaders::LinearGradient();
break;
case SkShaderBase::GradientType::kRadial:
s = SkGradientShader::MakeRadial({0, 0}, 100, colors, offsets, 2, tm);
o = PrecompileShaders::RadialGradient();
break;
case SkShaderBase::GradientType::kSweep:
s = SkGradientShader::MakeSweep(0, 0, colors, offsets, 2, tm,
0, 359, 0, nullptr);
o = PrecompileShaders::SweepGradient();
break;
case SkShaderBase::GradientType::kConical:
s = SkGradientShader::MakeTwoPointConical({100, 100}, 100,
{-100, -100}, 100,
colors, offsets, 2, tm);
o = PrecompileShaders::TwoPointConicalGradient();
break;
case SkShaderBase::GradientType::kNone:
case SkShaderBase::GradientType::kColor:
SkASSERT(0);
break;
}
return { s, o };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_localmatrix_shader(SkRandom* rand,
Recorder* recorder) {
auto [s, o] = create_random_shader(rand, recorder);
SkASSERT(!s == !o);
if (!s) {
return { nullptr, nullptr };
}
SkMatrix tmp = SkMatrix::Scale(1.5f, 2.0f); // TODO: fuzz
return { s->makeWithLocalMatrix(tmp), o->makeWithLocalMatrix() };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_colorfilter_shader(SkRandom* rand,
Recorder* recorder) {
auto [s, o] = create_random_shader(rand, recorder);
SkASSERT(!s == !o);
if (!s) {
return { nullptr, nullptr };
}
auto [cf, cfO] = create_random_colorfilter(rand);
return { s->makeWithColorFilter(std::move(cf)), o->makeWithColorFilter(std::move(cfO)) };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_image_shader(SkRandom* rand,
Recorder* recorder) {
SkTileMode tmX = random_tilemode(rand);
SkTileMode tmY = random_tilemode(rand);
sk_sp<SkShader> s = SkImageShader::Make(make_image(rand, recorder), tmX, tmY,
SkSamplingOptions(), nullptr);
sk_sp<PrecompileShader> o = PrecompileShaders::Image();
return { s, o };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_blend_shader(SkRandom* rand,
Recorder* recorder) {
// TODO: add explicit testing of the kClear, kDst and kSrc blend modes since they short
// circuit creation of a true blend shader (i.e., in SkShaders::Blend).
auto [blender, blenderO] = create_random_blender(rand);
auto [dstS, dstO] = create_random_shader(rand, recorder);
SkASSERT(!dstS == !dstO);
if (!dstS) {
return { nullptr, nullptr };
}
auto [srcS, srcO] = create_random_shader(rand, recorder);
SkASSERT(!srcS == !srcO);
if (!srcS) {
return { nullptr, nullptr };
}
auto s = SkShaders::Blend(std::move(blender), std::move(dstS), std::move(srcS));
auto o = PrecompileShaders::Blend(SkSpan<const sk_sp<PrecompileBlender>>({ blenderO }),
{ dstO }, { srcO });
return { s, o };
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_shader(SkRandom* rand,
Recorder* recorder,
ShaderType shaderType) {
switch (shaderType) {
case ShaderType::kNone:
return { nullptr, nullptr };
case ShaderType::kSolidColor:
return create_solid_shader(rand);
case ShaderType::kLinearGradient:
return create_gradient_shader(rand, SkShaderBase::GradientType::kLinear);
case ShaderType::kRadialGradient:
return create_gradient_shader(rand, SkShaderBase::GradientType::kRadial);
case ShaderType::kSweepGradient:
return create_gradient_shader(rand, SkShaderBase::GradientType::kSweep);
case ShaderType::kConicalGradient:
return create_gradient_shader(rand, SkShaderBase::GradientType::kConical);
case ShaderType::kLocalMatrix:
return create_localmatrix_shader(rand, recorder);
case ShaderType::kColorFilter:
return create_colorfilter_shader(rand, recorder);
case ShaderType::kImage:
return create_image_shader(rand, recorder);
case ShaderType::kBlend:
return create_blend_shader(rand, recorder);
}
SkUNREACHABLE;
}
std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_random_shader(SkRandom* rand,
Recorder* recorder) {
return create_shader(rand, recorder, random_shadertype(rand));
}
//--------------------------------------------------------------------------------------------------
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> src_blender() {
static SkRuntimeEffect* sSrcEffect = SkMakeRuntimeEffect(
SkRuntimeEffect::MakeForBlender,
"half4 main(half4 src, half4 dst) {"
"return src;"
"}"
);
sk_sp<SkBlender> b = sSrcEffect->makeBlender(/* uniforms= */ nullptr);
sk_sp<PrecompileBlender> o = MakePrecompileBlender(sk_ref_sp(sSrcEffect));
return { b , o };
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> dest_blender() {
static SkRuntimeEffect* sDestEffect = SkMakeRuntimeEffect(
SkRuntimeEffect::MakeForBlender,
"half4 main(half4 src, half4 dst) {"
"return dst;"
"}"
);
sk_sp<SkBlender> b = sDestEffect->makeBlender(/* uniforms= */ nullptr);
sk_sp<PrecompileBlender> o = MakePrecompileBlender(sk_ref_sp(sDestEffect));
return { b , o };
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> combo_blender() {
static SkRuntimeEffect* sComboEffect = SkMakeRuntimeEffect(
SkRuntimeEffect::MakeForBlender,
"uniform float blendFrac;"
"uniform blender a;"
"uniform blender b;"
"half4 main(half4 src, half4 dst) {"
"return (blendFrac * a.eval(src, dst)) + ((1 - blendFrac) * b.eval(src, dst));"
"}"
);
auto [src, srcO] = src_blender();
auto [dst, dstO] = dest_blender();
SkRuntimeEffect::ChildPtr children[] = { src, dst };
const PrecompileChildPtr childOptions[] = { srcO, dstO };
const float kUniforms[] = { 1.0f };
sk_sp<SkBlender> b = sComboEffect->makeBlender(SkData::MakeWithCopy(kUniforms,
sizeof(kUniforms)),
children);
sk_sp<PrecompileBlender> o = MakePrecompileBlender(sk_ref_sp(sComboEffect), { childOptions });
return { b , o };
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_bm_blender(SkRandom* rand,
SkBlendMode bm) {
return { SkBlender::Mode(bm), PrecompileBlender::Mode(bm) };
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_rt_blender(SkRandom* rand) {
int option = rand->nextULessThan(3);
switch (option) {
case 0: return src_blender();
case 1: return dest_blender();
case 2: return combo_blender();
}
return { nullptr, nullptr };
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_blender(SkRandom* rand,
BlenderType type) {
switch (type) {
case BlenderType::kNone:
return { nullptr, nullptr };
case BlenderType::kPorterDuff:
return create_bm_blender(rand, random_porter_duff_bm(rand));
case BlenderType::kShaderBased:
return create_bm_blender(rand, random_complex_bm(rand));
case BlenderType::kRuntime:
return create_rt_blender(rand);
}
SkUNREACHABLE;
}
std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_random_blender(SkRandom* rand) {
return create_blender(rand, random_blendertype(rand));
}
//--------------------------------------------------------------------------------------------------
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_blend_colorfilter(
SkRandom* rand) {
sk_sp<SkColorFilter> cf;
// SkColorFilters::Blend is clever and can weed out noop color filters. Loop until we get
// a valid color filter.
while (!cf) {
cf = SkColorFilters::Blend(random_color(rand),
random_colorspace(rand),
random_blend_mode(rand));
}
sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Blend();
return { cf, o };
}
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_matrix_colorfilter() {
sk_sp<SkColorFilter> cf = SkColorFilters::Matrix(
SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Matrix();
return { cf, o };
}
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_hsla_matrix_colorfilter() {
sk_sp<SkColorFilter> cf = SkColorFilters::HSLAMatrix(
SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace));
sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::HSLAMatrix();
return { cf, o };
}
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_colorfilter(
SkRandom* rand,
ColorFilterType type) {
switch (type) {
case ColorFilterType::kNone:
return { nullptr, nullptr };
case ColorFilterType::kBlend:
return create_blend_colorfilter(rand);
case ColorFilterType::kMatrix:
return create_matrix_colorfilter();
case ColorFilterType::kHSLAMatrix:
return create_hsla_matrix_colorfilter();
}
SkUNREACHABLE;
}
std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_random_colorfilter(
SkRandom* rand) {
return create_colorfilter(rand, random_colorfiltertype(rand));
}
//--------------------------------------------------------------------------------------------------
std::pair<SkPaint, PaintOptions> create_paint(SkRandom* rand,
Recorder* recorder,
ShaderType shaderType,
BlenderType blenderType,
ColorFilterType colorFilterType) {
SkPaint paint;
paint.setColor(random_opaque_color(rand));
PaintOptions paintOptions;
{
auto [s, o] = create_shader(rand, recorder, shaderType);
SkASSERT(!s == !o);
if (s) {
paint.setShader(std::move(s));
paintOptions.setShaders({o});
}
}
{
auto [cf, o] = create_colorfilter(rand, colorFilterType);
SkASSERT(!cf == !o);
if (cf) {
paint.setColorFilter(std::move(cf));
paintOptions.setColorFilters({o});
}
}
{
auto [b, o] = create_blender(rand, blenderType);
SkASSERT(!b == !o);
if (b) {
paint.setBlender(std::move(b));
paintOptions.setBlenders({o});
}
}
return { paint, paintOptions };
}
#ifdef SK_DEBUG
void dump(ShaderCodeDictionary* dict, UniquePaintParamsID id) {
auto entry = dict->lookup(id);
entry->paintParamsKey().dump(dict);
}
#endif
SkPath make_path() {
SkPathBuilder path;
path.moveTo(0, 0);
path.lineTo(8, 2);
path.lineTo(16, 0);
path.lineTo(14, 8);
path.lineTo(16, 16);
path.lineTo(8, 14);
path.lineTo(0, 16);
path.lineTo(2, 8);
path.close();
return path.detach();
}
struct DrawData {
SkPath fPath;
sk_sp<SkTextBlob> fBlob;
sk_sp<SkVertices> fVerts;
};
void check_draw(skiatest::Reporter* reporter,
Context* context,
Recorder* recorder,
const SkPaint& paint,
DrawTypeFlags dt,
const DrawData& drawData) {
int before = context->priv().globalCache()->numGraphicsPipelines();
{
// TODO: vary the colorType of the target surface too
SkImageInfo ii = SkImageInfo::Make(16, 16,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
sk_sp<SkSurface> surf = SkSurface::MakeGraphite(recorder, ii);
SkCanvas* canvas = surf->getCanvas();
switch (dt) {
case DrawTypeFlags::kShape:
canvas->drawRect(SkRect::MakeWH(16, 16), paint);
canvas->drawPath(drawData.fPath, paint);
break;
case DrawTypeFlags::kText:
canvas->drawTextBlob(drawData.fBlob, 0, 16, paint);
break;
case DrawTypeFlags::kDrawVertices:
canvas->drawVertices(drawData.fVerts, SkBlendMode::kDst, paint);
break;
default:
SkASSERT(false);
break;
}
std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
context->insertRecording({ recording.get() });
context->submit(SyncToCpu::kYes);
}
int after = context->priv().globalCache()->numGraphicsPipelines();
// Actually using the SkPaint with the specified type of draw shouldn't have caused
// any additional compilation
REPORTER_ASSERT(reporter, before == after);
}
} // anonymous namespace
// This is intended to be a smoke test for the agreement between the two ways of creating a
// PaintParamsKey:
// via ExtractPaintData (i.e., from an SkPaint)
// and via the pre-compilation system
//
// TODO: keep this as a smoke test but add a fuzzer that reuses all the helpers
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(PaintParamsKeyTest, reporter, context) {
auto recorder = context->makeRecorder();
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
SkColorInfo ci = SkColorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType,
SkColorSpace::MakeSRGB());
KeyContext extractPaintKeyContext(recorder.get(), {}, ci, SkColors::kBlack);
std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
KeyContext precompileKeyContext(dict, rtDict.get(), ci);
SkFont font(ToolUtils::create_portable_typeface(), 16);
const char text[] = "hambur";
// TODO: add a drawVertices call w/o colors. That impacts whether the RenderSteps emit
// a primitive color blender
constexpr int kNumVerts = 4;
constexpr SkPoint kPositions[kNumVerts] { {0,0}, {0,16}, {16,16}, {16,0} };
constexpr SkColor kColors[kNumVerts] = { SK_ColorBLUE, SK_ColorGREEN,
SK_ColorCYAN, SK_ColorYELLOW };
DrawData drawData = {
make_path(),
SkTextBlob::MakeFromText(text, strlen(text), font),
SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, kNumVerts,
kPositions, kPositions, kColors),
};
SkRandom rand;
PaintParamsKeyBuilder builder(dict);
PipelineDataGatherer gatherer(Layout::kMetal);
for (auto s : { ShaderType::kNone,
ShaderType::kSolidColor,
ShaderType::kLinearGradient,
ShaderType::kRadialGradient,
ShaderType::kSweepGradient,
ShaderType::kConicalGradient,
ShaderType::kLocalMatrix,
ShaderType::kImage,
ShaderType::kBlend }) {
for (auto bm : { BlenderType::kNone,
BlenderType::kPorterDuff,
BlenderType::kShaderBased,
BlenderType::kRuntime }) {
for (auto cf : { ColorFilterType::kNone,
ColorFilterType::kBlend,
ColorFilterType::kMatrix,
ColorFilterType::kHSLAMatrix }) {
auto [paint, paintOptions] = create_paint(&rand, recorder.get(), s, bm, cf);
for (auto dt : { DrawTypeFlags::kShape,
DrawTypeFlags::kText,
DrawTypeFlags::kDrawVertices }) {
for (bool withPrimitiveBlender : { false, true }) {
sk_sp<SkBlender> primitiveBlender;
if (withPrimitiveBlender) {
if (dt != DrawTypeFlags::kDrawVertices) {
// Only drawVertices calls need a primitive blender
continue;
}
primitiveBlender = SkBlender::Mode(SkBlendMode::kSrcOver);
}
auto [paintID, uData, tData] = ExtractPaintData(
recorder.get(), &gatherer, &builder, Layout::kMetal, {},
PaintParams(paint,
std::move(primitiveBlender),
/* skipColorXform= */ false),
extractPaintKeyContext.dstColorInfo());
std::vector<UniquePaintParamsID> precompileIDs;
paintOptions.priv().buildCombinations(precompileKeyContext,
withPrimitiveBlender,
[&](UniquePaintParamsID id) {
precompileIDs.push_back(id);
});
// The specific key generated by ExtractPaintData should be one of the
// combinations generated by the combination system.
auto result = std::find(precompileIDs.begin(), precompileIDs.end(),
paintID);
#ifdef SK_DEBUG
if (result == precompileIDs.end()) {
SkDebugf("From paint: ");
dump(dict, paintID);
SkDebugf("From combination builder:");
for (auto iter : precompileIDs) {
dump(dict, iter);
}
}
#endif
REPORTER_ASSERT(reporter, result != precompileIDs.end());
{
context->priv().globalCache()->resetGraphicsPipelines();
int before = context->priv().globalCache()->numGraphicsPipelines();
Precompile(context, paintOptions, dt);
int after = context->priv().globalCache()->numGraphicsPipelines();
REPORTER_ASSERT(reporter, before == 0);
REPORTER_ASSERT(reporter, after > before);
check_draw(reporter, context, recorder.get(), paint, dt, drawData);
}
}
}
}
}
}
}
#endif // SK_GRAPHITE