/**
* True if the processor's output is a modulation of its input color or alpha with a computed
- * color or alpha in the 0..1 range. If true and the blend mode allows it we may fold coverage
- * into the first color fragment processor's input.
+ * premultiplied color or alpha in the 0..1 range. If true and the blend mode allows it we may
+ * fold coverage into the first color fragment processor's input.
*/
bool modulatesInput() const { return SkToBool(fFlags & kModulatesInput_OptimizationFlag); }
friend class GrMSAAPathRenderer; // for access to addDrawOp
friend class GrStencilAndCoverPathRenderer; // for access to addDrawOp
friend class GrTessellatingPathRenderer; // for access to addDrawOp
+ // for a unit test
+ friend void test_draw_op(GrRenderTargetContext*, sk_sp<GrFragmentProcessor>, GrTexture*);
void internalClear(const GrFixedClip&, const GrColor, bool canIgnoreClip);
sk_sp<GrFragmentProcessor> GrAlphaThresholdFragmentProcessor::TestCreate(GrProcessorTestData* d) {
GrTexture* bmpTex = d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx];
GrTexture* maskTex = d->fTextures[GrProcessorUnitTest::kAlphaTextureIdx];
- float innerThresh = d->fRandom->nextUScalar1();
- float outerThresh = d->fRandom->nextUScalar1();
+ // Make the inner and outer thresholds be in (0, 1) exclusive and be sorted correctly.
+ float innerThresh = d->fRandom->nextUScalar1() * .99f + 0.005f;
+ float outerThresh = d->fRandom->nextUScalar1() * .99f + 0.005f;
+ if (innerThresh > outerThresh) {
+ SkTSwap(innerThresh, outerThresh);
+ }
const int kMaxWidth = 1000;
const int kMaxHeight = 1000;
uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
const SkImageFilter::CropRect* cropRect) {
innerThreshold = pin_0_1(innerThreshold);
outerThreshold = pin_0_1(outerThreshold);
+ if (innerThreshold > outerThreshold) {
+ return nullptr;
+ }
if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) {
return nullptr;
}
#if GR_TEST_UTILS
GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
- fColorCount = random->nextRangeU(1, kMaxRandomGradientColors);
+ // Set color count to min of 2 so that we don't trigger the const color optimization and make
+ // a non-gradient processor.
+ fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
fUseColors4f = random->nextBool();
// if one color, omit stops, otherwise randomly decide whether or not to
the gradient factory. (The constructor may decide not to use stops, in which case fStops
will be nullptr). */
struct RandomGradientParams {
- static const int kMaxRandomGradientColors = 4;
+ static const int kMaxRandomGradientColors = 5;
RandomGradientParams(SkRandom* r);
#if GR_TEST_UTILS
sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) {
- SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius = d->fRandom->nextUScalar1();
-
- RandomGradientParams params(d->fRandom);
- auto shader = params.fUseColors4f ?
- SkGradientShader::MakeRadial(center, radius, params.fColors4f, params.fColorSpace,
- params.fStops, params.fColorCount, params.fTileMode) :
- SkGradientShader::MakeRadial(center, radius, params.fColors,
- params.fStops, params.fColorCount, params.fTileMode);
+ sk_sp<SkShader> shader;
+ do {
+ RandomGradientParams params(d->fRandom);
+ SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+ SkScalar radius = d->fRandom->nextUScalar1();
+ shader = params.fUseColors4f
+ ? SkGradientShader::MakeRadial(center, radius, params.fColors4f,
+ params.fColorSpace, params.fStops,
+ params.fColorCount, params.fTileMode)
+ : SkGradientShader::MakeRadial(center, radius, params.fColors,
+ params.fStops, params.fColorCount,
+ params.fTileMode);
+ } while (!shader);
GrTest::TestAsFPArgs asFPArgs(d);
sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
GrAlwaysAssert(fp);
const GrSwizzle& swizzle,
PMConversion pmConversion,
const SkMatrix& matrix)
- : INHERITED(texture, nullptr, matrix, ModulationFlags(texture->config()))
+ : INHERITED(texture, nullptr, matrix, kNone_OptimizationFlags)
, fSwizzle(swizzle)
, fPMConversion(pmConversion) {
this->initClassID<GrConfigConversionEffect>();
}
GrConfigConversionEffect::GrConfigConversionEffect(GrContext* context,
- sk_sp<GrTextureProxy> proxy,
+ sk_sp<GrTextureProxy>
+ proxy,
const GrSwizzle& swizzle,
PMConversion pmConversion,
const SkMatrix& matrix)
- : INHERITED(context, ModulationFlags(proxy->config()), proxy, nullptr, matrix)
- , fSwizzle(swizzle)
- , fPMConversion(pmConversion) {
+ : INHERITED(context, kNone_OptimizationFlags, proxy, nullptr, matrix)
+ , fSwizzle(swizzle)
+ , fPMConversion(pmConversion) {
this->initClassID<GrConfigConversionEffect>();
// We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
// conversion.
static OptimizationFlags OptFlags(const GrFragmentProcessor* src,
const GrFragmentProcessor* dst, SkBlendMode mode) {
// We only attempt the constant output optimization.
- // The CPU and GPU implementations differ significantly for the advanced modes.
- if (mode <= SkBlendMode::kLastSeparableMode && src->hasConstantOutputForConstantInput() &&
- dst->hasConstantOutputForConstantInput()) {
+ // The CPU and GPU implementations differ significantly for the advanced modes and
+ // softlight.
+ if (mode <= SkBlendMode::kLastSeparableMode && mode != SkBlendMode::kSoftLight &&
+ src->hasConstantOutputForConstantInput() && dst->hasConstantOutputForConstantInput()) {
return kConstantOutputForConstantInput_OptimizationFlag;
}
return kNone_OptimizationFlags;
private:
OptimizationFlags OptFlags(const GrFragmentProcessor* child, SkBlendMode mode) {
// We only attempt the constant output optimization.
- // The CPU and GPU implementations differ significantly for the advanced modes.
- if (mode <= SkBlendMode::kLastSeparableMode && child->hasConstantOutputForConstantInput()) {
+ // The CPU and GPU implementations differ significantly for the advanced modes and
+ // softlight.
+ if (mode <= SkBlendMode::kLastSeparableMode && mode != SkBlendMode::kSoftLight &&
+ child->hasConstantOutputForConstantInput()) {
return kConstantOutputForConstantInput_OptimizationFlag;
}
return kNone_OptimizationFlags;
SkXfermode::Coeff srcCoeff, dstCoeff;
if (SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff)) {
+ // The only coeff mode that can go out of range is plus.
+ bool clamp = mode == SkBlendMode::kPlus;
+
fsBuilder->codeAppendf("%s = ", outColor);
+ if (clamp) {
+ fsBuilder->codeAppend("clamp(");
+ }
// append src blend
bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
false);
if(!append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor, didAppend)) {
fsBuilder->codeAppend("vec4(0, 0, 0, 0)");
}
+ if (clamp) {
+ fsBuilder->codeAppend(", 0, 1);");
+ }
fsBuilder->codeAppend(";");
} else {
emit_advanced_xfermode_code(fsBuilder, srcColor, dstColor, outColor, mode);
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrGpuResource.h"
+#include "GrPipelineBuilder.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetContextPriv.h"
#include "GrResourceProvider.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "ops/GrNonAAFillRectOp.h"
#include "ops/GrTestMeshDrawOp.h"
namespace {
static GrColor4f texel_color4f(int i, int j) { return GrColor4f::FromGrColor(texel_color(i, j)); }
+void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp,
+ GrTexture* inputDataTexture) {
+ GrPaint paint;
+ paint.addColorTextureProcessor(inputDataTexture, nullptr, SkMatrix::I());
+ paint.addColorFragmentProcessor(std::move(fp));
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ GrPipelineBuilder pb(std::move(paint), GrAAType::kNone);
+ auto op =
+ GrNonAAFillRectOp::Make(GrColor_WHITE, SkMatrix::I(),
+ SkRect::MakeWH(rtc->width(), rtc->height()), nullptr, nullptr);
+ rtc->addDrawOp(pb, GrNoClip(), std::move(op));
+}
+
#if GR_TEST_UTILS
-DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) {
- // This tests code under development but not used in skia lib. Leaving this disabled until
- // some platform-specific issues are addressed.
- if (1) {
- return;
- }
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
using FPFactory = GrProcessorTestFactory<GrFragmentProcessor>;
SkRandom random;
GrTexture* textures[] = {tex0.get(), tex1.get()};
GrProcessorTestData testData(&random, context, rtc.get(), textures);
- std::unique_ptr<GrColor> data(new GrColor[256 * 256]);
+ std::unique_ptr<GrColor[]> data(new GrColor[256 * 256]);
for (int y = 0; y < 256; ++y) {
for (int x = 0; x < 256; ++x) {
data.get()[256 * y + x] = texel_color(x, y);
!fp->modulatesInput()) {
continue;
}
- GrPaint paint;
- paint.addColorTextureProcessor(dataTexture.get(), nullptr, SkMatrix::I());
- paint.addColorFragmentProcessor(fp);
- paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
- SkRect::MakeWH(256.f, 256.f));
+ test_draw_op(rtc.get(), fp, dataTexture.get());
memset(data.get(), 0x0, sizeof(GrColor) * 256 * 256);
rtc->readPixels(
SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
float gDiff = fabsf(output4f.fRGBA[1] - expected4f.fRGBA[1]);
float bDiff = fabsf(output4f.fRGBA[2] - expected4f.fRGBA[2]);
float aDiff = fabsf(output4f.fRGBA[3] - expected4f.fRGBA[3]);
- static constexpr float kTol = 3 / 255.f;
+ static constexpr float kTol = 4 / 255.f;
if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) {
ERRORF(reporter,
"Processor %s claimed output for const input doesn't match "
- "actual output.",
- fp->name());
+ "actual output. Error: %f, Tolerance: %f, input: (%f, %f, %f, "
+ "%f), actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f)",
+ fp->name(), SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))),
+ kTol, input4f.fRGBA[0], input4f.fRGBA[1], input4f.fRGBA[2],
+ input4f.fRGBA[3], output4f.fRGBA[0], output4f.fRGBA[1],
+ output4f.fRGBA[2], output4f.fRGBA[3], expected4f.fRGBA[0],
+ expected4f.fRGBA[1], expected4f.fRGBA[2], expected4f.fRGBA[3]);
passing = false;
}
}