759 lines
27 KiB
C++
759 lines
27 KiB
C++
/*
|
|
* 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);
|
|
|
|
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
|