Add MSAA and non-aa modes to GrFillRRect Op
Adds a non-aa mode and an MSAA mode that uses the sample mask. Also
adds a new cap to decide whether we prefer this new sample mask Op for
large round rects, or whether it's faster to just continue drawing
them as paths like before.
Bug: skia:
Change-Id: Ic344ace26e7889c312c3040ad345b4d9a717f96d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/204135
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ops/GrFillRRectOp.cpp b/src/gpu/ops/GrFillRRectOp.cpp
index ead28f6..12d4300 100644
--- a/src/gpu/ops/GrFillRRectOp.cpp
+++ b/src/gpu/ops/GrFillRRectOp.cpp
@@ -21,34 +21,41 @@
// Hardware derivatives are not always accurate enough for highly elliptical corners. This method
// checks to make sure the corners will still all look good if we use HW derivatives.
-static bool can_use_hw_derivatives(const GrShaderCaps&, const SkMatrix&, const SkRRect&);
+static bool can_use_hw_derivatives_with_coverage(
+ const GrShaderCaps&, const SkMatrix&, const SkRRect&);
std::unique_ptr<GrFillRRectOp> GrFillRRectOp::Make(
- GrRecordingContext* ctx, const SkMatrix& viewMatrix, const SkRRect& rrect,
+ GrRecordingContext* ctx, GrAAType aaType, const SkMatrix& viewMatrix, const SkRRect& rrect,
const GrCaps& caps, GrPaint&& paint) {
if (!caps.instanceAttribSupport()) {
return nullptr;
}
- // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we already
- // use HW derivatives. The only trick will be adjusting the AA outset to account for
- // perspective. (i.e., outset = 0.5 * z.)
- if (viewMatrix.hasPerspective()) {
- return nullptr;
- }
-
- GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
- return pool->allocate<GrFillRRectOp>(*caps.shaderCaps(), viewMatrix, rrect, std::move(paint));
-}
-
-GrFillRRectOp::GrFillRRectOp(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
- const SkRRect& rrect, GrPaint&& paint)
- : GrDrawOp(ClassID())
- , fOriginalColor(paint.getColor4f())
- , fLocalRect(rrect.rect())
- , fProcessors(std::move(paint)) {
- if (can_use_hw_derivatives(shaderCaps, viewMatrix, rrect)) {
- fFlags |= Flags::kUseHWDerivatives;
+ Flags flags = Flags::kNone;
+ if (GrAAType::kCoverage == aaType) {
+ // TODO: Support perspective in a follow-on CL. This shouldn't be difficult, since we
+ // already use HW derivatives. The only trick will be adjusting the AA outset to account for
+ // perspective. (i.e., outset = 0.5 * z.)
+ if (viewMatrix.hasPerspective()) {
+ return nullptr;
+ }
+ if (can_use_hw_derivatives_with_coverage(*caps.shaderCaps(), viewMatrix, rrect)) {
+ // HW derivatives (more specifically, fwidth()) are consistently faster on all platforms
+ // in coverage mode. We use them as long as the approximation will be accurate enough.
+ flags |= Flags::kUseHWDerivatives;
+ }
+ } else {
+ if (GrAAType::kMSAA == aaType) {
+ if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleVariablesSupport()) {
+ return nullptr;
+ }
+ }
+ if (viewMatrix.hasPerspective()) {
+ // HW derivatives are consistently slower on all platforms in sample mask mode. We
+ // therefore only use them when there is perspective, since then we can't interpolate
+ // the symbolic screen-space gradient.
+ flags |= Flags::kUseHWDerivatives | Flags::kHasPerspective;
+ }
}
// Produce a matrix that draws the round rect from normalized [-1, -1, +1, +1] space.
@@ -60,23 +67,61 @@
// Map to device space.
m.postConcat(viewMatrix);
- // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
- // device-space quad, it's quite simple to find the bounding rectangle:
- SkASSERT(!m.hasPerspective());
- SkRect bounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
- bounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
- SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
- this->setBounds(bounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
+ SkRect devBounds;
+ if (!(flags & Flags::kHasPerspective)) {
+ // Since m is an affine matrix that maps the rect [-1, -1, +1, +1] into the shape's
+ // device-space quad, it's quite simple to find the bounding rectangle:
+ devBounds = SkRect::MakeXYWH(m.getTranslateX(), m.getTranslateY(), 0, 0);
+ devBounds.outset(SkScalarAbs(m.getScaleX()) + SkScalarAbs(m.getSkewX()),
+ SkScalarAbs(m.getSkewY()) + SkScalarAbs(m.getScaleY()));
+ } else {
+ viewMatrix.mapRect(&devBounds, rrect.rect());
+ }
+
+ if (GrAAType::kMSAA == aaType && caps.preferTrianglesOverSampleMask()) {
+ // We are on a platform that prefers fine triangles instead of using the sample mask. See if
+ // the round rect is large enough that it will be faster for us to send it off to the
+ // default path renderer instead. The 200x200 threshold was arrived at using the
+ // "shapes_rrect" benchmark on an ARM Galaxy S9.
+ if (devBounds.height() * devBounds.width() > 200 * 200) {
+ return nullptr;
+ }
+ }
+
+ GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
+ return pool->allocate<GrFillRRectOp>(aaType, rrect, flags, m, std::move(paint), devBounds);
+}
+
+GrFillRRectOp::GrFillRRectOp(
+ GrAAType aaType, const SkRRect& rrect, Flags flags,
+ const SkMatrix& totalShapeMatrix, GrPaint&& paint, const SkRect& devBounds)
+ : GrDrawOp(ClassID())
+ , fAAType(aaType)
+ , fOriginalColor(paint.getColor4f())
+ , fLocalRect(rrect.rect())
+ , fFlags(flags)
+ , fProcessors(std::move(paint)) {
+ SkASSERT((fFlags & Flags::kHasPerspective) == totalShapeMatrix.hasPerspective());
+ this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
// Write the matrix attribs.
- this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
- this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
+ const SkMatrix& m = totalShapeMatrix;
+ if (!(fFlags & Flags::kHasPerspective)) {
+ // Affine 2D transformation (float2x2 plus float2 translate).
+ SkASSERT(!m.hasPerspective());
+ this->writeInstanceData(m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY());
+ this->writeInstanceData(m.getTranslateX(), m.getTranslateY());
+ } else {
+ // Perspective float3x3 transformation matrix.
+ SkASSERT(m.hasPerspective());
+ m.get9(this->appendInstanceData<float>(9));
+ }
// Convert the radii to [-1, -1, +1, +1] space and write their attribs.
Sk4f radiiX, radiiY;
Sk4f::Load2(SkRRectPriv::GetRadiiArray(rrect), &radiiX, &radiiY);
- (radiiX * (2/(r - l))).store(this->appendInstanceData<float>(4));
- (radiiY * (2/(b - t))).store(this->appendInstanceData<float>(4));
+ (radiiX * (2/rrect.width())).store(this->appendInstanceData<float>(4));
+ (radiiY * (2/rrect.height())).store(this->appendInstanceData<float>(4));
// We will write the color and local rect attribs during finalize().
}
@@ -132,13 +177,73 @@
}
}
-namespace {
+class GrFillRRectOp::Processor : public GrGeometryProcessor {
+public:
+ Processor(GrAAType aaType, Flags flags)
+ : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
+ , fAAType(aaType)
+ , fFlags(flags) {
+ int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
+ this->setVertexAttributes(kVertexAttribs, numVertexAttribs);
-// Our round rect geometry consists of an inset octagon with solid coverage, surrounded by linear
+ if (!(flags & Flags::kHasPerspective)) {
+ // Affine 2D transformation (float2x2 plus float2 translate).
+ fInstanceAttribs.emplace_back("skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ fInstanceAttribs.emplace_back(
+ "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
+ } else {
+ // Perspective float3x3 transformation matrix.
+ fInstanceAttribs.emplace_back("persp_x", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
+ fInstanceAttribs.emplace_back("persp_y", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
+ fInstanceAttribs.emplace_back("persp_z", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
+ }
+ fInstanceAttribs.emplace_back("radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ fInstanceAttribs.emplace_back("radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ fColorAttrib = &fInstanceAttribs.push_back(
+ MakeColorAttribute("color", (flags & Flags::kWideColor)));
+ if (fFlags & Flags::kHasLocalCoords) {
+ fInstanceAttribs.emplace_back(
+ "local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ }
+ this->setInstanceAttributes(fInstanceAttribs.begin(), fInstanceAttribs.count());
+
+ if (GrAAType::kMSAA == fAAType) {
+ this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
+ }
+ }
+
+ const char* name() const override { return "GrFillRRectOp::Processor"; }
+
+ void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+ b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
+ }
+
+ GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
+
+private:
+ static constexpr Attribute kVertexAttribs[] = {
+ {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
+ {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
+ // Coverage only.
+ {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
+
+ const GrAAType fAAType;
+ const Flags fFlags;
+
+ SkSTArray<6, Attribute> fInstanceAttribs;
+ const Attribute* fColorAttrib;
+
+ class CoverageImpl;
+ class MSAAImpl;
+};
+
+constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
+
+// Our coverage geometry consists of an inset octagon with solid coverage, surrounded by linear
// coverage ramps on the horizontal and vertical edges, and "arc coverage" pieces on the diagonal
// edges. The Vertex struct tells the shader where to place its vertex within a normalized
// ([l, t, r, b] = [-1, -1, +1, +1]) space, and how to calculate coverage. See onEmitCode.
-struct Vertex {
+struct CoverageVertex {
std::array<float, 4> fRadiiSelector;
std::array<float, 2> fCorner;
std::array<float, 2> fRadiusOutset;
@@ -152,7 +257,7 @@
// rectangles.
static constexpr float kOctoOffset = 1/(1 + SK_ScalarRoot2Over2);
-static constexpr Vertex kVertexData[] = {
+static constexpr CoverageVertex kCoverageVertexData[] = {
// Left inset edge.
{{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}, {{+1,0}}, 1, 1},
{{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}, {{+1,0}}, 1, 1},
@@ -219,9 +324,9 @@
{{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}, {{-1,+1}}, 0, 0},
{{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}, {{-1,+1}}, 0, 0}};
-GR_DECLARE_STATIC_UNIQUE_KEY(gVertexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
-static constexpr uint16_t kIndexData[] = {
+static constexpr uint16_t kCoverageIndexData[] = {
// Inset octagon (solid coverage).
0, 1, 7,
1, 2, 7,
@@ -260,64 +365,18 @@
39, 36, 38,
36, 38, 37};
-GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
-}
-
-class GrFillRRectOp::Processor : public GrGeometryProcessor {
-public:
- Processor(Flags flags)
- : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
- , fFlags(flags) {
- this->setVertexAttributes(kVertexAttribs, 3);
- fInSkew = { "skew", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
- fInTranslate = { "translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType };
- fInRadiiX = { "radii_x", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
- fInRadiiY = { "radii_y", kFloat4_GrVertexAttribType, kFloat4_GrSLType };
- fInColor = MakeColorAttribute("color", (flags & Flags::kWideColor));
- fInLocalRect = {"local_rect", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
-
- this->setInstanceAttributes(&fInSkew, (flags & Flags::kHasLocalCoords) ? 6 : 5);
- SkASSERT(this->vertexStride() == sizeof(Vertex));
- }
-
- const char* name() const override { return "GrFillRRectOp::Processor"; }
-
- void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
- b->add32(static_cast<uint32_t>(fFlags));
- }
-
- GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
-
-private:
- static constexpr Attribute kVertexAttribs[] = {
- {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
- {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
- {"aa_bloat_and_coverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
-
- Attribute fInSkew;
- Attribute fInTranslate;
- Attribute fInRadiiX;
- Attribute fInRadiiY;
- Attribute fInColor;
- Attribute fInLocalRect; // Conditional.
-
- const Flags fFlags;
-
- class Impl;
-};
-
-constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
-
-class GrFillRRectOp::Processor::Impl : public GrGLSLGeometryProcessor {
-public:
+class GrFillRRectOp::Processor::CoverageImpl : public GrGLSLGeometryProcessor {
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
const auto& proc = args.fGP.cast<Processor>();
bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
+ SkASSERT(proc.vertexStride() == sizeof(CoverageVertex));
+
GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
varyings->emitAttributes(proc);
- varyings->addPassThroughAttribute(proc.fInColor, args.fOutputColor,
+ varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
// Emit the vertex shader.
@@ -388,6 +447,7 @@
args.fFPCoordTransformHandler);
// Transform to device space.
+ SkASSERT(!(proc.fFlags & Flags::kHasPerspective));
v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
@@ -420,7 +480,7 @@
f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn());
f->codeAppendf("half coverage;");
f->codeAppendf("if (0 == x_plus_1) {");
- f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (i.e., linear coverage).
+ f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage).
f->codeAppendf("} else {");
f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1
f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1
@@ -443,9 +503,195 @@
}
};
+// Our MSAA geometry consists of an inset octagon with full sample mask coverage, circumscribed
+// by a larger octagon that modifies the sample mask for the arc at each corresponding corner.
+struct MSAAVertex {
+ std::array<float, 4> fRadiiSelector;
+ std::array<float, 2> fCorner;
+ std::array<float, 2> fRadiusOutset;
+};
+
+static constexpr MSAAVertex kMSAAVertexData[] = {
+ // Left edge. (Negative radii selector indicates this is not an arc section.)
+ {{{0,0,0,-1}}, {{-1,+1}}, {{0,-1}}},
+ {{{-1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
+
+ // Top edge.
+ {{{-1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
+ {{{0,-1,0,0}}, {{+1,-1}}, {{-1,0}}},
+
+ // Right edge.
+ {{{0,-1,0,0}}, {{+1,-1}}, {{0,+1}}},
+ {{{0,0,-1,0}}, {{+1,+1}}, {{0,-1}}},
+
+ // Bottom edge.
+ {{{0,0,-1,0}}, {{+1,+1}}, {{-1,0}}},
+ {{{0,0,0,-1}}, {{-1,+1}}, {{+1,0}}},
+
+ // Top-left corner.
+ {{{1,0,0,0}}, {{-1,-1}}, {{0,+1}}},
+ {{{1,0,0,0}}, {{-1,-1}}, {{0,+kOctoOffset}}},
+ {{{1,0,0,0}}, {{-1,-1}}, {{+1,0}}},
+ {{{1,0,0,0}}, {{-1,-1}}, {{+kOctoOffset,0}}},
+
+ // Top-right corner.
+ {{{0,1,0,0}}, {{+1,-1}}, {{-1,0}}},
+ {{{0,1,0,0}}, {{+1,-1}}, {{-kOctoOffset,0}}},
+ {{{0,1,0,0}}, {{+1,-1}}, {{0,+1}}},
+ {{{0,1,0,0}}, {{+1,-1}}, {{0,+kOctoOffset}}},
+
+ // Bottom-right corner.
+ {{{0,0,1,0}}, {{+1,+1}}, {{0,-1}}},
+ {{{0,0,1,0}}, {{+1,+1}}, {{0,-kOctoOffset}}},
+ {{{0,0,1,0}}, {{+1,+1}}, {{-1,0}}},
+ {{{0,0,1,0}}, {{+1,+1}}, {{-kOctoOffset,0}}},
+
+ // Bottom-left corner.
+ {{{0,0,0,1}}, {{-1,+1}}, {{+1,0}}},
+ {{{0,0,0,1}}, {{-1,+1}}, {{+kOctoOffset,0}}},
+ {{{0,0,0,1}}, {{-1,+1}}, {{0,-1}}},
+ {{{0,0,0,1}}, {{-1,+1}}, {{0,-kOctoOffset}}}};
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
+
+static constexpr uint16_t kMSAAIndexData[] = {
+ // Inset octagon. (Full sample mask.)
+ 0, 1, 2,
+ 0, 2, 3,
+ 0, 3, 6,
+ 3, 4, 5,
+ 3, 5, 6,
+ 6, 7, 0,
+
+ // Top-left arc. (Sample mask is set to the arc.)
+ 8, 9, 10,
+ 9, 11, 10,
+
+ // Top-right arc.
+ 12, 13, 14,
+ 13, 15, 14,
+
+ // Bottom-right arc.
+ 16, 17, 18,
+ 17, 19, 18,
+
+ // Bottom-left arc.
+ 20, 21, 22,
+ 21, 23, 22};
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
+
+class GrFillRRectOp::Processor::MSAAImpl : public GrGLSLGeometryProcessor {
+ void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+ const auto& proc = args.fGP.cast<Processor>();
+ bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives);
+ bool hasPerspective = (proc.fFlags & Flags::kHasPerspective);
+ bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords);
+ SkASSERT(useHWDerivatives == hasPerspective);
+
+ SkASSERT(proc.vertexStride() == sizeof(MSAAVertex));
+
+ // Emit the vertex shader.
+ GrGLSLVertexBuilder* v = args.fVertBuilder;
+
+ GrGLSLVaryingHandler* varyings = args.fVaryingHandler;
+ varyings->emitAttributes(proc);
+ varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor,
+ GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
+
+ // Unpack vertex attribs.
+ v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;");
+ v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;");
+
+ // Identify our radii.
+ v->codeAppend("float2 radii;");
+ v->codeAppend("radii.x = dot(radii_selector, radii_x);");
+ v->codeAppend("radii.y = dot(radii_selector, radii_y);");
+ v->codeAppendf("bool is_arc_section = (radii.x > 0);");
+ v->codeAppendf("radii = abs(radii);");
+
+ // Find our vertex position, adjusted for radii. Our rect is drawn in normalized
+ // [-1,-1,+1,+1] space.
+ v->codeAppend("float2 vertexpos = corner + radius_outset * radii;");
+
+ // Emit transforms.
+ GrShaderVar localCoord("", kFloat2_GrSLType);
+ if (hasLocalCoords) {
+ v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + "
+ "local_rect.zw * (1 + vertexpos)) * .5;");
+ localCoord.set(kFloat2_GrSLType, "localcoord");
+ }
+ this->emitTransforms(v, varyings, args.fUniformHandler, localCoord,
+ args.fFPCoordTransformHandler);
+
+ // Transform to device space.
+ if (!hasPerspective) {
+ v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);");
+ v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;");
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
+ } else {
+ v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);");
+ v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;");
+ gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord");
+ }
+
+ // Determine normalized arc coordinates for the implicit function.
+ GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType);
+ varyings->addVarying("arccoord", &arcCoord);
+ v->codeAppendf("if (is_arc_section) {");
+ v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut());
+ if (!useHWDerivatives) {
+ // The gradient is order-1: Interpolate it across arccoord.zw.
+ // This doesn't work with perspective.
+ SkASSERT(!hasPerspective);
+ v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);");
+ v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);",
+ arcCoord.vsOut(), arcCoord.vsOut());
+ }
+ v->codeAppendf("} else {");
+ if (useHWDerivatives) {
+ v->codeAppendf("%s = float2(0);", arcCoord.vsOut());
+ } else {
+ v->codeAppendf("%s = float4(0);", arcCoord.vsOut());
+ }
+ v->codeAppendf("}");
+
+ // Emit the fragment shader.
+ GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
+
+ f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
+
+ // If x,y == 0, then we are drawing a triangle that does not track an arc.
+ f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn());
+ f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn());
+ if (GrAAType::kMSAA == proc.fAAType) {
+ using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags;
+ if (!useHWDerivatives) {
+ f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn());
+ f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch);
+ } else {
+ f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch);
+ }
+ } else {
+ f->codeAppendf("if (fn > 0) {");
+ f->codeAppendf( "%s = half4(0);", args.fOutputCoverage);
+ f->codeAppendf("}");
+ }
+ f->codeAppendf("}");
+ }
+
+ void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+ FPCoordTransformIter&& transformIter) override {
+ this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ }
+};
+
GrGLSLPrimitiveProcessor* GrFillRRectOp::Processor::createGLSLInstance(
const GrShaderCaps&) const {
- return new Impl();
+ if (GrAAType::kCoverage != fAAType) {
+ return new MSAAImpl();
+ }
+ return new CoverageImpl();
}
void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
@@ -453,26 +699,50 @@
return; // Setup failed.
}
- GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+ sk_sp<const GrBuffer> indexBuffer, vertexBuffer;
+ int indexCount;
- sk_sp<const GrBuffer> indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
- GrGpuBufferType::kIndex, sizeof(kIndexData), kIndexData, gIndexBufferKey);
- if (!indexBuffer) {
+ if (GrAAType::kCoverage == fAAType) {
+ GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageIndexBufferKey);
+
+ indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
+ GrGpuBufferType::kIndex, sizeof(kCoverageIndexData), kCoverageIndexData,
+ gCoverageIndexBufferKey);
+
+ GR_DEFINE_STATIC_UNIQUE_KEY(gCoverageVertexBufferKey);
+
+ vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
+ GrGpuBufferType::kVertex, sizeof(kCoverageVertexData), kCoverageVertexData,
+ gCoverageVertexBufferKey);
+
+ indexCount = SK_ARRAY_COUNT(kCoverageIndexData);
+ } else {
+ GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
+
+ indexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
+ GrGpuBufferType::kIndex, sizeof(kMSAAIndexData), kMSAAIndexData,
+ gMSAAIndexBufferKey);
+
+ GR_DEFINE_STATIC_UNIQUE_KEY(gMSAAVertexBufferKey);
+
+ vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
+ GrGpuBufferType::kVertex, sizeof(kMSAAVertexData), kMSAAVertexData,
+ gMSAAVertexBufferKey);
+
+ indexCount = SK_ARRAY_COUNT(kMSAAIndexData);
+ }
+
+ if (!indexBuffer || !vertexBuffer) {
return;
}
- GR_DEFINE_STATIC_UNIQUE_KEY(gVertexBufferKey);
-
- sk_sp<const GrBuffer> vertexBuffer = flushState->resourceProvider()->findOrMakeStaticBuffer(
- GrGpuBufferType::kVertex, sizeof(kVertexData), kVertexData, gVertexBufferKey);
- if (!vertexBuffer) {
- return;
- }
-
- Processor proc(fFlags);
+ Processor proc(fAAType, fFlags);
SkASSERT(proc.instanceStride() == (size_t)fInstanceStride);
GrPipeline::InitArgs initArgs;
+ if (GrAAType::kMSAA == fAAType) {
+ initArgs.fFlags = GrPipeline::kHWAntialias_Flag;
+ }
initArgs.fCaps = &flushState->caps();
initArgs.fResourceProvider = flushState->resourceProvider();
initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
@@ -481,15 +751,16 @@
GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
GrMesh mesh(GrPrimitiveType::kTriangles);
- mesh.setIndexedInstanced(std::move(indexBuffer), SK_ARRAY_COUNT(kIndexData), fInstanceBuffer,
- fInstanceCount, fBaseInstance, GrPrimitiveRestart::kNo);
+ mesh.setIndexedInstanced(
+ std::move(indexBuffer), indexCount, fInstanceBuffer, fInstanceCount, fBaseInstance,
+ GrPrimitiveRestart::kNo);
mesh.setVertexData(std::move(vertexBuffer));
- flushState->rtCommandBuffer()->draw(proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1,
- this->bounds());
+ flushState->rtCommandBuffer()->draw(
+ proc, pipeline, &fixedDynamicState, nullptr, &mesh, 1, this->bounds());
}
// Will the given corner look good if we use HW derivatives?
-static bool can_use_hw_derivatives(const Sk2f& devScale, const Sk2f& cornerRadii) {
+static bool can_use_hw_derivatives_with_coverage(const Sk2f& devScale, const Sk2f& cornerRadii) {
Sk2f devRadii = devScale * cornerRadii;
if (devRadii[1] < devRadii[0]) {
devRadii = SkNx_shuffle<1,0>(devRadii);
@@ -500,13 +771,14 @@
return minDevRadius * minDevRadius * 5 > devRadii[1];
}
-static bool can_use_hw_derivatives(const Sk2f& devScale, const SkVector& cornerRadii) {
- return can_use_hw_derivatives(devScale, Sk2f::Load(&cornerRadii));
+static bool can_use_hw_derivatives_with_coverage(
+ const Sk2f& devScale, const SkVector& cornerRadii) {
+ return can_use_hw_derivatives_with_coverage(devScale, Sk2f::Load(&cornerRadii));
}
// Will the given round rect look good if we use HW derivatives?
-static bool can_use_hw_derivatives(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
- const SkRRect& rrect) {
+static bool can_use_hw_derivatives_with_coverage(
+ const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, const SkRRect& rrect) {
if (!shaderCaps.shaderDerivativeSupport()) {
return false;
}
@@ -521,27 +793,27 @@
case SkRRect::kOval_Type:
case SkRRect::kSimple_Type:
- return can_use_hw_derivatives(devScale, rrect.getSimpleRadii());
+ return can_use_hw_derivatives_with_coverage(devScale, rrect.getSimpleRadii());
case SkRRect::kNinePatch_Type: {
Sk2f r0 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect));
Sk2f r1 = Sk2f::Load(SkRRectPriv::GetRadiiArray(rrect) + 2);
Sk2f minRadii = Sk2f::Min(r0, r1);
Sk2f maxRadii = Sk2f::Max(r0, r1);
- return can_use_hw_derivatives(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
- can_use_hw_derivatives(devScale, Sk2f(maxRadii[0], minRadii[1]));
+ return can_use_hw_derivatives_with_coverage(devScale, Sk2f(minRadii[0], maxRadii[1])) &&
+ can_use_hw_derivatives_with_coverage(devScale, Sk2f(maxRadii[0], minRadii[1]));
}
case SkRRect::kComplex_Type: {
for (int i = 0; i < 4; ++i) {
auto corner = static_cast<SkRRect::Corner>(i);
- if (!can_use_hw_derivatives(devScale, rrect.radii(corner))) {
+ if (!can_use_hw_derivatives_with_coverage(devScale, rrect.radii(corner))) {
return false;
}
}
return true;
}
}
- SK_ABORT("Unreachable code.");
+ SK_ABORT("Invalid round rect type.");
return false; // Add this return to keep GCC happy.
}