Handle F16Norm clamping in SkPaint->GrPaint conversion.
Previously this clamping was inserted by the program builder.
Adds GrSaturateProcessor to handle saturating in the fragment shader.
Clamp the GrPaint color rather than using a fp when possible.
Has to be plumbed through GrTextureOp because that skips SkPaint
conversion.
Removes a usage of GrPixelConfig.
Bug: skia:6718
Change-Id: Ifa6544496d34677f17e797433e6ef3a97be5c2b2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/242558
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 251914c..d7a0c57 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -57,7 +57,7 @@
* manually adjusted.
*/
#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-static const int kFPFactoryCount = 36;
+static const int kFPFactoryCount = 37;
static const int kGPFactoryCount = 14;
static const int kXPFactoryCount = 4;
#else
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index 39de0fc..326c674 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -77,14 +77,12 @@
kDefaultGeoProc_ClassID,
kDIEllipseGeometryProcessor_ClassID,
kDisableColorXP_ClassID,
- kTwoPointConicalEffect_ClassID,
kEllipseGeometryProcessor_ClassID,
kEllipticalRRectEffect_ClassID,
kGP_ClassID,
kVertexColorSpaceBenchGP_ClassID,
kGrAARectEffect_ClassID,
kGrAlphaThresholdFragmentProcessor_ClassID,
- kGrArithmeticFP_ClassID,
kGrBicubicEffect_ClassID,
kGrBitmapTextGeoProc_ClassID,
kGrBlurredEdgeFragmentProcessor_ClassID,
@@ -107,16 +105,12 @@
kGrDistanceFieldA8TextGeoProc_ClassID,
kGrDistanceFieldLCDTextGeoProc_ClassID,
kGrDistanceFieldPathGeoProc_ClassID,
- kGrDitherEffect_ClassID,
kGrDualIntervalGradientColorizer_ClassID,
kGrEllipseEffect_ClassID,
kGrFillRRectOp_Processor_ClassID,
kGrGaussianConvolutionFragmentProcessor_ClassID,
kGrGSCoverageProcessor_ClassID,
kGrImprovedPerlinNoiseEffect_ClassID,
- kGrIncrDecrWindingCountXP_ClassID,
- kGrLightingEffect_ClassID,
- kGrLinearGradient_ClassID,
kGrLinearGradientLayout_ClassID,
kGrLumaColorFilterEffect_ClassID,
kGrMagnifierEffect_ClassID,
@@ -124,47 +118,38 @@
kGrMeshTestProcessor_ClassID,
kGrMorphologyEffect_ClassID,
kGrMixerEffect_ClassID,
- kGrOverdrawFragmentProcessor_ClassID,
kGrOverrideInputFragmentProcessor_ClassID,
kGrPathProcessor_ClassID,
kGrPerlinNoise2Effect_ClassID,
kGrPipelineDynamicStateTestProcessor_ClassID,
kGrPremulInputFragmentProcessor_ClassID,
kGrQuadEffect_ClassID,
- kGrRadialGradient_ClassID,
kGrRadialGradientLayout_ClassID,
kGrRectBlurEffect_ClassID,
kGrRRectBlurEffect_ClassID,
kGrRRectShadowGeoProc_ClassID,
- kGrResolveWindingCountXP_ClassID,
kGrSimpleTextureEffect_ClassID,
kGrSingleIntervalGradientColorizer_ClassID,
kGrSkSLFP_ClassID,
kGrSpecularLightingEffect_ClassID,
kGrSRGBEffect_ClassID,
kGrSampleMaskProcessor_ClassID,
- kGrSweepGradient_ClassID,
+ kGrSaturateProcessor_ClassID,
kGrSweepGradientLayout_ClassID,
kGrTextureDomainEffect_ClassID,
kGrTextureGradientColorizer_ClassID,
kGrTiledGradientEffect_ClassID,
kGrTwoPointConicalGradientLayout_ClassID,
- kGrUnpremulInputFragmentProcessor_ClassID,
kGrUnrolledBinaryGradientColorizer_ClassID,
kGrVSCoverageProcessor_ClassID,
kGrYUVtoRGBEffect_ClassID,
kHighContrastFilterEffect_ClassID,
- kInstanceProcessor_ClassID,
kLatticeGP_ClassID,
- kLumaColorFilterEffect_ClassID,
- kMSAAQuadProcessor_ClassID,
kPDLCDXferProcessor_ClassID,
kPorterDuffXferProcessor_ClassID,
kPremulFragmentProcessor_ClassID,
kQuadEdgeEffect_ClassID,
kQuadPerEdgeAAGeometryProcessor_ClassID,
- kReplaceInputFragmentProcessor_ClassID,
- kRRectsGaussianEdgeFP_ClassID,
kSampleLocationsTestProcessor_ClassID,
kSeriesFragmentProcessor_ClassID,
kShaderPDXferProcessor_ClassID,
@@ -172,7 +157,6 @@
kFwidthSquircleTestProcessor_ClassID,
kSwizzleFragmentProcessor_ClassID,
kTestFP_ClassID,
- kTextureGeometryProcessor_ClassID,
kFlatNormalsFP_ClassID,
kMappedNormalsFP_ClassID,
kLightingFP_ClassID,
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index 10a593b..acc0fd1 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -261,7 +261,5 @@
SkASSERT(header->processorFeatures() == processorFeatures); // Ensure enough bits.
header->fSnapVerticesToPixelCenters = pipeline.snapVerticesToPixelCenters();
header->fHasPointSize = hasPointSize ? 1 : 0;
- header->fClampBlendInput =
- GrClampType::kManual == GrPixelConfigClampType(renderTarget->config()) ? 1 : 0;
return true;
}
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 4854646..63cd46d 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -112,8 +112,7 @@
uint8_t fProcessorFeatures : 1;
bool fSnapVerticesToPixelCenters : 1;
bool fHasPointSize : 1;
- bool fClampBlendInput : 1;
- uint8_t fPad : 2;
+ uint8_t fPad : 3;
};
GR_STATIC_ASSERT(sizeof(KeyHeader) == 6);
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index dd3472c..29bceb3 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -5,6 +5,8 @@
* found in the LICENSE file.
*/
+#include "src/gpu/GrRenderTargetContext.h"
+
#include "include/core/SkDrawable.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/private/GrRecordingContext.h"
@@ -32,7 +34,6 @@
#include "src/gpu/GrPathRenderer.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrRenderTarget.h"
-#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/GrStencilAttachment.h"
@@ -676,6 +677,7 @@
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
SkDEBUGCODE(this->validate();)
+ SkASSERT(proxy);
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTexturedQuad", fContext);
AutoCheckFlush acf(this->drawingManager());
@@ -693,12 +695,15 @@
const GrClip& finalClip = opt == QuadOptimization::kClipApplied ? GrFixedClip::Disabled()
: clip;
GrAAType aaType = this->chooseAAType(aa);
+ auto clampType = GrColorTypeClampType(this->colorSpaceInfo().colorType());
+ auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
+ : GrTextureOp::Saturate::kNo;
// Use the provided domain, although hypothetically we could detect that the cropped local
// quad is sufficiently inside the domain and the constraint could be dropped.
- this->addDrawOp(finalClip, GrTextureOp::Make(fContext, std::move(proxy),
- std::move(textureXform), filter, color,
- blendMode, aaType, edgeFlags,
- croppedDeviceQuad, croppedLocalQuad, domain));
+ this->addDrawOp(finalClip,
+ GrTextureOp::Make(fContext, std::move(proxy), std::move(textureXform),
+ filter, color, saturate, blendMode, aaType, edgeFlags,
+ croppedDeviceQuad, croppedLocalQuad, domain));
}
}
@@ -956,8 +961,11 @@
// Can use a single op, avoiding GrPaint creation, and can batch across proxies
AutoCheckFlush acf(this->drawingManager());
GrAAType aaType = this->chooseAAType(aa);
- auto op = GrTextureOp::MakeSet(fContext, set, cnt, filter, aaType, constraint, viewMatrix,
- std::move(texXform));
+ auto clampType = GrColorTypeClampType(this->colorSpaceInfo().colorType());
+ auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
+ : GrTextureOp::Saturate::kNo;
+ auto op = GrTextureOp::MakeSet(fContext, set, cnt, filter, saturate, aaType, constraint,
+ viewMatrix, std::move(texXform));
this->addDrawOp(clip, std::move(op));
}
}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 750e554..d105747 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -5,6 +5,8 @@
* found in the LICENSE file.
*/
+#include "src/gpu/SkGr.h"
+
#include "include/core/SkCanvas.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkData.h"
@@ -33,16 +35,15 @@
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/GrXferProcessor.h"
-#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
#include "src/gpu/effects/GrSkSLFP.h"
#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
+#include "src/gpu/effects/generated/GrSaturateProcessor.h"
#include "src/image/SkImage_Base.h"
#include "src/shaders/SkShaderBase.h"
-#if SK_SUPPORT_GPU
GR_FP_SRC_STRING SKSL_DITHER_SRC = R"(
// This controls the range of values added to color channels
layout(key) in int rangeType;
@@ -82,7 +83,6 @@
color = half4(clamp(color.rgb + value * range, 0.0, color.a), color.a);
}
)";
-#endif
GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo& info) {
GrSurfaceDesc desc;
@@ -472,6 +472,17 @@
}
}
#endif
+ if (GrColorTypeClampType(colorSpaceInfo.colorType()) == GrClampType::kManual) {
+ if (grPaint->numColorFragmentProcessors()) {
+ grPaint->addColorFragmentProcessor(GrSaturateProcessor::Make());
+ } else {
+ auto color = grPaint->getColor4f();
+ grPaint->setColor4f({SkTPin(color.fR, 0.f, 1.f),
+ SkTPin(color.fG, 0.f, 1.f),
+ SkTPin(color.fB, 0.f, 1.f),
+ SkTPin(color.fA, 0.f, 1.f)});
+ }
+ }
return true;
}
diff --git a/src/gpu/effects/GrSaturateProcessor.fp b/src/gpu/effects/GrSaturateProcessor.fp
new file mode 100644
index 0000000..137205c
--- /dev/null
+++ b/src/gpu/effects/GrSaturateProcessor.fp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+@optimizationFlags {
+ kConstantOutputForConstantInput_OptimizationFlag |
+ kPreservesOpaqueInput_OptimizationFlag
+}
+
+void main() { sk_OutColor = saturate(sk_InColor); }
+
+@class {
+ SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+ return {SkTPin(input.fR, 0.f, 1.f),
+ SkTPin(input.fG, 0.f, 1.f),
+ SkTPin(input.fB, 0.f, 1.f),
+ SkTPin(input.fA, 0.f, 1.f)};
+ }
+}
+
+@test(d) { return GrSaturateProcessor::Make(); }
diff --git a/src/gpu/effects/generated/GrSaturateProcessor.cpp b/src/gpu/effects/generated/GrSaturateProcessor.cpp
new file mode 100644
index 0000000..6f1734d
--- /dev/null
+++ b/src/gpu/effects/generated/GrSaturateProcessor.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrSaturateProcessor.fp; do not modify.
+ **************************************************************************************************/
+#include "GrSaturateProcessor.h"
+
+#include "include/gpu/GrTexture.h"
+#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
+#include "src/sksl/SkSLCPP.h"
+#include "src/sksl/SkSLUtil.h"
+class GrGLSLSaturateProcessor : public GrGLSLFragmentProcessor {
+public:
+ GrGLSLSaturateProcessor() {}
+ void emitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ const GrSaturateProcessor& _outer = args.fFp.cast<GrSaturateProcessor>();
+ (void)_outer;
+ fragBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", args.fOutputColor,
+ args.fInputColor);
+ }
+
+private:
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& _proc) override {}
+};
+GrGLSLFragmentProcessor* GrSaturateProcessor::onCreateGLSLInstance() const {
+ return new GrGLSLSaturateProcessor();
+}
+void GrSaturateProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {}
+bool GrSaturateProcessor::onIsEqual(const GrFragmentProcessor& other) const {
+ const GrSaturateProcessor& that = other.cast<GrSaturateProcessor>();
+ (void)that;
+ return true;
+}
+GrSaturateProcessor::GrSaturateProcessor(const GrSaturateProcessor& src)
+ : INHERITED(kGrSaturateProcessor_ClassID, src.optimizationFlags()) {}
+std::unique_ptr<GrFragmentProcessor> GrSaturateProcessor::clone() const {
+ return std::unique_ptr<GrFragmentProcessor>(new GrSaturateProcessor(*this));
+}
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSaturateProcessor);
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrSaturateProcessor::TestCreate(GrProcessorTestData* d) {
+ return GrSaturateProcessor::Make();
+}
+#endif
diff --git a/src/gpu/effects/generated/GrSaturateProcessor.h b/src/gpu/effects/generated/GrSaturateProcessor.h
new file mode 100644
index 0000000..28af190
--- /dev/null
+++ b/src/gpu/effects/generated/GrSaturateProcessor.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrSaturateProcessor.fp; do not modify.
+ **************************************************************************************************/
+#ifndef GrSaturateProcessor_DEFINED
+#define GrSaturateProcessor_DEFINED
+#include "include/core/SkTypes.h"
+
+#include "src/gpu/GrCoordTransform.h"
+#include "src/gpu/GrFragmentProcessor.h"
+class GrSaturateProcessor : public GrFragmentProcessor {
+public:
+ SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+ return {SkTPin(input.fR, 0.f, 1.f), SkTPin(input.fG, 0.f, 1.f), SkTPin(input.fB, 0.f, 1.f),
+ SkTPin(input.fA, 0.f, 1.f)};
+ }
+ static std::unique_ptr<GrFragmentProcessor> Make() {
+ return std::unique_ptr<GrFragmentProcessor>(new GrSaturateProcessor());
+ }
+ GrSaturateProcessor(const GrSaturateProcessor& src);
+ std::unique_ptr<GrFragmentProcessor> clone() const override;
+ const char* name() const override { return "SaturateProcessor"; }
+
+private:
+ GrSaturateProcessor()
+ : INHERITED(kGrSaturateProcessor_ClassID,
+ (OptimizationFlags)kConstantOutputForConstantInput_OptimizationFlag |
+ kPreservesOpaqueInput_OptimizationFlag) {}
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ typedef GrFragmentProcessor INHERITED;
+};
+#endif
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index c71071b..9df85a9 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -255,16 +255,7 @@
SkASSERT(dstTexture->texturePriv().textureType() != GrTextureType::kExternal);
}
- SkString finalInColor;
- if (colorIn.size()) {
- if (this->desc()->header().fClampBlendInput) {
- finalInColor.printf("saturate(%s)", colorIn.c_str());
- } else {
- finalInColor = colorIn;
- }
- } else {
- finalInColor = "float4(1)";
- }
+ SkString finalInColor = colorIn.size() ? colorIn : SkString("float4(1)");
GrGLSLXferProcessor::EmitArgs args(&fFS,
this->uniformHandler(),
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 6c4211c..8b9aa34 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -850,6 +850,7 @@
class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
public:
+ using Saturate = GrTextureOp::Saturate;
static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
@@ -859,33 +860,36 @@
GrTextureType textureType,
const GrSamplerState& samplerState,
const GrSwizzle& swizzle, uint32_t extraSamplerKey,
- sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+ Saturate saturate) {
return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
vertexSpec, caps, textureType, samplerState, swizzle, extraSamplerKey,
- std::move(textureColorSpaceXform)));
+ std::move(textureColorSpaceXform), saturate));
}
const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
// texturing, device-dimensions are single bit flags
- uint32_t x = fTexDomain.isInitialized() ? 0 : 1;
- x |= fSampler.isInitialized() ? 0 : 2;
- x |= fNeedsPerspective ? 0 : 4;
+ uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
+ | (fSampler.isInitialized() ? 0 : 0x2)
+ | (fNeedsPerspective ? 0 : 0x4)
+ | (fSaturate == Saturate::kNo ? 0 : 0x8);
// local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
if (fLocalCoord.isInitialized()) {
- x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
+ x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 0x10 : 0x20;
}
// similar for colors, 00 for none, 01 for bytes, 10 for half-floats
if (fColor.isInitialized()) {
- x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
+ x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
}
// and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
// position+geomdomain
SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
if (fCoverageMode != CoverageMode::kNone) {
- x |= fGeomDomain.isInitialized() ?
- 384 : (CoverageMode::kWithPosition == fCoverageMode ? 128 : 256);
+ x |= fGeomDomain.isInitialized()
+ ? 0x300
+ : (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
}
b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
@@ -990,6 +994,14 @@
args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
&fTextureColorSpaceXformHelper);
args.fFragBuilder->codeAppend(";");
+ if (gp.fSaturate == Saturate::kYes) {
+ args.fFragBuilder->codeAppendf("%s = saturate(%s);",
+ args.fOutputColor, args.fOutputColor);
+ }
+ } else {
+ // Saturate is only intended for use with a proxy to account for the fact
+ // that GrTextureOp skips SkPaint conversion, which normally handles this.
+ SkASSERT(gp.fSaturate == Saturate::kNo);
}
// And lastly, output the coverage calculation code
@@ -1056,8 +1068,10 @@
const GrSamplerState& samplerState,
const GrSwizzle& swizzle,
uint32_t extraSamplerKey,
- sk_sp<GrColorSpaceXform> textureColorSpaceXform)
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+ Saturate saturate)
: INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
+ , fSaturate(saturate)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fSampler(textureType, samplerState, swizzle, extraSamplerKey) {
SkASSERT(spec.hasLocalCoords());
@@ -1122,6 +1136,8 @@
// The positions attribute may have coverage built into it, so float3 is an ambiguous type
// and may mean 2d with coverage, or 3d with no coverage
bool fNeedsPerspective;
+ // Should saturate() be called on the color? Only relevant when created with a texture.
+ Saturate fSaturate = Saturate::kNo;
CoverageMode fCoverageMode;
// Color space will be null and fSampler.isInitialized() returns false when the GP is configured
@@ -1140,9 +1156,11 @@
GrTextureType textureType,
const GrSamplerState& samplerState,
const GrSwizzle& swizzle, uint32_t extraSamplerKey,
- sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+ Saturate saturate) {
return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, samplerState, swizzle,
- extraSamplerKey, std::move(textureColorSpaceXform));
+ extraSamplerKey, std::move(textureColorSpaceXform),
+ saturate);
}
} // namespace GrQuadPerEdgeAA
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index ca92859..3558218 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -16,12 +16,14 @@
#include "src/gpu/GrSamplerState.h"
#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
+#include "src/gpu/ops/GrTextureOp.h"
class GrCaps;
class GrColorSpaceXform;
class GrShaderCaps;
namespace GrQuadPerEdgeAA {
+ using Saturate = GrTextureOp::Saturate;
enum class Domain : bool { kNo = false, kYes = true };
enum class ColorType { kNone, kByte, kHalf, kLast = kHalf };
@@ -84,7 +86,7 @@
sk_sp<GrGeometryProcessor> MakeTexturedProcessor(
const VertexSpec& spec, const GrShaderCaps& caps, GrTextureType textureType,
const GrSamplerState& samplerState, const GrSwizzle& swizzle, uint32_t extraSamplerKey,
- sk_sp<GrColorSpaceXform> textureColorSpaceXform);
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform, Saturate saturate);
// Fill vertices with the vertex data needed to represent the given quad. The device position,
// local coords, vertex color, domain, and edge coefficients will be written and/or computed
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 4c04ce0..c77b250 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -5,8 +5,8 @@
* found in the LICENSE file.
*/
-#include "src/gpu/ops/GrTextureOp.h"
#include <new>
+
#include "include/core/SkPoint.h"
#include "include/core/SkPoint3.h"
#include "include/gpu/GrTexture.h"
@@ -31,6 +31,7 @@
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrTextureDomain.h"
+#include "src/gpu/effects/generated/GrSaturateProcessor.h"
#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/geometry/GrQuadBuffer.h"
#include "src/gpu/geometry/GrQuadUtils.h"
@@ -38,6 +39,7 @@
#include "src/gpu/ops/GrFillRectOp.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/ops/GrQuadPerEdgeAA.h"
+#include "src/gpu/ops/GrTextureOp.h"
namespace {
@@ -146,28 +148,31 @@
sk_sp<GrColorSpaceXform> textureXform,
GrSamplerState::Filter filter,
const SkPMColor4f& color,
+ GrTextureOp::Saturate saturate,
GrAAType aaType,
GrQuadAAFlags aaFlags,
const GrQuad& deviceQuad,
const GrQuad& localQuad,
const SkRect* domain) {
GrOpMemoryPool* pool = context->priv().opMemoryPool();
- return pool->allocate<TextureOp>(
- std::move(proxy), std::move(textureXform), filter, color, aaType, aaFlags,
- deviceQuad, localQuad, domain);
+ return pool->allocate<TextureOp>(std::move(proxy), std::move(textureXform), filter, color,
+ saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
}
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
const GrRenderTargetContext::TextureSetEntry set[],
- int cnt, GrSamplerState::Filter filter, GrAAType aaType,
+ int cnt,
+ GrSamplerState::Filter filter,
+ GrTextureOp::Saturate saturate,
+ GrAAType aaType,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
GrOpMemoryPool* pool = context->priv().opMemoryPool();
void* mem = pool->allocate(size);
- return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
- set, cnt, filter, aaType, constraint, viewMatrix,
- std::move(textureColorSpaceXform)));
+ return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, saturate, aaType,
+ constraint, viewMatrix,
+ std::move(textureColorSpaceXform)));
}
~TextureOp() override {
@@ -263,13 +268,20 @@
// dstQuad should be the geometry transformed by the view matrix. If domainRect
// is not null it will be used to apply the strict src rect constraint.
- TextureOp(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
- GrSamplerState::Filter filter, const SkPMColor4f& color,
- GrAAType aaType, GrQuadAAFlags aaFlags,
- const GrQuad& dstQuad, const GrQuad& srcQuad, const SkRect* domainRect)
+ TextureOp(sk_sp<GrTextureProxy> proxy,
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+ GrSamplerState::Filter filter,
+ const SkPMColor4f& color,
+ GrTextureOp::Saturate saturate,
+ GrAAType aaType,
+ GrQuadAAFlags aaFlags,
+ const GrQuad& dstQuad,
+ const GrQuad& srcQuad,
+ const SkRect* domainRect)
: INHERITED(ClassID())
, fQuads(1, true /* includes locals */)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+ , fSaturate(static_cast<unsigned>(saturate))
, fFilter(static_cast<unsigned>(filter)) {
// Clean up disparities between the overall aa type and edge configuration and apply
// optimizations based on the rect and matrix when appropriate
@@ -295,13 +307,18 @@
IsZeroArea::kNo);
fDomain = static_cast<unsigned>(domainRect != nullptr);
}
- TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
- GrSamplerState::Filter filter, GrAAType aaType,
- SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
+ TextureOp(const GrRenderTargetContext::TextureSetEntry set[],
+ int cnt,
+ GrSamplerState::Filter filter,
+ GrTextureOp::Saturate saturate,
+ GrAAType aaType,
+ SkCanvas::SrcRectConstraint constraint,
+ const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureColorSpaceXform)
: INHERITED(ClassID())
, fQuads(cnt, true /* includes locals */)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+ , fSaturate(static_cast<unsigned>(saturate))
, fFilter(static_cast<unsigned>(filter)) {
fProxyCnt = SkToUInt(cnt);
SkRect bounds = SkRectPriv::MakeLargestInverted();
@@ -454,9 +471,10 @@
uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
samplerState, fProxies[0].fProxy->backendFormat());
+ auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
vertexSpec, *target->caps().shaderCaps(), textureType, samplerState, swizzle,
- extraSamplerKey, std::move(fTextureColorSpaceXform));
+ extraSamplerKey, std::move(fTextureColorSpaceXform), saturate);
// We'll use a dynamic state array for the GP textures when there are multiple ops.
// Otherwise, we use fixed dynamic state to specify the single op's proxy.
@@ -554,6 +572,9 @@
}
upgradeToCoverageAAOnMerge = true;
}
+ if (fSaturate != that->fSaturate) {
+ return CombineResult::kCannotCombine;
+ }
if (fFilter != that->fFilter) {
return CombineResult::kCannotCombine;
}
@@ -587,12 +608,13 @@
GrQuadBuffer<ColorDomainAndAA> fQuads;
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+ unsigned fSaturate : 1;
unsigned fFilter : 2;
unsigned fAAType : 2;
unsigned fDomain : 1;
unsigned fColorType : 2;
GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
- unsigned fProxyCnt : 32 - 7;
+ unsigned fProxyCnt : 32 - 8;
Proxy fProxies[1];
static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
@@ -609,6 +631,7 @@
sk_sp<GrColorSpaceXform> textureXform,
GrSamplerState::Filter filter,
const SkPMColor4f& color,
+ Saturate saturate,
SkBlendMode blendMode,
GrAAType aaType,
GrQuadAAFlags aaFlags,
@@ -627,7 +650,7 @@
if (blendMode == SkBlendMode::kSrcOver) {
return TextureOp::Make(context, std::move(proxy), std::move(textureXform), filter, color,
- aaType, aaFlags, deviceQuad, localQuad, domain);
+ saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
} else {
// Emulate complex blending using GrFillRectOp
GrPaint paint;
@@ -649,6 +672,9 @@
}
fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
paint.addColorFragmentProcessor(std::move(fp));
+ if (saturate == GrTextureOp::Saturate::kYes) {
+ paint.addColorFragmentProcessor(GrSaturateProcessor::Make());
+ }
return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
deviceQuad, localQuad);
@@ -659,11 +685,12 @@
const GrRenderTargetContext::TextureSetEntry set[],
int cnt,
GrSamplerState::Filter filter,
+ Saturate saturate,
GrAAType aaType,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
- return TextureOp::Make(context, set, cnt, filter, aaType, constraint, viewMatrix,
+ return TextureOp::Make(context, set, cnt, filter, saturate, aaType, constraint, viewMatrix,
std::move(textureColorSpaceXform));
}
@@ -719,8 +746,9 @@
aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
bool useDomain = random->nextBool();
+ auto saturate = random->nextBool() ? GrTextureOp::Saturate::kYes : GrTextureOp::Saturate::kNo;
return GrTextureOp::Make(context, std::move(proxy), std::move(texXform), filter, color,
- SkBlendMode::kSrcOver, aaType, aaFlags,
+ saturate, SkBlendMode::kSrcOver, aaType, aaFlags,
GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect),
useDomain ? &srcRect : nullptr);
}
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index efd815e..1e85c29 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -23,6 +23,12 @@
namespace GrTextureOp {
/**
+ * Controls whether saturate() is called after the texture is color-converted to ensure all
+ * color values are in 0..1 range.
+ */
+enum class Saturate : bool { kNo = false, kYes = true };
+
+/**
* Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by the
* texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using 'localQuad'
* to map into the proxy's texture space. If non-null, 'domain' represents the boundary for the
@@ -33,14 +39,15 @@
* deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
* src over, this will return a GrFillRectOp with a paint that samples the proxy.
*/
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
- sk_sp<GrTextureProxy> proxy,
- sk_sp<GrColorSpaceXform> textureXform,
- GrSamplerState::Filter filter,
- const SkPMColor4f& color,
- SkBlendMode blendMode,
- GrAAType aaType,
- GrQuadAAFlags aaFlags,
+std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
+ sk_sp<GrTextureProxy>,
+ sk_sp<GrColorSpaceXform>,
+ GrSamplerState::Filter,
+ const SkPMColor4f&,
+ Saturate,
+ SkBlendMode,
+ GrAAType,
+ GrQuadAAFlags,
const GrQuad& deviceQuad,
const GrQuad& localQuad,
const SkRect* domain = nullptr);
@@ -50,6 +57,7 @@
const GrRenderTargetContext::TextureSetEntry[],
int cnt,
GrSamplerState::Filter,
+ Saturate,
GrAAType,
SkCanvas::SrcRectConstraint,
const SkMatrix& viewMatrix,