diff options
author | Michael Ludwig <michaelludwig@google.com> | 2018-11-28 12:05:46 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-11-28 19:08:34 +0000 |
commit | d3c92d9a368be2d1f592bef4e65727721fc3ecf8 (patch) | |
tree | b7aa6292d3be171a32f7a0544677c0655e5b06c6 | |
parent | 12c0e50fda17dc5e337553c8a244e18dc83249a9 (diff) | |
download | skqp-d3c92d9a368be2d1f592bef4e65727721fc3ecf8.tar.gz |
Initial definition of fill rect op
Bug: skia:
Change-Id: Ie0c99eb5163501853d1adc885bd3841f90a71924
Reviewed-on: https://skia-review.googlesource.com/c/163486
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
-rw-r--r-- | gm/drawquadset.cpp | 230 | ||||
-rw-r--r-- | gn/gm.gni | 1 | ||||
-rw-r--r-- | gn/gpu.gni | 2 | ||||
-rw-r--r-- | src/gpu/GrProcessor.h | 1 | ||||
-rw-r--r-- | src/gpu/GrQuad.cpp | 25 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.cpp | 9 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.h | 11 | ||||
-rw-r--r-- | src/gpu/ops/GrFillRectOp.cpp | 545 | ||||
-rw-r--r-- | src/gpu/ops/GrFillRectOp.h | 57 | ||||
-rw-r--r-- | src/gpu/ops/GrQuadPerEdgeAA.h | 1 | ||||
-rw-r--r-- | src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp | 14 | ||||
-rw-r--r-- | src/gpu/ops/GrSimpleMeshDrawOpHelper.h | 12 | ||||
-rw-r--r-- | tools/gpu/GrTest.cpp | 2 |
13 files changed, 895 insertions, 15 deletions
diff --git a/gm/drawquadset.cpp b/gm/drawquadset.cpp new file mode 100644 index 0000000000..9c3c6f09b5 --- /dev/null +++ b/gm/drawquadset.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" + +#include "GrClip.h" +#include "GrContext.h" +#include "GrRenderTargetContext.h" +#include "GrSurfaceContextPriv.h" +#include "SkGr.h" +#include "SkGradientShader.h" + +static constexpr SkScalar kTileWidth = 40; +static constexpr SkScalar kTileHeight = 30; + +static constexpr int kRowCount = 4; +static constexpr int kColCount = 3; + +static void draw_text(SkCanvas* canvas, const char* text) { + SkPaint paint; + paint.setColor(SK_ColorBLACK); + paint.setTextSize(12.0f); + paint.setAntiAlias(true); + + canvas->drawString(text, 0, 0, paint); +} + +static void draw_gradient_tiles(GrRenderTargetContext* rtc, const SkMatrix& view, + bool adjustLocal) { + GrRenderTargetContext::QuadSetEntry quads[kRowCount * kColCount]; + + for (int i = 0; i < kRowCount; ++i) { + for (int j = 0; j < kColCount; ++j) { + SkRect tile = SkRect::MakeXYWH(j * kTileWidth, i * kTileHeight, kTileWidth, kTileHeight); + + int q = i * kColCount + j; + quads[q].fRect = tile; + quads[q].fColor = {1.f, 1.f, 1.f, 1.f}; + + if (adjustLocal) { + quads[q].fLocalMatrix.setTranslate(-tile.fLeft, -tile.fTop); + } else { + quads[q].fLocalMatrix.setIdentity(); + } + + quads[q].fAAFlags = GrQuadAAFlags::kNone; + if (i == 0) { + quads[q].fAAFlags |= GrQuadAAFlags::kTop; + } + if (i == kRowCount - 1) { + quads[q].fAAFlags |= GrQuadAAFlags::kBottom; + } + if (j == 0) { + quads[q].fAAFlags |= GrQuadAAFlags::kLeft; + } + if (j == kColCount - 1) { + quads[q].fAAFlags |= GrQuadAAFlags::kRight; + } + } + } + + // Make a shared gradient paint + static constexpr SkPoint pts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} }; + static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorWHITE }; + + auto gradient = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode); + SkPaint paint; + paint.setShader(gradient); + GrPaint grPaint; + SkPaintToGrPaint(rtc->surfPriv().getContext(), rtc->colorSpaceInfo(), paint, view, &grPaint); + // And use private API to use GrFillRectOp + rtc->drawQuadSet(GrNoClip(), std::move(grPaint), GrAA::kYes, view, quads, SK_ARRAY_COUNT(quads)); +} + +static void draw_color_tiles(GrRenderTargetContext* rtc, const SkMatrix& view, bool multicolor) { + GrRenderTargetContext::QuadSetEntry quads[kRowCount * kColCount]; + + for (int i = 0; i < kRowCount; ++i) { + for (int j = 0; j < kColCount; ++j) { + SkRect tile = SkRect::MakeXYWH(j * kTileWidth, i * kTileHeight, kTileWidth, kTileHeight); + + int q = i * kColCount + j; + quads[q].fRect = tile; + quads[q].fLocalMatrix.setIdentity(); + + if (multicolor) { + quads[q].fColor = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f}; + } else { + quads[q].fColor = {.2f, .8f, .3f, 1.f}; + } + + quads[q].fAAFlags = GrQuadAAFlags::kNone; + if (i == 0) { + quads[q].fAAFlags |= GrQuadAAFlags::kTop; + } + if (i == kRowCount - 1) { + quads[q].fAAFlags |= GrQuadAAFlags::kBottom; + } + if (j == 0) { + quads[q].fAAFlags |= GrQuadAAFlags::kLeft; + } + if (j == kColCount - 1) { + quads[q].fAAFlags |= GrQuadAAFlags::kRight; + } + } + } + + GrPaint grPaint; + // And use private API to use GrFillRectOp + rtc->drawQuadSet(GrNoClip(), std::move(grPaint), GrAA::kYes, view, quads, SK_ARRAY_COUNT(quads)); +} + +static void draw_tile_boundaries(SkCanvas* canvas, const SkMatrix& local) { + // Draw grid of red lines at interior tile boundaries. + static constexpr SkScalar kLineOutset = 10.f; + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorRED); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0.f); + for (int x = 1; x < kColCount; ++x) { + SkPoint pts[] = {{x * kTileWidth, 0}, {x * kTileWidth, kRowCount * kTileHeight}}; + local.mapPoints(pts, 2); + SkVector v = pts[1] - pts[0]; + v.setLength(v.length() + kLineOutset); + canvas->drawLine(pts[1] - v, pts[0] + v, paint); + } + for (int y = 1; y < kRowCount; ++y) { + SkPoint pts[] = {{0, y * kTileHeight}, {kTileWidth * kColCount, y * kTileHeight}}; + local.mapPoints(pts, 2); + SkVector v = pts[1] - pts[0]; + v.setLength(v.length() + kLineOutset); + canvas->drawLine(pts[1] - v, pts[0] + v, paint); + } +} + +// Tile renderers (column variation) +typedef void (*TileRenderer)(GrRenderTargetContext*, const SkMatrix&); +static TileRenderer kTileSets[] = { + [](GrRenderTargetContext* rtc, const SkMatrix& view) { draw_gradient_tiles(rtc, view, true); }, + [](GrRenderTargetContext* rtc, const SkMatrix& view) { draw_gradient_tiles(rtc, view, false); }, + [](GrRenderTargetContext* rtc, const SkMatrix& view) { draw_color_tiles(rtc, view, false); }, + [](GrRenderTargetContext* rtc, const SkMatrix& view) { draw_color_tiles(rtc, view, true); }, +}; +static const char* kTileSetNames[] = { "Local", "Aligned", "Green", "Multicolor" }; +static_assert(SK_ARRAY_COUNT(kTileSets) == SK_ARRAY_COUNT(kTileSetNames), "Count mismatch"); + +namespace skiagm { + +class DrawQuadSetGM : public GM { +private: + SkString onShortName() final { return SkString("draw_quad_set"); } + SkISize onISize() override { return SkISize::Make(800, 800); } + + void onDraw(SkCanvas* canvas) override { + GrContext* ctx = canvas->getGrContext(); + if (!ctx) { + DrawGpuOnlyMessage(canvas); + return; + } + GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext(); + SkASSERT(rtc); + + SkMatrix rowMatrices[5]; + // Identity + rowMatrices[0].setIdentity(); + // Translate/scale + rowMatrices[1].setTranslate(5.5f, 20.25f); + rowMatrices[1].postScale(.9f, .7f); + // Rotation + rowMatrices[2].setRotate(20.0f); + rowMatrices[2].preTranslate(15.f, -20.f); + // Skew + rowMatrices[3].setSkew(.5f, .25f); + rowMatrices[3].preTranslate(-30.f, 0.f); + // Perspective + SkPoint src[4]; + SkRect::MakeWH(kColCount * kTileWidth, kRowCount * kTileHeight).toQuad(src); + SkPoint dst[4] = {{0, 0}, + {kColCount * kTileWidth + 10.f, 15.f}, + {kColCount * kTileWidth - 28.f, kRowCount * kTileHeight + 40.f}, + {25.f, kRowCount * kTileHeight - 15.f}}; + SkAssertResult(rowMatrices[4].setPolyToPoly(src, dst, 4)); + rowMatrices[4].preTranslate(0.f, +10.f); + static const char* matrixNames[] = { "Identity", "T+S", "Rotate", "Skew", "Perspective" }; + static_assert(SK_ARRAY_COUNT(matrixNames) == SK_ARRAY_COUNT(rowMatrices), "Count mismatch"); + + // Print a column header + canvas->save(); + canvas->translate(110.f, 20.f); + for (size_t j = 0; j < SK_ARRAY_COUNT(kTileSetNames); ++j) { + draw_text(canvas, kTileSetNames[j]); + canvas->translate(kColCount * kTileWidth + 30.f, 0.f); + } + canvas->restore(); + canvas->translate(0.f, 40.f); + + // Render all tile variations + for (size_t i = 0; i < SK_ARRAY_COUNT(rowMatrices); ++i) { + canvas->save(); + canvas->translate(10.f, 0.5f * kRowCount * kTileHeight); + draw_text(canvas, matrixNames[i]); + + canvas->translate(100.f, -0.5f * kRowCount * kTileHeight); + for (size_t j = 0; j < SK_ARRAY_COUNT(kTileSets); ++j) { + canvas->save(); + draw_tile_boundaries(canvas, rowMatrices[i]); + + canvas->concat(rowMatrices[i]); + kTileSets[j](rtc, canvas->getTotalMatrix()); + // Undo the local transformation + canvas->restore(); + // And advance to the next column + canvas->translate(kColCount * kTileWidth + 30.f, 0.f); + } + // Reset back to the left edge + canvas->restore(); + // And advance to the next row + canvas->translate(0.f, kRowCount * kTileHeight + 20.f); + } + } +}; + +DEF_GM(return new DrawQuadSetGM();) + +} // namespace skiagm @@ -123,6 +123,7 @@ gm_sources = [ "$_gm/drawlooper.cpp", "$_gm/drawimageset.cpp", "$_gm/drawminibitmaprect.cpp", + "$_gm/drawquadset.cpp", "$_gm/drawregion.cpp", "$_gm/drawregionmodes.cpp", "$_gm/dropshadowimagefilter.cpp", diff --git a/gn/gpu.gni b/gn/gpu.gni index 651fd16b7a..a8a5bb16e3 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -262,6 +262,8 @@ skia_gpu_sources = [ "$_src/gpu/ops/GrDrawOp.h", "$_src/gpu/ops/GrDrawVerticesOp.cpp", "$_src/gpu/ops/GrDrawVerticesOp.h", + "$_src/gpu/ops/GrFillRectOp.cpp", + "$_src/gpu/ops/GrFillRectOp.h", "$_src/gpu/ops/GrMeshDrawOp.cpp", "$_src/gpu/ops/GrMeshDrawOp.h", "$_src/gpu/ops/GrNonAAFillRectOp.cpp", diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h index de3925d0c7..b8338b3b5b 100644 --- a/src/gpu/GrProcessor.h +++ b/src/gpu/GrProcessor.h @@ -155,6 +155,7 @@ public: kPorterDuffXferProcessor_ClassID, kPremulFragmentProcessor_ClassID, kQuadEdgeEffect_ClassID, + kQuadPerEdgeAAGeometryProcessor_ClassID, kReplaceInputFragmentProcessor_ClassID, kRRectsGaussianEdgeFP_ClassID, kSeriesFragmentProcessor_ClassID, diff --git a/src/gpu/GrQuad.cpp b/src/gpu/GrQuad.cpp index cce49f19c3..567301d446 100644 --- a/src/gpu/GrQuad.cpp +++ b/src/gpu/GrQuad.cpp @@ -19,7 +19,21 @@ // Allow some tolerance from floating point matrix transformations, but SkScalarNearlyEqual doesn't // support comparing infinity, and coords_form_rect should return true for infinite edges #define NEARLY_EQUAL(f1, f2) (f1 == f2 || SkScalarNearlyEqual(f1, f2, 1e-5f)) -#define NEARLY_ZERO(f1) NEARLY_EQUAL(f1, 0.f) +// Similarly, support infinite rectangles by looking at the sign of infinities +static bool dot_nearly_zero(const SkVector& e1, const SkVector& e2) { + static constexpr auto dot = SkPoint::DotProduct; + static constexpr auto sign = SkScalarSignAsScalar; + + SkScalar dotValue = dot(e1, e2); + if (SkScalarIsNaN(dotValue)) { + // Form vectors from the signs of infinities, and check their dot product + dotValue = dot({sign(e1.fX), sign(e1.fY)}, {sign(e2.fX), sign(e2.fY)}); + } + + // Unfortunately must have a pretty healthy tolerance here or transformed rects that are + // effectively rectilinear will have edge dot products of around .005 + return SkScalarNearlyZero(dotValue, 1e-2f); +} // This is not the most performance critical function; code using GrQuad should rely on the faster // quad type from matrix path, so this will only be called as part of SkASSERT. @@ -31,18 +45,13 @@ static bool coords_form_rect(const float xs[4], const float ys[4]) { } static bool coords_rectilinear(const float xs[4], const float ys[4]) { - // Edge from 0 to 1 should have the same length as edge from 3 to 2 - // and edge from 1 to 3 should have the same length as edge from 2 to 0 - // noo that makes it a parallelogram, need dot product between edge 0 to 1 and edge 1 to 3 = 0 - // and 0-2 and 2-3 is 0. - static constexpr auto dot = SkPoint::DotProduct; SkVector e0{xs[1] - xs[0], ys[1] - ys[0]}; // Connects to e1 and e2(repeat) SkVector e1{xs[3] - xs[1], ys[3] - ys[1]}; // connects to e0(repeat) and e3 SkVector e2{xs[0] - xs[2], ys[0] - ys[2]}; // connects to e0 and e3(repeat) SkVector e3{xs[2] - xs[3], ys[2] - ys[3]}; // connects to e1(repeat) and e2 - return NEARLY_ZERO(dot(e0, e1)) && NEARLY_ZERO(dot(e1, e3)) && - NEARLY_ZERO(dot(e2, e0)) && NEARLY_ZERO(dot(e3, e2)); + return dot_nearly_zero(e0, e1) && dot_nearly_zero(e1, e3) && + dot_nearly_zero(e2, e0) && dot_nearly_zero(e3, e2); } GrQuadType GrQuad::quadType() const { diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index 33c470a4b5..bdcfd4ecc3 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -44,6 +44,7 @@ #include "ops/GrDrawAtlasOp.h" #include "ops/GrDrawOp.h" #include "ops/GrDrawVerticesOp.h" +#include "ops/GrFillRectOp.h" #include "ops/GrAAFillRRectOp.h" #include "ops/GrLatticeOp.h" #include "ops/GrOp.h" @@ -638,6 +639,14 @@ void GrRenderTargetContext::drawRect(const GrClip& clip, this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(rect, *style)); } +void GrRenderTargetContext::drawQuadSet(const GrClip& clip, GrPaint&& paint, GrAA aa, + const SkMatrix& viewMatrix, const QuadSetEntry quads[], + int cnt) { + GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo); + this->addDrawOp(clip, GrFillRectOp::MakeSet(fContext, std::move(paint), aaType, viewMatrix, + quads, cnt)); +} + int GrRenderTargetContextPriv::maxWindowRectangles() const { return fRenderTargetContext->fRenderTargetProxy->maxWindowRectangles( *fRenderTargetContext->caps()); diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h index b4114f717c..4c73746897 100644 --- a/src/gpu/GrRenderTargetContext.h +++ b/src/gpu/GrRenderTargetContext.h @@ -127,6 +127,17 @@ public: const SkRect& rect, const SkMatrix& localMatrix); + /** Used with drawQuadSet */ + struct QuadSetEntry { + SkRect fRect; + SkPMColor4f fColor; // Overrides any color on the GrPaint + SkMatrix fLocalMatrix; + GrQuadAAFlags fAAFlags; + }; + + void drawQuadSet(const GrClip& clip, GrPaint&& paint, GrAA aa, const SkMatrix& viewMatrix, + const QuadSetEntry[], int cnt); + /** * Creates an op that draws a subrectangle of a texture. The passed color is modulated by the * texture's color. 'srcRect' specifies the rectangle of the texture to draw. 'dstRect' diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp new file mode 100644 index 0000000000..475d0d1ed4 --- /dev/null +++ b/src/gpu/ops/GrFillRectOp.cpp @@ -0,0 +1,545 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrFillRectOp.h" + +#include "GrGeometryProcessor.h" +#include "GrMeshDrawOp.h" +#include "GrPaint.h" +#include "GrQuad.h" +#include "GrQuadPerEdgeAA.h" +#include "GrSimpleMeshDrawOpHelper.h" +#include "SkMatrix.h" +#include "SkRect.h" +#include "glsl/GrGLSLColorSpaceXformHelper.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" + +namespace { + +using VertexSpec = GrQuadPerEdgeAA::VertexSpec; +using ColorType = GrQuadPerEdgeAA::ColorType; + +// NOTE: This info structure is intentionally modeled after GrTextureOps' Quad so that they can +// more easily be integrated together in the future. +class TransformedQuad { +public: + TransformedQuad(const GrPerspQuad& deviceQuad, const GrPerspQuad& localQuad, + const SkPMColor4f& color, GrQuadAAFlags aaFlags) + : fDeviceQuad(deviceQuad) + , fLocalQuad(localQuad) + , fColor(color) + , fAAFlags(aaFlags) {} + + const GrPerspQuad& deviceQuad() const { return fDeviceQuad; } + const GrPerspQuad& localQuad() const { return fLocalQuad; } + const SkPMColor4f& color() const { return fColor; } + GrQuadAAFlags aaFlags() const { return fAAFlags; } + + void setColor(const SkPMColor4f& color) { fColor = color; } + + SkString dumpInfo(int index) const { + GrQuadAAFlags edges = static_cast<GrQuadAAFlags>(fAAFlags); + SkString str; + str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n" + " device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), " + "(%.2f, %.2f, %.2f)],\n" + " local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), " + "(%.2f, %.2f, %.2f)]\n", + index, fColor.fR, fColor.fG, fColor.fB, fColor.fA, + edges & GrQuadAAFlags::kLeft, edges & GrQuadAAFlags::kTop, + edges & GrQuadAAFlags::kRight, edges & GrQuadAAFlags::kBottom, + fDeviceQuad.x(0), fDeviceQuad.y(0), fDeviceQuad.w(0), + fDeviceQuad.x(1), fDeviceQuad.y(1), fDeviceQuad.w(1), + fDeviceQuad.x(2), fDeviceQuad.y(2), fDeviceQuad.w(2), + fDeviceQuad.x(3), fDeviceQuad.y(3), fDeviceQuad.w(3), + fLocalQuad.x(0), fLocalQuad.y(0), fLocalQuad.w(0), + fLocalQuad.x(1), fLocalQuad.y(1), fLocalQuad.w(1), + fLocalQuad.x(2), fLocalQuad.y(2), fLocalQuad.w(2), + fLocalQuad.x(3), fLocalQuad.y(3), fLocalQuad.w(3)); + return str; + } +private: + // NOTE: The TransformedQuad does not store the types for device and local. The owning op tracks + // the most general type for device and local across all of its merged quads. + GrPerspQuad fDeviceQuad; // In device space, allowing rects to be combined across view matrices + GrPerspQuad fLocalQuad; // Original rect transformed by its local matrix + SkPMColor4f fColor; + GrQuadAAFlags fAAFlags; +}; + +// A GeometryProcessor for rendering TransformedQuads using the vertex attributes from +// GrQuadPerEdgeAA. This is similar to the TextureGeometryProcessor of GrTextureOp except that it +// handles full GrPaints. +class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor { +public: + + static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) { + return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec)); + } + + const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; } + + void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { + // The attributes' key includes the device and local quad types implicitly since those + // types decide the vertex attribute size + b->add32(fAttrs.getKey()); + } + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override { + class GLSLProcessor : public GrGLSLGeometryProcessor { + public: + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, + FPCoordTransformIter&& transformIter) override { + const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>(); + if (gp.fAttrs.hasLocalCoords()) { + this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); + } + } + + private: + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { + const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>(); + args.fVaryingHandler->emitAttributes(gp); + gpArgs->fPositionVar = gp.fAttrs.positions().asShaderVar(); + + if (gp.fAttrs.hasLocalCoords()) { + this->emitTransforms(args.fVertBuilder, + args.fVaryingHandler, + args.fUniformHandler, + gp.fAttrs.localCoords().asShaderVar(), + args.fFPCoordTransformHandler); + } + + gp.fAttrs.emitColor(args, "paintColor"); + gp.fAttrs.emitCoverage(args, "aaDist"); + } + }; + return new GLSLProcessor; + } + +private: + QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec) + : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID) + , fAttrs(spec) { + SkASSERT(spec.hasVertexColors()); + this->setVertexAttributes(fAttrs.attributes(), fAttrs.attributeCount()); + } + + GrQuadPerEdgeAA::GPAttributes fAttrs; + + typedef GrGeometryProcessor INHERITED; +}; + +class FillRectOp final : public GrMeshDrawOp { +private: + using Helper = GrSimpleMeshDrawOpHelperWithStencil; + +public: + static std::unique_ptr<GrDrawOp> Make(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const GrUserStencilSettings* stencilSettings, + const GrPerspQuad& deviceQuad, + GrQuadType deviceQuadType, + const GrPerspQuad& localQuad, + GrQuadType localQuadType) { + // Clean up deviations between aaType and edgeAA + GrResolveAATypeForQuad(aaType, edgeAA, deviceQuad, deviceQuadType, &aaType, &edgeAA); + + // Analyze the paint to see if it is compatible with scissor-clearing + SkPMColor4f color = paint.getColor4f(); + // Only non-null if the paint can be turned into a clear, it can be a local pointer since + // the op ctor consumes the value right away if it's provided + SkPMColor4f* clearColor = nullptr; + if (paint.isTrivial() || paint.isConstantBlendedColor(&color)) { + clearColor = &color; + } + + return Helper::FactoryHelper<FillRectOp>(context, std::move(paint), clearColor, aaType, + edgeAA, stencilSettings, deviceQuad, deviceQuadType, localQuad, localQuadType); + } + + // Analysis of the GrPaint to determine the const blend color must be done before, passing + // nullptr for constBlendColor disables all scissor-clear optimizations (must keep the + // paintColor argument because it is assumed by the GrSimpleMeshDrawOpHelper). Similarly, aaType + // is passed to Helper in the initializer list, so incongruities between aaType and edgeFlags + // must be resolved prior to calling this constructor. + FillRectOp(Helper::MakeArgs args, SkPMColor4f paintColor, const SkPMColor4f* constBlendColor, + GrAAType aaType, GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil, + const GrPerspQuad& deviceQuad, GrQuadType deviceQuadType, + const GrPerspQuad& localQuad, GrQuadType localQuadType) + : INHERITED(ClassID()) + , fHelper(args, aaType, stencil) + , fDeviceQuadType(static_cast<unsigned>(deviceQuadType)) + , fLocalQuadType(static_cast<unsigned>(localQuadType)) { + if (constBlendColor) { + // The GrPaint is compatible with clearing, and the constant blend color overrides the + // paint color (although in most cases they are probably the same) + paintColor = *constBlendColor; + // However, just because the paint is compatible, the device quad must also be a rect + // that is non-AA (AA aligned with pixel bounds should have already been turned into + // non-AA). + fClearCompatible = deviceQuadType == GrQuadType::kRect && aaType == GrAAType::kNone; + } else { + // Paint isn't clear compatible + fClearCompatible = false; + } + + fWideColor = !SkPMColor4fFitsInBytes(paintColor); + + // The color stored with the quad is the clear color if a scissor-clear is decided upon + // when executing the op. + fQuads.emplace_back(deviceQuad, localQuad, paintColor, edgeFlags); + this->setBounds(deviceQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage), + IsZeroArea::kNo); + } + + const char* name() const override { return "FillRectOp"; } + + void visitProxies(const VisitProxyFunc& func, VisitorType) const override { + return fHelper.visitProxies(func); + } + +#ifdef SK_DEBUG + SkString dumpInfo() const override { + SkString str; + str.appendf("# draws: %d\n", fQuads.count()); + str.appendf("Clear compatible: %u\n", static_cast<bool>(fClearCompatible)); + str.appendf("Device quad type: %u, local quad type: %u\n", + static_cast<GrQuadType>(fDeviceQuadType), + static_cast<GrQuadType>(fLocalQuadType)); + str += fHelper.dumpInfo(); + for (int i = 0; i < fQuads.count(); i++) { + str += fQuads[i].dumpInfo(i); + + } + str += INHERITED::dumpInfo(); + return str; + } +#endif + + RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { + // Initialize aggregate color analysis with the first quad's color (which always exists) + SkASSERT(fQuads.count() > 0); + GrProcessorAnalysisColor quadColors(fQuads[0].color()); + // Then combine the colors of any additional quads (e.g. from MakeSet) + for (int i = 1; i < fQuads.count(); ++i) { + quadColors = GrProcessorAnalysisColor::Combine(quadColors, fQuads[i].color()); + } + auto result = fHelper.xpRequiresDstTexture( + caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, &quadColors); + // If there is a constant color after analysis, that means all of the quads should be set + // to the same color (even if they started out with different colors). + SkPMColor4f colorOverride; + if (quadColors.isConstant(&colorOverride)) { + for (int i = 0; i < fQuads.count(); ++i) { + fQuads[i].setColor(colorOverride); + } + } + + return result; + } + + FixedFunctionFlags fixedFunctionFlags() const override { + // Since the AA type of the whole primitive is kept consistent with the per edge AA flags + // the helper's fixed function flags are appropriate. + return fHelper.fixedFunctionFlags(); + } + + DEFINE_OP_CLASS_ID + +private: + // For GrFillRectOp::MakeSet's use of addQuad + // FIXME(reviewer): better to just make addQuad public? + friend std::unique_ptr<GrDrawOp> GrFillRectOp::MakeSet(GrContext* context, GrPaint&& paint, + GrAAType aaType, const SkMatrix& viewMatrix, + const GrRenderTargetContext::QuadSetEntry quads[], int quadCount, + const GrUserStencilSettings* stencilSettings); + + void onPrepareDraws(Target* target) override { + TRACE_EVENT0("skia", TRACE_FUNC); + + using Domain = GrQuadPerEdgeAA::Domain; + static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty(); + + VertexSpec vertexSpec(this->deviceQuadType(), + fWideColor ? ColorType::kHalf : ColorType::kByte, + this->localQuadType(), fHelper.usesLocalCoords(), Domain::kNo, + fHelper.aaType()); + + sk_sp<GrGeometryProcessor> gp = QuadPerEdgeAAGeometryProcessor::Make(vertexSpec); + size_t vertexSize = gp->vertexStride(); + + const GrBuffer* vbuffer; + int vertexOffsetInBuffer = 0; + + // Fill the allocated vertex data + void* vdata = target->makeVertexSpace(vertexSize, fQuads.count() * 4, &vbuffer, + &vertexOffsetInBuffer); + if (!vdata) { + SkDebugf("Could not allocate vertices\n"); + return; + } + + // vertices pointer advances through vdata based on Tessellate's return value + void* vertices = vdata; + for (int i = 0; i < fQuads.count(); ++i) { + const auto& q = fQuads[i]; + vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, q.deviceQuad(), q.color(), + q.localQuad(), kEmptyDomain, q.aaFlags()); + } + + // Configure the mesh for the vertex data + GrMesh* mesh; + if (fQuads.count() > 1) { + mesh = target->allocMesh(GrPrimitiveType::kTriangles); + sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer(); + if (!ibuffer) { + SkDebugf("Could not allocate quad indices\n"); + return; + } + mesh->setIndexedPatterned(ibuffer.get(), 6, 4, fQuads.count(), + GrResourceProvider::QuadCountOfQuadBuffer()); + } else { + mesh = target->allocMesh(GrPrimitiveType::kTriangleStrip); + mesh->setNonIndexedNonInstanced(4); + } + mesh->setVertexData(vbuffer, vertexOffsetInBuffer); + + auto pipe = fHelper.makePipeline(target); + target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh); + } + + CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override { + TRACE_EVENT0("skia", TRACE_FUNC); + const auto* that = t->cast<FillRectOp>(); + + // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa + // draw ops together, so pass true as the last argument. + if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) { + return CombineResult::kCannotCombine; + } + + // If the processor sets are compatible, the two ops are always compatible; it just needs + // to adjust the state of the op to be the more general quad and aa types of the two ops. + + // The GrQuadType enum is ordered such that higher values are more general quad types + if (that->fDeviceQuadType > fDeviceQuadType) { + fDeviceQuadType = that->fDeviceQuadType; + } + if (that->fLocalQuadType > fLocalQuadType) { + fLocalQuadType = that->fLocalQuadType; + } + fClearCompatible &= that->fClearCompatible; + fWideColor |= that->fWideColor; + + // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa + // types to be none and coverage, in which case this op's aa type must be lifted to coverage + // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed. + if (fHelper.aaType() == GrAAType::kNone && that->fHelper.aaType() == GrAAType::kCoverage) { + fHelper.setAAType(GrAAType::kCoverage); + } + + fQuads.push_back_n(that->fQuads.count(), that->fQuads.begin()); + return CombineResult::kMerged; + } + + // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible. + // But since it's avoiding the op list management, it must update the op's bounds. This is only + // used with quad sets, which uses the same view matrix for each quad so this assumes that the + // device quad type of the new quad is the same as the op's. + void addQuad(TransformedQuad&& quad, GrQuadType localQuadType, GrAAType aaType) { + SkASSERT(quad.deviceQuad().quadType() <= this->deviceQuadType()); + + // The new quad's aa type should be the same, or converted to none, which means this op's + // helper's aatype never has to be updated + SkASSERT(aaType == fHelper.aaType() || aaType == GrAAType::kNone); + + // The new quad's local coordinates could differ + if (localQuadType > this->localQuadType()) { + fLocalQuadType = static_cast<unsigned>(localQuadType); + } + + // clear compatible won't need to be updated, since device quad type and paint is the same, + // but this quad has a new color, so maybe update wide color + fWideColor |= !SkPMColor4fFitsInBytes(quad.color()); + + // Update the bounds and add the quad to this op's storage + SkRect newBounds = this->bounds(); + newBounds.joinPossiblyEmptyRect(quad.deviceQuad().bounds()); + this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage), + IsZeroArea::kNo); + fQuads.push_back(std::move(quad)); + } + + GrQuadType deviceQuadType() const { return static_cast<GrQuadType>(fDeviceQuadType); } + GrQuadType localQuadType() const { return static_cast<GrQuadType>(fLocalQuadType); } + + Helper fHelper; + SkSTArray<1, TransformedQuad, true> fQuads; + + // While we always store full GrPerspQuads in memory, if the type is known to be simpler we can + // optimize our geometry generation. + unsigned fDeviceQuadType: 2; + unsigned fLocalQuadType: 2; + unsigned fWideColor: 1; + + // True if fQuad produced by a rectangle-preserving view matrix, is pixel aligned or non-AA, + // and its paint is a constant blended color. + unsigned fClearCompatible: 1; + + typedef GrMeshDrawOp INHERITED; +}; + +} // anonymous namespace + +namespace GrFillRectOp { + +std::unique_ptr<GrDrawOp> Make(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkRect& rect, + const GrUserStencilSettings* stencilSettings) { + return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, + GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), + GrPerspQuad(rect, SkMatrix::I()), GrQuadType::kRect); +} + +std::unique_ptr<GrDrawOp> MakeWithLocalMatrix(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkMatrix& localMatrix, + const SkRect& rect, + const GrUserStencilSettings* stencilSettings) { + GrQuadType localQuadType = GrQuadTypeForTransformedRect(localMatrix); + return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, + GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), + GrPerspQuad(rect, localMatrix), localQuadType); +} + +std::unique_ptr<GrDrawOp> MakeWithLocalRect(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& localRect, + const GrUserStencilSettings* stencilSettings) { + return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, + GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), + GrPerspQuad(localRect, SkMatrix::I()), GrQuadType::kRect); +} + +std::unique_ptr<GrDrawOp> MakeSet(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + const SkMatrix& viewMatrix, + const GrRenderTargetContext::QuadSetEntry quads[], + int cnt, + const GrUserStencilSettings* stencilSettings) { + // First make a draw op for the first quad in the set + SkASSERT(cnt > 0); + GrQuadType deviceQuadType = GrQuadTypeForTransformedRect(viewMatrix); + + paint.setColor4f(quads[0].fColor); + std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType, + quads[0].fAAFlags, stencilSettings, GrPerspQuad(quads[0].fRect, viewMatrix), + deviceQuadType, GrPerspQuad(quads[0].fRect, quads[0].fLocalMatrix), + GrQuadTypeForTransformedRect(quads[0].fLocalMatrix)); + auto* fillRects = op->cast<FillRectOp>(); + + // Accumulate remaining quads similar to onCombineIfPossible() without creating an op + for (int i = 1; i < cnt; ++i) { + GrPerspQuad deviceQuad(quads[i].fRect, viewMatrix); + + GrAAType resolvedAA; + GrQuadAAFlags resolvedEdgeFlags; + GrResolveAATypeForQuad(aaType, quads[i].fAAFlags, deviceQuad, deviceQuadType, + &resolvedAA, &resolvedEdgeFlags); + + fillRects->addQuad({ deviceQuad, GrPerspQuad(quads[i].fRect, quads[i].fLocalMatrix), + quads[i].fColor, resolvedEdgeFlags }, + GrQuadTypeForTransformedRect(quads[i].fLocalMatrix), resolvedAA); + } + + return op; +} + +} // namespace GrFillRectOp + +#if GR_TEST_UTILS + +#include "GrDrawOpTest.h" +#include "SkGr.h" + +GR_DRAW_OP_TEST_DEFINE(FillRectOp) { + SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); + SkRect rect = GrTest::TestRect(random); + + GrAAType aaType = GrAAType::kNone; + if (random->nextBool()) { + aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage; + } + const GrUserStencilSettings* stencil = random->nextBool() ? nullptr + : GrGetRandomStencil(random, context); + + GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; + + if (random->nextBool()) { + if (random->nextBool()) { + if (random->nextBool()) { + // Local matrix with a set op + uint32_t extraQuadCt = random->nextRangeU(1, 4); + SkTArray<GrRenderTargetContext::QuadSetEntry> quads(extraQuadCt + 1); + quads.push_back( + {rect, SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())), + GrTest::TestMatrixInvertible(random), aaFlags}); + for (uint32_t i = 0; i < extraQuadCt; ++i) { + GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; + aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; + + quads.push_back( + {GrTest::TestRect(random), + SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())), + GrTest::TestMatrixInvertible(random), aaFlags}); + } + + return GrFillRectOp::MakeSet(context, std::move(paint), aaType, viewMatrix, + quads.begin(), quads.count(), stencil); + } else { + // Single local matrix + SkMatrix localMatrix = GrTest::TestMatrixInvertible(random); + return GrFillRectOp::MakeWithLocalMatrix(context, std::move(paint), aaType, aaFlags, + viewMatrix, localMatrix, rect, stencil); + } + } else { + // Pass local rect directly + SkRect localRect = GrTest::TestRect(random); + return GrFillRectOp::MakeWithLocalRect(context, std::move(paint), aaType, aaFlags, + viewMatrix, rect, localRect, stencil); + } + } else { + // The simplest constructor + return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags, viewMatrix, rect, + stencil); + } +} + +#endif diff --git a/src/gpu/ops/GrFillRectOp.h b/src/gpu/ops/GrFillRectOp.h new file mode 100644 index 0000000000..8d287bb458 --- /dev/null +++ b/src/gpu/ops/GrFillRectOp.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrFillRectOp_DEFINED +#define GrFillRectOp_DEFINED + +#include "GrRenderTargetContext.h" +#include "GrTypesPriv.h" + +class GrDrawOp; +class GrPaint; +struct GrUserStencilSettings; +class SkMatrix; +struct SkRect; + +namespace GrFillRectOp { + +std::unique_ptr<GrDrawOp> Make(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkRect& rect, + const GrUserStencilSettings* stencil = nullptr); + +std::unique_ptr<GrDrawOp> MakeWithLocalMatrix(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkMatrix& localMatrix, + const SkRect& rect, + const GrUserStencilSettings* stencil = nullptr); + +std::unique_ptr<GrDrawOp> MakeWithLocalRect(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + GrQuadAAFlags edgeAA, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& localRect, + const GrUserStencilSettings* stencil = nullptr); + +std::unique_ptr<GrDrawOp> MakeSet(GrContext* context, + GrPaint&& paint, + GrAAType aaType, + const SkMatrix& viewMatrix, + const GrRenderTargetContext::QuadSetEntry quads[], + int quadCount, + const GrUserStencilSettings* stencil = nullptr); +} + +#endif // GrFillRectOp_DEFINED diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h index 1f3817f0ca..cefa2ff764 100644 --- a/src/gpu/ops/GrQuadPerEdgeAA.h +++ b/src/gpu/ops/GrQuadPerEdgeAA.h @@ -134,7 +134,6 @@ namespace GrQuadPerEdgeAA { Attribute fAAEdgeDistances; // named "aaEdgeDist" in SkSL }; - // 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 // based on the configuration in the vertex spec; if that attribute is disabled in the spec, diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp index 928c4ffd7d..f8a7763971 100644 --- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp +++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp @@ -40,9 +40,14 @@ GrDrawOp::FixedFunctionFlags GrSimpleMeshDrawOpHelper::fixedFunctionFlags() cons : GrDrawOp::FixedFunctionFlags::kNone; } +static bool none_as_coverage_aa_compatible(GrAAType aa1, GrAAType aa2) { + return (aa1 == GrAAType::kNone && aa2 == GrAAType::kCoverage) || + (aa1 == GrAAType::kCoverage && aa2 == GrAAType::kNone); +} + bool GrSimpleMeshDrawOpHelper::isCompatible(const GrSimpleMeshDrawOpHelper& that, const GrCaps& caps, const SkRect& thisBounds, - const SkRect& thatBounds) const { + const SkRect& thatBounds, bool noneAsCoverageAA) const { if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) { return false; } @@ -57,7 +62,8 @@ bool GrSimpleMeshDrawOpHelper::isCompatible(const GrSimpleMeshDrawOpHelper& that } } } - bool result = fPipelineFlags == that.fPipelineFlags && fAAType == that.fAAType; + bool result = fPipelineFlags == that.fPipelineFlags && (fAAType == that.fAAType || + (noneAsCoverageAA && none_as_coverage_aa_compatible(this->aaType(), that.aaType()))); SkASSERT(!result || fCompatibleWithAlphaAsCoveage == that.fCompatibleWithAlphaAsCoveage); SkASSERT(!result || fUsesLocalCoords == that.fUsesLocalCoords); return result; @@ -178,8 +184,8 @@ GrDrawOp::FixedFunctionFlags GrSimpleMeshDrawOpHelperWithStencil::fixedFunctionF bool GrSimpleMeshDrawOpHelperWithStencil::isCompatible( const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps& caps, - const SkRect& thisBounds, const SkRect& thatBounds) const { - return INHERITED::isCompatible(that, caps, thisBounds, thatBounds) && + const SkRect& thisBounds, const SkRect& thatBounds, bool noneAsCoverageAA) const { + return INHERITED::isCompatible(that, caps, thisBounds, thatBounds, noneAsCoverageAA) && fStencilSettings == that.fStencilSettings; } diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h index b11b7d3960..69232e90e3 100644 --- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h +++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h @@ -53,8 +53,10 @@ public: GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const; + // noneAACompatibleWithCoverage should be set to true if the op can properly render a non-AA + // primitive merged into a coverage-based op. bool isCompatible(const GrSimpleMeshDrawOpHelper& that, const GrCaps&, const SkRect& thisBounds, - const SkRect& thatBounds) const; + const SkRect& thatBounds, bool noneAACompatibleWithCoverage = false) const; /** * Finalizes the processor set and determines whether the destination must be provided @@ -114,6 +116,10 @@ public: #endif GrAAType aaType() const { return static_cast<GrAAType>(fAAType); } + void setAAType(GrAAType aaType) { + fAAType = static_cast<unsigned>(aaType); + } + protected: uint32_t pipelineFlags() const { return fPipelineFlags; } @@ -165,7 +171,8 @@ public: using GrSimpleMeshDrawOpHelper::compatibleWithAlphaAsCoverage; bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps&, - const SkRect& thisBounds, const SkRect& thatBounds) const; + const SkRect& thisBounds, const SkRect& thatBounds, + bool noneAACompatibleWithCoverage = false) const; PipelineAndFixedDynamicState makePipeline(GrMeshDrawOp::Target*, int numPrimitiveProcessorTextures = 0); @@ -174,6 +181,7 @@ public: SkString dumpInfo() const; #endif GrAAType aaType() const { return INHERITED::aaType(); } + void setAAType(GrAAType aaType) { INHERITED::setAAType(aaType); } private: const GrUserStencilSettings* fStencilSettings; diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp index 9cf50b9b45..cd44ec9540 100644 --- a/tools/gpu/GrTest.cpp +++ b/tools/gpu/GrTest.cpp @@ -299,6 +299,7 @@ DRAW_OP_TEST_EXTERN(DashOp); DRAW_OP_TEST_EXTERN(DefaultPathOp); DRAW_OP_TEST_EXTERN(DIEllipseOp); DRAW_OP_TEST_EXTERN(EllipseOp); +DRAW_OP_TEST_EXTERN(FillRectOp); DRAW_OP_TEST_EXTERN(GrAtlasTextOp); DRAW_OP_TEST_EXTERN(GrDrawAtlasOp); DRAW_OP_TEST_EXTERN(GrDrawVerticesOp); @@ -326,6 +327,7 @@ void GrDrawRandomOp(SkRandom* random, GrRenderTargetContext* renderTargetContext DRAW_OP_TEST_ENTRY(DefaultPathOp), DRAW_OP_TEST_ENTRY(DIEllipseOp), DRAW_OP_TEST_ENTRY(EllipseOp), + DRAW_OP_TEST_ENTRY(FillRectOp), DRAW_OP_TEST_ENTRY(GrAtlasTextOp), DRAW_OP_TEST_ENTRY(GrDrawAtlasOp), DRAW_OP_TEST_ENTRY(GrDrawVerticesOp), |