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/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,