Revert "Consolidate quad optimizations into single internal function."
This reverts commit 646616a78fe0a003c94a27e79c7e55dcc9ae2d66.
Reason for revert: Suspected as cause of layout test changes.
Original change's description:
> Consolidate quad optimizations into single internal function.
>
> Routes all non-textured quad draws through single internal function
>
> Change-Id: Ief66864a0ad2d598982c5bf500c8a84ecbf84387
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215455
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
TBR=robertphillips@google.com,michaelludwig@google.com
Change-Id: I0dc6a0d948c0f5e9221ff6c9fbbbbbb9bc3d9bc0
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221737
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/samplelocations.cpp b/gm/samplelocations.cpp
index 273d627..baec473 100644
--- a/gm/samplelocations.cpp
+++ b/gm/samplelocations.cpp
@@ -290,8 +290,9 @@
GrPaint coverPaint;
coverPaint.setColor4f({1,0,0,1});
coverPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver));
- rtc->priv().stencilRect(GrNoClip(), &kStencilCover, std::move(coverPaint), GrAA::kNo,
- SkMatrix::I(), SkRect::MakeWH(200, 200));
+ rtc->priv().drawFilledRect(
+ GrNoClip(), std::move(coverPaint), GrAA::kNo, SkMatrix::I(),
+ SkRect::MakeWH(200, 200), &kStencilCover);
// Copy offscreen texture to canvas.
rtc->drawTexture(
diff --git a/gn/tests.gni b/gn/tests.gni
index 734c1e4..e16a550 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -106,7 +106,6 @@
"$_tests/GrOpListFlushTest.cpp",
"$_tests/GrPipelineDynamicStateTest.cpp",
"$_tests/GrPorterDuffTest.cpp",
- "$_tests/GrQuadCropTest.cpp",
"$_tests/GrQuadListTest.cpp",
"$_tests/GrShapeTest.cpp",
"$_tests/GrSurfaceTest.cpp",
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c579c1c..f10bda9 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -44,7 +44,6 @@
#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
#include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadUtils.h"
#include "src/gpu/geometry/GrShape.h"
#include "src/gpu/ops/GrAtlasTextOp.h"
#include "src/gpu/ops/GrClearOp.h"
@@ -451,27 +450,67 @@
void GrRenderTargetContext::drawPaint(const GrClip& clip,
GrPaint&& paint,
const SkMatrix& viewMatrix) {
- // Start with the render target, since that is the maximum content we could possibly fill.
- // drawFilledQuad() will automatically restrict it to clip bounds for us if possible.
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawPaint", fContext);
+
+ // set rect to be big enough to fill the space, but not super-huge, so we
+ // don't overflow fixed-point implementations
+
SkRect r = fRenderTargetProxy->getBoundsRect();
+
+ // Check if we can optimize a clipped drawPaint(). We only do the transformation when there are
+ // no fragment processors because they may depend on having correct local coords and this path
+ // draws in device space without a local matrix. It currently handles converting clipRRect()
+ // to drawRRect() and solid colors to screen-filling drawRects() (which are then converted into
+ // clears if possible in drawRect).
if (!paint.numTotalFragmentProcessors()) {
- // The paint is trivial so we won't need to use local coordinates, so skip calculating the
- // inverse view matrix.
- this->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r, r);
- } else {
- // Use the inverse view matrix to arrive at appropriate local coordinates for the paint.
- SkMatrix localMatrix;
- if (!viewMatrix.invert(&localMatrix)) {
- return;
+ SkRRect rrect;
+ GrAA aa = GrAA::kNo;
+ if (clip.isRRect(r, &rrect, &aa)) {
+ if (rrect.isRect()) {
+ // Use drawFilledRect() with no clip and the reduced rectangle
+ this->drawFilledRect(GrNoClip(), std::move(paint), aa, SkMatrix::I(), rrect.rect());
+ } else {
+ // Use drawRRect() with no clip
+ this->drawRRect(GrNoClip(), std::move(paint), aa, SkMatrix::I(), rrect,
+ GrStyle::SimpleFill());
+ }
+ } else {
+ // Use drawFilledRect() with no view matrix to draw a fullscreen quad, but preserve
+ // the clip. Since the paint has no FPs we can drop the view matrix without worrying
+ // about local coordinates. If the clip is simple, drawFilledRect() will turn this into
+ // a clear or a scissored clear.
+ this->drawFilledRect(clip, std::move(paint), aa, SkMatrix::I(), r);
}
- this->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r,
- localMatrix);
+ return;
}
+
+ // Since the paint is not trivial, there's no way at this point drawRect() could have converted
+ // this drawPaint() into an optimized clear. drawRect() would then use GrFillRectOp without
+ // a local matrix, so we can simplify things and use the local matrix variant to draw a screen
+ // filling rect with the inverse view matrix for local coords, which works for all matrix
+ // conditions.
+ SkMatrix localMatrix;
+ if (!viewMatrix.invert(&localMatrix)) {
+ return;
+ }
+
+ AutoCheckFlush acf(this->drawingManager());
+ std::unique_ptr<GrDrawOp> op = GrFillRectOp::Make(
+ fContext, std::move(paint), GrAAType::kNone, GrQuadAAFlags::kNone,
+ GrQuad(r), GrQuad::MakeFromRect(r, localMatrix));
+ this->addDrawOp(clip, std::move(op));
+}
+
+static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
+ return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
+ point.fY >= rect.fTop && point.fY <= rect.fBottom;
}
// Attempts to crop a rect and optional local rect to the clip boundaries.
// Returns false if the draw can be skipped entirely.
-// FIXME to be removed once drawTexture et al are updated to use attemptQuadOptimization instead
static bool crop_filled_rect(int width, int height, const GrClip& clip,
const SkMatrix& viewMatrix, SkRect* rect,
SkRect* localRect = nullptr) {
@@ -521,186 +560,173 @@
return rect->intersect(clipBounds);
}
-enum class GrRenderTargetContext::QuadOptimization {
- // The rect to draw doesn't intersect clip or render target, so no draw op should be added
- kDiscarded,
- // The rect to draw was converted to some other op and appended to the oplist, so no additional
- // op is necessary. Currently this can convert it to a clear op or a rrect op. Only valid if
- // a constColor is provided.
- kSubmitted,
- // The clip was folded into the device quad, with updated edge flags and local coords, and
- // caller is responsible for adding an appropriate op.
- kClipApplied,
- // No change to clip, but quad updated to better fit clip/render target, and caller is
- // responsible for adding an appropriate op.
- kCropped
-};
-
-GrRenderTargetContext::QuadOptimization GrRenderTargetContext::attemptQuadOptimization(
- const GrClip& clip, const SkPMColor4f* constColor, bool allowAAChange,
- GrAA* aa, GrQuadAAFlags* edgeFlags, GrQuad* deviceQuad, GrQuad* localQuad) {
- // Optimization requirements:
- // 1. kDiscard applies when clip bounds and quad bounds do not intersect
- // 2. kClear applies when constColor and final geom is pixel aligned rect;
- // pixel aligned rect requires rect clip and (rect quad or quad covers clip)
- // 3. kRRect applies when constColor and rrect clip and quad covers clip
- // 4. kExplicitClip applies when rect clip and (rect quad or quad covers clip)
- // 5. kCropped applies when rect quad (currently)
- // 6. kNone always applies
- GrQuadAAFlags newFlags = *edgeFlags;
-
- // Must use worst case bounds so that stencil buffer updates on approximately sized
- // render targets don't get corrupted.
- const SkRect rtRect = SkRect::MakeWH(fRenderTargetProxy->worstCaseWidth(),
- fRenderTargetProxy->worstCaseHeight());
- SkRect drawBounds = deviceQuad->bounds();
- if (constColor) {
- // Don't bother updating local coordinates when the paint will ignore them anyways
- localQuad = nullptr;
+GrQuadAAFlags set_edge_flag(GrQuadAAFlags currentFlags, GrQuadAAFlags edge, GrAA edgeState) {
+ if (edgeState == GrAA::kNo) {
+ // Turn off 'edge' in currentFlags
+ return currentFlags & (~edge);
+ } else {
+ // Turn on 'edge' in currentFlags
+ return currentFlags | edge;
}
-
- // If the quad is entirely off screen, it doesn't matter what the clip does
- if (!rtRect.intersects(drawBounds)) {
- return QuadOptimization::kDiscarded;
- }
-
- // Check if clip can be represented as a rounded rect (initialize as if clip fully contained
- // the render target).
- SkRRect clipRRect = SkRRect::MakeRect(rtRect);
- GrAA clipAA = allowAAChange ? GrAA::kNo : *aa;
- bool axisAlignedClip = true;
- if (!clip.quickContains(rtRect)) {
- if (!clip.isRRect(rtRect, &clipRRect, &clipAA)) {
- axisAlignedClip = false;
- }
- }
-
- // If the clip rrect is valid (i.e. axis-aligned), we can potentially combine it with the
- // draw geometry so that no clip is needed when drawing.
- if (axisAlignedClip && (allowAAChange || clipAA == *aa)) {
- // Tighten clip bounds (if clipRRect.isRect() is true, clipBounds now holds the intersection
- // of the render target and the clip rect)
- SkRect clipBounds = rtRect;
- if (!clipBounds.intersect(clipRRect.rect()) || !clipBounds.intersects(drawBounds)) {
- return QuadOptimization::kDiscarded;
- }
-
- if (clipRRect.isRect()) {
- // No rounded corners, so the kClear and kExplicitClip optimizations are possible
- if (GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad)) {
- if (constColor && deviceQuad->quadType() == GrQuad::Type::kAxisAligned) {
- // Clear optimization is possible
- drawBounds = deviceQuad->bounds();
- if (drawBounds.contains(rtRect)) {
- // Fullscreen clear
- this->clear(nullptr, *constColor, CanClearFullscreen::kYes);
- return QuadOptimization::kSubmitted;
- } else if (GrClip::IsPixelAligned(drawBounds) &&
- drawBounds.width() > 256 && drawBounds.height() > 256) {
- // Scissor + clear (round shouldn't do anything since we are pixel aligned)
- SkIRect scissorRect;
- drawBounds.round(&scissorRect);
- this->clear(&scissorRect, *constColor, CanClearFullscreen::kNo);
- return QuadOptimization::kSubmitted;
- }
- }
-
- // Update overall AA setting.
- *edgeFlags = newFlags;
- if (*aa == GrAA::kNo && clipAA == GrAA::kYes &&
- newFlags != GrQuadAAFlags::kNone) {
- // The clip was anti-aliased and now the draw needs to be upgraded to AA to
- // properly reflect the smooth edge of the clip.
- *aa = GrAA::kYes;
- }
- // We intentionally do not downgrade AA here because we don't know if we need to
- // preserve MSAA (see GrQuadAAFlags docs). But later in the pipeline, the ops can
- // use GrResolveAATypeForQuad() to turn off coverage AA when all flags are off.
-
- // deviceQuad is exactly the intersection of original quad and clip, so it can be
- // drawn with no clip (submitted by caller)
- return QuadOptimization::kClipApplied;
- } else {
- // The quads have been updated to better fit the clip bounds, but can't get rid of
- // the clip entirely
- return QuadOptimization::kCropped;
- }
- } else if (constColor) {
- // Rounded corners and constant filled color (limit ourselves to solid colors because
- // there is no way to use custom local coordinates with drawRRect).
- if (GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad) &&
- deviceQuad->quadType() == GrQuad::Type::kAxisAligned &&
- deviceQuad->bounds().contains(clipBounds)) {
- // Since the cropped quad became a rectangle which covered the bounds of the rrect,
- // we can draw the rrect directly and ignore the edge flags
- GrPaint paint;
- clear_to_grpaint(*constColor, &paint);
- this->drawRRect(GrFixedClip::Disabled(), std::move(paint), clipAA, SkMatrix::I(),
- clipRRect, GrStyle::SimpleFill());
- return QuadOptimization::kSubmitted;
- } else {
- // The quad has been updated to better fit clip bounds, but can't remove the clip
- return QuadOptimization::kCropped;
- }
- }
- }
-
- // Crop the quad to the conservative bounds of the clip.
- SkIRect clipDevBounds;
- clip.getConservativeBounds(rtRect.width(), rtRect.height(), &clipDevBounds);
- SkRect clipBounds = SkRect::Make(clipDevBounds);
-
- // One final check for discarding, since we may have gone here directly due to a complex clip
- if (!clipBounds.intersects(drawBounds)) {
- return QuadOptimization::kDiscarded;
- }
-
- // Even if this were to return true, the crop rect does not exactly match the clip, so can not
- // report explicit-clip. Since these edges aren't visible, don't update the final edge flags.
- GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad);
-
- return QuadOptimization::kCropped;
}
-void GrRenderTargetContext::drawFilledQuad(const GrClip& clip,
+bool GrRenderTargetContext::drawFilledRectAsClear(const GrClip& clip, GrPaint&& paint, GrAA aa,
+ const SkMatrix& viewMatrix, const SkRect& rect) {
+ // Rules for a filled rect to become a clear [+scissor]:
+ // 1. The paint is a constant blend color with no other FPs
+ // 2. The view matrix maps rectangles to rectangles, or the transformed quad fully covers
+ // the render target (or clear region in #3).
+ // 3. The clip is an intersection of rectangles, so the clear region will be the
+ // intersection of the clip and the provided rect.
+ // 4. The clear region aligns with pixel bounds
+ // 5. There are no user stencil settings (and since the clip was IOR, the clip won't need
+ // to use the stencil either).
+ // If all conditions pass, the filled rect can either be a fullscreen clear (if it's big
+ // enough), or the rectangle geometry will be used as the scissor clip on the clear.
+ // If everything passes but rule #4, this submits a simplified fill rect op instead so that the
+ // rounding differences between clip and draws don't fight each other.
+ // NOTE: we route draws into clear() regardless of performColorClearsAsDraws() since the
+ // clear call is allowed to reset the oplist even when it also happens to use a GrFillRectOp.
+
+ SkPMColor4f clearColor;
+ if (paint.numCoverageFragmentProcessors() > 0 || !paint.isConstantBlendedColor(&clearColor)) {
+ return false;
+ }
+
+ const SkRect rtRect = fRenderTargetProxy->getBoundsRect();
+ // Will be the intersection of render target, clip, and quad
+ SkRect combinedRect = rtRect;
+
+ SkRRect clipRRect;
+ GrAA clipAA;
+ if (!clip.quickContains(rtRect)) {
+ // If the clip is an rrect with no rounding, then it can replace the full RT bounds as the
+ // limiting region, although we will have to worry about AA. If the clip is anything
+ // more complicated, just punt to the regular fill rect op.
+ if (!clip.isRRect(rtRect, &clipRRect, &clipAA) || !clipRRect.isRect()) {
+ return false;
+ }
+
+ combinedRect = clipRRect.rect();
+ } else {
+ // The clip is outside the render target, so the clip can be ignored
+ clipAA = GrAA::kNo;
+ }
+
+ GrQuadAAFlags edgeFlags; // To account for clip and draw mixing AA modes
+ if (viewMatrix.rectStaysRect()) {
+ // Skip the extra overhead of inverting the view matrix to see if rtRect is contained in the
+ // drawn rectangle, and instead just intersect rtRect with the transformed rect. It will be
+ // the new clear region.
+ SkRect drawRect = viewMatrix.mapRect(rect);
+ if (!combinedRect.intersect(drawRect)) {
+ // No intersection means nothing should be drawn, so return true but don't add an op
+ return true;
+ }
+
+ // In this case, edge flags start based on draw's AA and then switch per-edge to the clip's
+ // AA setting if that edge was inset.
+ edgeFlags = aa == GrAA::kNo ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+ if (combinedRect.fLeft > drawRect.fLeft) {
+ edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kLeft, clipAA);
+ }
+ if (combinedRect.fTop > drawRect.fTop) {
+ edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kTop, clipAA);
+ }
+ if (combinedRect.fRight < drawRect.fRight) {
+ edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kRight, clipAA);
+ }
+ if (combinedRect.fBottom < drawRect.fBottom) {
+ edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kBottom, clipAA);
+ }
+ } else {
+ // If the transformed rectangle does not contain the combined rt and clip, the draw is too
+ // complex to be implemented as a clear
+ SkMatrix invM;
+ if (!viewMatrix.invert(&invM)) {
+ return false;
+ }
+ // The clip region in the rect's local space, so the test becomes the local rect containing
+ // the quad's points. If clip is non-AA, test rounded out region to avoid the scenario where
+ // the draw contains the unrounded non-aa clip, but does not contain the rounded version. Be
+ // conservative since we don't know how the GPU would round.
+ SkRect conservative;
+ if (clipAA == GrAA::kNo) {
+ conservative = SkRect::Make(combinedRect.roundOut());
+ } else {
+ conservative = combinedRect;
+ }
+ GrQuad quad = GrQuad::MakeFromRect(conservative, invM);
+ if (!rect_contains_inclusive(rect, quad.point(0)) ||
+ !rect_contains_inclusive(rect, quad.point(1)) ||
+ !rect_contains_inclusive(rect, quad.point(2)) ||
+ !rect_contains_inclusive(rect, quad.point(3))) {
+ // No containment, so combinedRect can't be filled by a solid color
+ return false;
+ }
+ // combinedRect can be filled by a solid color but doesn't need to be modified since it's
+ // inside the quad to be drawn, which also means the edge AA flags respect the clip AA
+ edgeFlags = clipAA == GrAA::kNo ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+ }
+
+ // Almost every condition is met; now it requires that the combined rect align with pixel
+ // boundaries in order for it to become a scissor-clear. Ignore the AA status in this case
+ // since non-AA with partial-pixel coordinates can be rounded differently on the GPU,
+ // leading to unexpected differences between a scissor test and a rasterized quad.
+ // Also skip very small rectangles since the scissor+clear doesn't by us much then.
+ if (combinedRect.contains(rtRect)) {
+ // Full screen clear
+ this->clear(nullptr, clearColor, CanClearFullscreen::kYes);
+ return true;
+ } else if (GrClip::IsPixelAligned(combinedRect) &&
+ combinedRect.width() > 256 && combinedRect.height() > 256) {
+ // Scissor + clear (round shouldn't do anything since we are pixel aligned)
+ SkIRect scissorRect;
+ combinedRect.round(&scissorRect);
+ this->clear(&scissorRect, clearColor, CanClearFullscreen::kNo);
+ return true;
+ }
+
+ // If we got here, we can't use a scissor + clear, but combinedRect represents the correct
+ // geometry combination of quad + clip so we can perform a simplified fill rect op. We do this
+ // mostly to avoid mismatches in rounding logic on the CPU vs. the GPU, which frequently appears
+ // when drawing and clipping something to the same non-AA rect that never-the-less has
+ // non-integer coordinates.
+ aa = edgeFlags == GrQuadAAFlags::kNone ? GrAA::kNo : GrAA::kYes;
+ GrAAType aaType = this->chooseAAType(aa);
+ this->addDrawOp(GrFixedClip::Disabled(),
+ GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+ GrQuad(combinedRect), GrQuad(combinedRect)));
+ return true;
+}
+
+void GrRenderTargetContext::drawFilledRect(const GrClip& clip,
GrPaint&& paint,
GrAA aa,
- GrQuadAAFlags edgeFlags,
- const GrQuad& deviceQuad,
- const GrQuad& localQuad,
+ const SkMatrix& viewMatrix,
+ const SkRect& rect,
const GrUserStencilSettings* ss) {
- ASSERT_SINGLE_OWNER
- RETURN_IF_ABANDONED
- SkDEBUGCODE(this->validate();)
- GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawFilledQuad", fContext);
- AutoCheckFlush acf(this->drawingManager());
-
- SkPMColor4f* constColor = nullptr;
- SkPMColor4f paintColor;
- if (!ss && !paint.numCoverageFragmentProcessors() &&
- paint.isConstantBlendedColor(&paintColor)) {
- // Only consider clears/rrects when it's easy to guarantee 100% fill with single color
- constColor = &paintColor;
+ if (!ss) {
+ if (this->drawFilledRectAsClear(clip, std::move(paint), aa, viewMatrix, rect)) {
+ return;
+ }
+ // Fall through to fill rect op
+ assert_alive(paint);
}
- GrQuad croppedDeviceQuad = deviceQuad;
- GrQuad croppedLocalQuad = localQuad;
- // Only allow AA settings to change if there are no stencil settings. If they were allowed to
- // change at this stage, it would bypass higher-level decisions about stencil MSAA.
- QuadOptimization opt = this->attemptQuadOptimization(clip, constColor, /* allowAAChange */ !ss,
- &aa, &edgeFlags, &croppedDeviceQuad,
- &croppedLocalQuad);
- if (opt >= QuadOptimization::kClipApplied) {
- // These optimizations require caller to add an op themselves
- const GrClip& finalClip = opt == QuadOptimization::kClipApplied ? GrFixedClip::Disabled()
- : clip;
- GrAAType aaType = ss ? (aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone)
- : this->chooseAAType(aa);
- this->addDrawOp(finalClip, GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
- croppedDeviceQuad, croppedLocalQuad, ss));
+ SkRect croppedRect = rect;
+ if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+ // The rectangle would not be drawn, so no need to add a draw op to the list
+ return;
}
- // All other optimization levels were completely handled inside attempt(), so no extra op needed
+
+ GrAAType aaType = this->chooseAAType(aa);
+ GrQuadAAFlags edgeFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+ this->addDrawOp(clip,
+ GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+ GrQuad::MakeFromRect(croppedRect, viewMatrix),
+ GrQuad(croppedRect), ss));
}
void GrRenderTargetContext::drawRect(const GrClip& clip,
@@ -724,8 +750,7 @@
const SkStrokeRec& stroke = style->strokeRec();
if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
- // Fills the rect, using rect as its own local coordinates
- this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect);
+ this->drawFilledRect(clip, std::move(paint), aa, viewMatrix, rect);
return;
} else if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
stroke.getStyle() == SkStrokeRec::kHairline_Style) {
@@ -867,6 +892,98 @@
fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
}
+void GrRenderTargetContextPriv::stencilRect(const GrClip& clip,
+ const GrUserStencilSettings* ss,
+ GrPaint&& paint,
+ GrAA doStencilMSAA,
+ const SkMatrix& viewMatrix,
+ const SkRect& rect,
+ const SkMatrix* localMatrix) {
+ ASSERT_SINGLE_OWNER_PRIV
+ RETURN_IF_ABANDONED_PRIV
+ SkDEBUGCODE(fRenderTargetContext->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "stencilRect",
+ fRenderTargetContext->fContext);
+
+ AutoCheckFlush acf(fRenderTargetContext->drawingManager());
+
+ auto aaType = (GrAA::kYes == doStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
+
+ GrQuad localQuad = localMatrix ? GrQuad::MakeFromRect(rect, *localMatrix)
+ : GrQuad(rect);
+ std::unique_ptr<GrDrawOp> op = GrFillRectOp::Make(
+ fRenderTargetContext->fContext, std::move(paint), aaType, GrQuadAAFlags::kNone,
+ GrQuad::MakeFromRect(rect, viewMatrix), localQuad, ss);
+ fRenderTargetContext->addDrawOp(clip, std::move(op));
+}
+
+void GrRenderTargetContext::fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
+ GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
+ const SkRect& rect, const SkRect* localRect) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillRectWithEdgeAA", fContext);
+
+ GrAAType aaType = this->chooseAAType(aa);
+ std::unique_ptr<GrDrawOp> op;
+
+ if (localRect) {
+ // If local coordinates are provided, skip the optimization check to go through
+ // drawFilledRect, and also calculate clipped local coordinates
+ SkRect croppedRect = rect;
+ SkRect croppedLocalRect = *localRect;
+ if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect,
+ &croppedLocalRect)) {
+ return;
+ }
+ op = GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+ GrQuad::MakeFromRect(croppedRect, viewMatrix),
+ GrQuad(croppedLocalRect));
+ } else {
+ // If aaType turns into MSAA, make sure to keep quads with no AA edges as MSAA. Sending
+ // those to drawFilledRect() would have it turn off MSAA in that case, which breaks seaming
+ // with any partial AA edges that kept MSAA.
+ if (aaType != GrAAType::kMSAA &&
+ (edgeAA == GrQuadAAFlags::kNone || edgeAA == GrQuadAAFlags::kAll)) {
+ // This is equivalent to a regular filled rect draw, so route through there to take
+ // advantage of draw->clear optimizations
+ this->drawFilledRect(clip, std::move(paint), GrAA(edgeAA == GrQuadAAFlags::kAll),
+ viewMatrix, rect);
+ return;
+ }
+
+ SkRect croppedRect = rect;
+ if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+ return;
+ }
+ op = GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+ GrQuad::MakeFromRect(croppedRect, viewMatrix),
+ GrQuad(croppedRect));
+ }
+
+ AutoCheckFlush acf(this->drawingManager());
+ this->addDrawOp(clip, std::move(op));
+}
+
+void GrRenderTargetContext::fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
+ GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
+ const SkPoint quad[4], const SkPoint localQuad[4]) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillQuadWithEdgeAA", fContext);
+
+ GrAAType aaType = this->chooseAAType(aa);
+
+ AutoCheckFlush acf(this->drawingManager());
+ const SkPoint* localPoints = localQuad ? localQuad : quad;
+ this->addDrawOp(clip,
+ GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+ GrQuad::MakeFromSkQuad(quad, viewMatrix),
+ GrQuad::MakeFromSkQuad(localPoints, SkMatrix::I())));
+}
+
// Creates a paint for GrFillRectOp that matches behavior of GrTextureOp
static void draw_texture_to_grpaint(sk_sp<GrTextureProxy> proxy, const SkRect* domain,
GrSamplerState::Filter filter, SkBlendMode mode,
@@ -1030,6 +1147,32 @@
}
}
+void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
+ GrPaint&& paint,
+ GrAA aa,
+ const SkMatrix& viewMatrix,
+ const SkRect& rectToDraw,
+ const SkMatrix& localMatrix) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillRectWithLocalMatrix", fContext);
+
+ SkRect croppedRect = rectToDraw;
+ if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+ return;
+ }
+
+ AutoCheckFlush acf(this->drawingManager());
+
+ GrAAType aaType = this->chooseAAType(aa);
+ GrQuadAAFlags edgeFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+ this->addDrawOp(clip,
+ GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+ GrQuad::MakeFromRect(croppedRect, viewMatrix),
+ GrQuad::MakeFromRect(croppedRect, localMatrix)));
+}
+
void GrRenderTargetContext::drawVertices(const GrClip& clip,
GrPaint&& paint,
const SkMatrix& viewMatrix,
@@ -1775,8 +1918,8 @@
GrPaint paint;
paint.addColorFragmentProcessor(std::move(fp));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- currRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
- dstRect, dstRect);
+ currRTC->drawFilledRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
+ dstRect);
} else {
auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest
: GrSamplerState::Filter::kBilerp;
@@ -2131,9 +2274,6 @@
auto texMatrix = SkMatrix::MakeTrans(x, y);
- SkRect dstRectY = SkRect::MakeWH(dstW, dstH);
- SkRect dstRectUV = SkRect::MakeWH(dstW / 2, dstH / 2);
-
// This matrix generates (r,g,b,a) = (0, 0, 0, y)
float yM[20];
std::fill_n(yM, 15, 0.f);
@@ -2143,8 +2283,8 @@
auto yFP = GrColorMatrixFragmentProcessor::Make(yM, false, true, false);
yPaint.addColorFragmentProcessor(std::move(yFP));
yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- yRTC->fillRectToRect(GrNoClip(), std::move(yPaint), GrAA::kNo, SkMatrix::I(),
- dstRectY, dstRectY);
+ yRTC->drawFilledRect(GrNoClip(), std::move(yPaint), GrAA::kNo, SkMatrix::I(),
+ SkRect::MakeWH(dstW, dstH));
auto yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(yRTC->width(), yRTC->height()));
if (!yTransfer.fTransferBuffer) {
@@ -2162,8 +2302,8 @@
auto uFP = GrColorMatrixFragmentProcessor::Make(uM, false, true, false);
uPaint.addColorFragmentProcessor(std::move(uFP));
uPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- uRTC->fillRectToRect(GrNoClip(), std::move(uPaint), GrAA::kNo, SkMatrix::I(),
- dstRectUV, dstRectUV);
+ uRTC->drawFilledRect(GrNoClip(), std::move(uPaint), GrAA::kNo, SkMatrix::I(),
+ SkRect::MakeWH(dstW / 2, dstH / 2));
auto uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(uRTC->width(), uRTC->height()));
if (!uTransfer.fTransferBuffer) {
@@ -2180,8 +2320,8 @@
auto vFP = GrColorMatrixFragmentProcessor::Make(vM, false, true, false);
vPaint.addColorFragmentProcessor(std::move(vFP));
vPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
- vRTC->fillRectToRect(GrNoClip(), std::move(vPaint), GrAA::kNo, SkMatrix::I(),
- dstRectUV, dstRectUV);
+ vRTC->drawFilledRect(GrNoClip(), std::move(vPaint), GrAA::kNo, SkMatrix::I(),
+ SkRect::MakeWH(dstW / 2, dstH / 2));
auto vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(vRTC->width(), vRTC->height()));
if (!vTransfer.fTransferBuffer) {
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index cc46660..fd72e52 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -18,7 +18,6 @@
#include "src/gpu/GrRenderTargetProxy.h"
#include "src/gpu/GrSurfaceContext.h"
#include "src/gpu/GrXferProcessor.h"
-#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/text/GrTextTarget.h"
class GrBackendSemaphore;
@@ -117,9 +116,9 @@
const SkMatrix& viewMatrix,
const SkRect& rectToDraw,
const SkRect& localRect) {
- this->drawFilledQuad(clip, std::move(paint), aa,
- aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
- GrQuad::MakeFromRect(rectToDraw, viewMatrix), GrQuad(localRect));
+ this->fillRectWithEdgeAA(clip, std::move(paint), aa,
+ aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
+ viewMatrix, rectToDraw, &localRect);
}
/**
@@ -127,15 +126,10 @@
*/
void fillRectWithLocalMatrix(const GrClip& clip,
GrPaint&& paint,
- GrAA aa,
+ GrAA,
const SkMatrix& viewMatrix,
const SkRect& rect,
- const SkMatrix& localMatrix) {
- this->drawFilledQuad(clip, std::move(paint), aa,
- aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
- GrQuad::MakeFromRect(rect, viewMatrix),
- GrQuad::MakeFromRect(rect, localMatrix));
- }
+ const SkMatrix& localMatrix);
/**
* Creates an op that draws a fill rect with per-edge control over anti-aliasing.
@@ -145,11 +139,7 @@
*/
void fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
const SkMatrix& viewMatrix, const SkRect& rect,
- const SkRect* optionalLocalRect = nullptr) {
- const SkRect& localRect = optionalLocalRect ? *optionalLocalRect : rect;
- this->drawFilledQuad(clip, std::move(paint), aa, edgeAA,
- GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(localRect));
- }
+ const SkRect* optionalLocalRect = nullptr);
/**
* Similar to fillRectWithEdgeAA but draws an arbitrary 2D convex quadrilateral transformed
@@ -165,12 +155,7 @@
*/
void fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
const SkMatrix& viewMatrix, const SkPoint quad[4],
- const SkPoint optionalLocalQuad[4]) {
- const SkPoint* localQuad = optionalLocalQuad ? optionalLocalQuad : quad;
- this->drawFilledQuad(clip, std::move(paint), aa, edgeAA,
- GrQuad::MakeFromSkQuad(quad, viewMatrix),
- GrQuad::MakeFromSkQuad(localQuad, SkMatrix::I()));
- }
+ const SkPoint optionalLocalQuad[4]);
/** Used with drawQuadSet */
struct QuadSetEntry {
@@ -502,7 +487,6 @@
private:
class TextTarget;
- enum class QuadOptimization;
GrAAType chooseAAType(GrAA);
@@ -541,35 +525,20 @@
const SkRRect& origOuter,
const SkRRect& origInner);
- // If the drawn quad's paint is a const blended color, provide it as a non-null pointer to
- // 'constColor', which enables the draw-as-clear optimization. Otherwise it is assumed the paint
- // requires some form of shading that invalidates using a clear op.
- //
- // The non-const pointers should be the original draw request on input, and will be updated as
- // appropriate depending on the returned optimization level.
- //
- // If 'allowAAChange' is true, 'aa' and 'edgeFlags' may be updated to incorporate the AA of the
- // clip. When it is false, only optimizations that will not change the AA state will be applied.
- QuadOptimization attemptQuadOptimization(const GrClip& clip,
- const SkPMColor4f* constColor,
- bool allowAAChange,
- GrAA* aa,
- GrQuadAAFlags* edgeFlags,
- GrQuad* deviceQuad,
- GrQuad* localQuad);
-
- // If stencil settings, 'ss', are non-null, AA controls MSAA or no AA. If they are null, then AA
- // can choose between coverage, MSAA as per chooseAAType(). This will always attempt to apply
- // quad optimizations, so all quad/rect public APIs should rely on this function for consistent
- // clipping behavior.
- void drawFilledQuad(const GrClip& clip,
+ void drawFilledRect(const GrClip& clip,
GrPaint&& paint,
- GrAA aa,
- GrQuadAAFlags edgeFlags,
- const GrQuad& deviceQuad,
- const GrQuad& localQuad,
+ GrAA,
+ const SkMatrix& viewMatrix,
+ const SkRect& rect,
const GrUserStencilSettings* ss = nullptr);
+ // Only consumes the GrPaint if successful.
+ bool drawFilledRectAsClear(const GrClip& clip,
+ GrPaint&& paint,
+ GrAA aa,
+ const SkMatrix& viewMatrix,
+ const SkRect& rect);
+
void drawShapeUsingPathRenderer(const GrClip&, GrPaint&&, GrAA, const SkMatrix&,
const GrShape&);
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index ef60655..fa1d9fd 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -66,16 +66,8 @@
// care to only provide hard clips or we could get stuck in a loop. The general clip is needed
// so that path renderers can use this function.
void stencilRect(
- const GrClip& clip, const GrUserStencilSettings* ss, GrPaint&& paint,
- GrAA doStencilMSAA, const SkMatrix& viewMatrix, const SkRect& rect,
- const SkMatrix* localMatrix = nullptr) {
- // Since this provides stencil settings to drawFilledQuad, it performs a different AA type
- // resolution compared to regular rect draws, which is the main reason it remains separate.
- GrQuad localQuad = localMatrix ? GrQuad::MakeFromRect(rect, *localMatrix) : GrQuad(rect);
- fRenderTargetContext->drawFilledQuad(
- clip, std::move(paint), doStencilMSAA, GrQuadAAFlags::kNone,
- GrQuad::MakeFromRect(rect, viewMatrix), localQuad, ss);
- }
+ const GrClip&, const GrUserStencilSettings* ss, GrPaint&& paint, GrAA doStencilMSAA,
+ const SkMatrix& viewMatrix, const SkRect& rect, const SkMatrix* localMatrix = nullptr);
void stencilPath(
const GrHardClip&, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const GrPath*);
@@ -92,6 +84,12 @@
const SkMatrix& viewMatrix,
const SkPath&);
+ void drawFilledRect(
+ const GrClip& clip, GrPaint&& paint, GrAA aa, const SkMatrix& m, const SkRect& rect,
+ const GrUserStencilSettings* ss = nullptr) {
+ fRenderTargetContext->drawFilledRect(clip, std::move(paint), aa, m, rect, ss);
+ }
+
SkBudgeted isBudgeted() const;
int maxWindowRectangles() const;
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index 9a080f3..9f4f974 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -117,15 +117,6 @@
// True if anti-aliasing affects this quad. Only valid when quadType == kAxisAligned
bool aaHasEffectOnRect() const;
- // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be
- // taken to keep its quad type aligned with the geometric nature of the new coordinates. This is
- // no different than using the constructors that accept a quad type.
-
- float* xs() { return fX; }
- float* ys() { return fY; }
- float* ws() { return fW; }
-
- void setQuadType(Type newType) { fType = newType; }
private:
template<typename T>
friend class GrQuadListBase; // for access to fX, fY, fW
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index fce38d2..ba109cf 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -7,158 +7,9 @@
#include "src/gpu/geometry/GrQuadUtils.h"
-#include "include/core/SkRect.h"
#include "include/private/GrTypesPriv.h"
-#include "include/private/SkVx.h"
#include "src/gpu/geometry/GrQuad.h"
-using V4f = skvx::Vec<4, float>;
-using M4f = skvx::Vec<4, int32_t>;
-
-// Since the local quad may not be type kRect, this uses the opposites for each vertex when
-// interpolating, and calculates new ws in addition to new xs, ys.
-static void interpolate_local(float alpha, int v0, int v1, int v2, int v3,
- float lx[4], float ly[4], float lw[4]) {
- SkASSERT(v0 >= 0 && v0 < 4);
- SkASSERT(v1 >= 0 && v1 < 4);
- SkASSERT(v2 >= 0 && v2 < 4);
- SkASSERT(v3 >= 0 && v3 < 4);
-
- float beta = 1.f - alpha;
- lx[v0] = alpha * lx[v0] + beta * lx[v2];
- ly[v0] = alpha * ly[v0] + beta * ly[v2];
- lw[v0] = alpha * lw[v0] + beta * lw[v2];
-
- lx[v1] = alpha * lx[v1] + beta * lx[v3];
- ly[v1] = alpha * ly[v1] + beta * ly[v3];
- lw[v1] = alpha * lw[v1] + beta * lw[v3];
-}
-
-// Crops v0 to v1 based on the clipDevRect. v2 is opposite of v0, v3 is opposite of v1.
-// It is written to not modify coordinates if there's no intersection along the edge.
-// Ideally this would have been detected earlier and the entire draw is skipped.
-static bool crop_rect_edge(const SkRect& clipDevRect, int v0, int v1, int v2, int v3,
- float x[4], float y[4], float lx[4], float ly[4], float lw[4]) {
- SkASSERT(v0 >= 0 && v0 < 4);
- SkASSERT(v1 >= 0 && v1 < 4);
- SkASSERT(v2 >= 0 && v2 < 4);
- SkASSERT(v3 >= 0 && v3 < 4);
-
- if (SkScalarNearlyEqual(x[v0], x[v1])) {
- // A vertical edge
- if (x[v0] < clipDevRect.fLeft && x[v2] >= clipDevRect.fLeft) {
- // Overlapping with left edge of clipDevRect
- if (lx) {
- float alpha = (x[v2] - clipDevRect.fLeft) / (x[v2] - x[v0]);
- interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
- }
- x[v0] = clipDevRect.fLeft;
- x[v1] = clipDevRect.fLeft;
- return true;
- } else if (x[v0] > clipDevRect.fRight && x[v2] <= clipDevRect.fRight) {
- // Overlapping with right edge of clipDevRect
- if (lx) {
- float alpha = (clipDevRect.fRight - x[v2]) / (x[v0] - x[v2]);
- interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
- }
- x[v0] = clipDevRect.fRight;
- x[v1] = clipDevRect.fRight;
- return true;
- }
- } else {
- // A horizontal edge
- SkASSERT(SkScalarNearlyEqual(y[v0], y[v1]));
- if (y[v0] < clipDevRect.fTop && y[v2] >= clipDevRect.fTop) {
- // Overlapping with top edge of clipDevRect
- if (lx) {
- float alpha = (y[v2] - clipDevRect.fTop) / (y[v2] - y[v0]);
- interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
- }
- y[v0] = clipDevRect.fTop;
- y[v1] = clipDevRect.fTop;
- return true;
- } else if (y[v0] > clipDevRect.fBottom && y[v2] <= clipDevRect.fBottom) {
- // Overlapping with bottom edge of clipDevRect
- if (lx) {
- float alpha = (clipDevRect.fBottom - y[v2]) / (y[v0] - y[v2]);
- interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
- }
- y[v0] = clipDevRect.fBottom;
- y[v1] = clipDevRect.fBottom;
- return true;
- }
- }
-
- // No overlap so don't crop it
- return false;
-}
-
-// Updates x and y to intersect with clipDevRect, and applies clipAA policy to edgeFlags for each
-// intersected edge. lx, ly, and lw are updated appropriately and may be null to skip calculations.
-static void crop_rect(const SkRect& clipDevRect, GrAA clipAA, GrQuadAAFlags* edgeFlags,
- float x[4], float y[4], float lx[4], float ly[4], float lw[4]) {
- // Filled in as if clipAA were true, will be inverted at the end if needed.
- GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone;
-
- // However, the quad's left edge may not align with the SkRect notion of left due to 90 degree
- // rotations or mirrors. So, this processes the logical edges of the quad and clamps it to the 4
- // sides of clipDevRect.
-
- // Quad's left is v0 to v1 (op. v2 and v3)
- if (crop_rect_edge(clipDevRect, 0, 1, 2, 3, x, y, lx, ly, lw)) {
- clipEdgeFlags |= GrQuadAAFlags::kLeft;
- }
- // Quad's top edge is v0 to v2 (op. v1 and v3)
- if (crop_rect_edge(clipDevRect, 0, 2, 1, 3, x, y, lx, ly, lw)) {
- clipEdgeFlags |= GrQuadAAFlags::kTop;
- }
- // Quad's right edge is v2 to v3 (op. v0 and v1)
- if (crop_rect_edge(clipDevRect, 2, 3, 0, 1, x, y, lx, ly, lw)) {
- clipEdgeFlags |= GrQuadAAFlags::kRight;
- }
- // Quad's bottom edge is v1 to v3 (op. v0 and v2)
- if (crop_rect_edge(clipDevRect, 1, 3, 0, 2, x, y, lx, ly, lw)) {
- clipEdgeFlags |= GrQuadAAFlags::kBottom;
- }
-
- if (clipAA == GrAA::kYes) {
- // Turn on all edges that were clipped
- *edgeFlags |= clipEdgeFlags;
- } else {
- // Turn off all edges that were clipped
- *edgeFlags &= ~clipEdgeFlags;
- }
-}
-
-// Calculates barycentric coordinates for each point in (testX, testY) in the triangle formed by
-// (x0,y0) - (x1,y1) - (x2, y2) and stores them in u, v, w.
-static void barycentric_coords(float x0, float y0, float x1, float y1, float x2, float y2,
- const V4f& testX, const V4f& testY,
- V4f* u, V4f* v, V4f* w) {
- // Modeled after SkPathOpsQuad::pointInTriangle() but uses float instead of double, is
- // vectorized and outputs normalized barycentric coordinates instead of inside/outside test
- float v0x = x2 - x0;
- float v0y = y2 - y0;
- float v1x = x1 - x0;
- float v1y = y1 - y0;
- V4f v2x = testX - x0;
- V4f v2y = testY - y0;
-
- float dot00 = v0x * v0x + v0y * v0y;
- float dot01 = v0x * v1x + v0y * v1y;
- V4f dot02 = v0x * v2x + v0y * v2y;
- float dot11 = v1x * v1x + v1y * v1y;
- V4f dot12 = v1x * v2x + v1y * v2y;
- float invDenom = sk_ieee_float_divide(1.f, dot00 * dot11 - dot01 * dot01);
- *u = (dot11 * dot02 - dot01 * dot12) * invDenom;
- *v = (dot00 * dot12 - dot01 * dot02) * invDenom;
- *w = 1.f - *u - *v;
-}
-
-static M4f inside_triangle(const V4f& u, const V4f& v, const V4f& w) {
- return ((u >= 0.f) & (u <= 1.f)) & ((v >= 0.f) & (v <= 1.f)) & ((w >= 0.f) & (w <= 1.f));
-}
-
namespace GrQuadUtils {
void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, const GrQuad& quad,
@@ -194,79 +45,6 @@
SK_ABORT("Should not use mixed sample AA with edge AA flags");
break;
}
-}
-
-bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
- GrQuad* local) {
- if (quad->quadType() == GrQuad::Type::kAxisAligned) {
- // crop_rect keeps the rectangles as rectangles, so there's no need to modify types
- if (local) {
- crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
- local->xs(), local->ys(), local->ws());
- } else {
- crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
- nullptr, nullptr, nullptr);
- }
- return true;
- }
-
- if (local) {
- // FIXME (michaelludwig) Calculate cropped local coordinates when not kAxisAligned
- return false;
- }
-
- V4f devX = quad->x4f();
- V4f devY = quad->y4f();
- V4f devIW = quad->iw4f();
- // Project the 3D coordinates to 2D
- if (quad->quadType() == GrQuad::Type::kPerspective) {
- devX *= devIW;
- devY *= devIW;
- }
-
- V4f clipX = {cropRect.fLeft, cropRect.fLeft, cropRect.fRight, cropRect.fRight};
- V4f clipY = {cropRect.fTop, cropRect.fBottom, cropRect.fTop, cropRect.fBottom};
-
- // Calculate barycentric coordinates for the 4 rect corners in the 2 triangles that the quad
- // is tessellated into when drawn.
- V4f u1, v1, w1;
- barycentric_coords(devX[0], devY[0], devX[1], devY[1], devX[2], devY[2], clipX, clipY,
- &u1, &v1, &w1);
- V4f u2, v2, w2;
- barycentric_coords(devX[1], devY[1], devX[3], devY[3], devX[2], devY[2], clipX, clipY,
- &u2, &v2, &w2);
-
- // clipDevRect is completely inside this quad if each corner is in at least one of two triangles
- M4f inTri1 = inside_triangle(u1, v1, w1);
- M4f inTri2 = inside_triangle(u2, v2, w2);
- if (all(inTri1 | inTri2)) {
- // We can crop to exactly the clipDevRect.
- // FIXME (michaelludwig) - there are other ways to have determined quad covering the clip
- // rect, but the barycentric coords will be useful to derive local coordinates in the future
-
- // Since we are cropped to exactly clipDevRect, we have discarded any perspective and the
- // type becomes kRect. If updated locals were requested, they will incorporate perspective.
- // FIXME (michaelludwig) - once we have local coordinates handled, it may be desirable to
- // keep the draw as perspective so that the hardware does perspective interpolation instead
- // of pushing it into a local coord w and having the shader do an extra divide.
- clipX.store(quad->xs());
- clipY.store(quad->ys());
- quad->ws()[0] = 1.f;
- quad->ws()[1] = 1.f;
- quad->ws()[2] = 1.f;
- quad->ws()[3] = 1.f;
- quad->setQuadType(GrQuad::Type::kAxisAligned);
-
- // Update the edge flags to match the clip setting since all 4 edges have been clipped
- *edgeFlags = cropAA == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
-
- return true;
- }
-
- // FIXME (michaelludwig) - use the GrQuadPerEdgeAA tessellation inset/outset math to move
- // edges to the closest clip corner they are outside of
-
- return false;
-}
+};
}; // namespace GrQuadUtils
diff --git a/src/gpu/geometry/GrQuadUtils.h b/src/gpu/geometry/GrQuadUtils.h
index 5f6d283..29feeeb 100644
--- a/src/gpu/geometry/GrQuadUtils.h
+++ b/src/gpu/geometry/GrQuadUtils.h
@@ -9,10 +9,8 @@
#define GrQuadUtils_DEFINED
enum class GrQuadAAFlags;
-enum class GrAA : bool;
enum class GrAAType : unsigned;
class GrQuad;
-struct SkRect;
namespace GrQuadUtils {
@@ -21,23 +19,6 @@
void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
const GrQuad& quad, GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags);
- /**
- * Crops quad to the provided device-space axis-aligned rectangle. If the intersection of this
- * quad (projected) and cropRect results in a quadrilateral, this returns true. If not, this
- * quad may be updated to be a smaller quad of the same type such that its intersection with
- * cropRect is visually the same.
- *
- * The provided edge flags are updated to reflect edges clipped by cropRect (toggling on or off
- * based on cropAA policy). If provided, the local coordinates will be updated to reflect the
- * updated device coordinates of this quad.
- *
- * 'local' may be null, in which case the new local coordinates will not be calculated. This is
- * useful when it's known a paint does not require local coordinates. However, neither
- * 'edgeFlags' nore 'quad' can be null.
- */
- bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
- GrQuad* local=nullptr);
-
}; // namespace GrQuadUtils
#endif
diff --git a/tests/GrQuadCropTest.cpp b/tests/GrQuadCropTest.cpp
deleted file mode 100644
index 36fafe4..0000000
--- a/tests/GrQuadCropTest.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2019 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkScalar.h"
-#include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadUtils.h"
-#include "tests/Test.h"
-
-#define ASSERT(cond) REPORTER_ASSERT(r, cond)
-#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
-#define TEST(name) DEF_TEST(GrQuadCrop##name, r)
-#define ASSERT_NEARLY_EQUAL(expected, actual) \
- ASSERTF(SkScalarNearlyEqual(expected, actual), "expected: %f, actual: %f", \
- expected, actual)
-
-// Make the base rect contain the origin and have unique edge values so that each transform
-// produces a different axis-aligned rectangle.
-static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
-
-static void run_crop_axis_aligned_test(skiatest::Reporter* r, const SkRect& clipRect, GrAA clipAA,
- const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
- // Should use run_crop_fully_covers_test for non-rect matrices
- SkASSERT(viewMatrix.rectStaysRect());
-
- GrQuad drawQuad = GrQuad::MakeFromRect(kDrawRect, viewMatrix);
- GrQuad localQuad = GrQuad::MakeFromRect(kDrawRect, localMatrix ? *localMatrix : SkMatrix::I());
- GrQuad* localQuadPtr = localMatrix ? &localQuad : nullptr;
- GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
-
- bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &edgeFlags, &drawQuad, localQuadPtr);
- ASSERTF(exact, "Expected exact crop");
- ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned,
- "Expected quad to remain axis-aligned");
-
- // Since we remained a rectangle, the bounds will exactly match the coordinates
- SkRect expectedBounds = viewMatrix.mapRect(kDrawRect);
- SkAssertResult(expectedBounds.intersect(clipRect));
-
- SkRect actualBounds = drawQuad.bounds();
- ASSERT_NEARLY_EQUAL(expectedBounds.fLeft, actualBounds.fLeft);
- ASSERT_NEARLY_EQUAL(expectedBounds.fTop, actualBounds.fTop);
- ASSERT_NEARLY_EQUAL(expectedBounds.fRight, actualBounds.fRight);
- ASSERT_NEARLY_EQUAL(expectedBounds.fBottom, actualBounds.fBottom);
-
- // Confirm that local coordinates match up with clipped edges and the transform
- SkMatrix invViewMatrix;
- SkAssertResult(viewMatrix.invert(&invViewMatrix));
-
- if (localMatrix) {
- SkMatrix toLocal = SkMatrix::Concat(*localMatrix, invViewMatrix);
-
- for (int p = 0; p < 4; ++p) {
- SkPoint expectedPoint = drawQuad.point(p);
- toLocal.mapPoints(&expectedPoint, 1);
- SkPoint actualPoint = localQuad.point(p);
-
- ASSERT_NEARLY_EQUAL(expectedPoint.fX, actualPoint.fX);
- ASSERT_NEARLY_EQUAL(expectedPoint.fY, actualPoint.fY);
- }
- }
-
- // Confirm that the edge flags match, by mapping clip rect to drawRect space and
- // comparing to the original draw rect edges
- SkRect drawClip = invViewMatrix.mapRect(clipRect);
- if (drawClip.fLeft > kDrawRect.fLeft) {
- if (clipAA == GrAA::kYes) {
- ASSERTF(edgeFlags & GrQuadAAFlags::kLeft, "Expected left edge AA set");
- } else {
- ASSERTF(!(edgeFlags & GrQuadAAFlags::kLeft), "Expected left edge AA unset");
- }
- }
- if (drawClip.fRight < kDrawRect.fRight) {
- if (clipAA == GrAA::kYes) {
- ASSERTF(edgeFlags & GrQuadAAFlags::kRight, "Expected right edge AA set");
- } else {
- ASSERTF(!(edgeFlags & GrQuadAAFlags::kRight), "Expected right edge AA unset");
- }
- }
- if (drawClip.fTop > kDrawRect.fTop) {
- if (clipAA == GrAA::kYes) {
- ASSERTF(edgeFlags & GrQuadAAFlags::kTop, "Expected top edge AA set");
- } else {
- ASSERTF(!(edgeFlags & GrQuadAAFlags::kTop), "Expected top edge AA unset");
- }
- }
- if (drawClip.fBottom < kDrawRect.fBottom) {
- if (clipAA == GrAA::kYes) {
- ASSERTF(edgeFlags & GrQuadAAFlags::kBottom, "Expected bottom edge AA set");
- } else {
- ASSERTF(!(edgeFlags & GrQuadAAFlags::kBottom), "Expected bottom edge AA unset");
- }
- }
-}
-
-static void run_crop_fully_covered_test(skiatest::Reporter* r, GrAA clipAA,
- const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
- // Should use run_crop_axis_aligned for rect transforms since that verifies more behavior
- SkASSERT(!viewMatrix.rectStaysRect());
-
- // Test what happens when the geometry fully covers the crop rect. Given a fixed crop,
- // use the provided view matrix to derive the "input" geometry that we know covers the crop.
- SkMatrix invViewMatrix;
- SkAssertResult(viewMatrix.invert(&invViewMatrix));
-
- SkRect containsCrop = kDrawRect; // Use kDrawRect as the crop rect for this test
- containsCrop.outset(10.f, 10.f);
- SkRect drawRect = invViewMatrix.mapRect(containsCrop);
-
- GrQuad drawQuad = GrQuad::MakeFromRect(drawRect, viewMatrix);
- GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
-
- if (localMatrix) {
- GrQuad localQuad = GrQuad::MakeFromRect(drawRect, *localMatrix);
-
- GrQuad originalDrawQuad = drawQuad;
- GrQuad originalLocalQuad = localQuad;
- GrQuadAAFlags originalEdgeFlags = edgeFlags;
-
- bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, &localQuad);
- // Currently non-rect matrices don't know how to update local coordinates, so the crop
- // doesn't know how to restrict itself and should leave the inputs unmodified
- ASSERTF(!exact, "Expected crop to be not exact");
- ASSERTF(edgeFlags == originalEdgeFlags, "Expected edge flags not to be modified");
-
- for (int i = 0; i < 4; ++i) {
- ASSERT_NEARLY_EQUAL(originalDrawQuad.x(i), drawQuad.x(i));
- ASSERT_NEARLY_EQUAL(originalDrawQuad.y(i), drawQuad.y(i));
- ASSERT_NEARLY_EQUAL(originalDrawQuad.w(i), drawQuad.w(i));
-
- ASSERT_NEARLY_EQUAL(originalLocalQuad.x(i), localQuad.x(i));
- ASSERT_NEARLY_EQUAL(originalLocalQuad.y(i), localQuad.y(i));
- ASSERT_NEARLY_EQUAL(originalLocalQuad.w(i), localQuad.w(i));
- }
- } else {
- // Since no local coordinates were provided, and the input draw geometry is known to
- // fully cover the crop rect, the quad should be updated to match cropRect exactly
- bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, nullptr);
- ASSERTF(exact, "Expected crop to be exact");
-
- GrQuadAAFlags expectedFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kAll
- : GrQuadAAFlags::kNone;
- ASSERTF(expectedFlags == edgeFlags, "Expected edge flags do not match clip AA setting");
- ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type");
-
- ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(0));
- ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(0));
- ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(0));
-
- ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(1));
- ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(1));
- ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(1));
-
- ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(2));
- ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(2));
- ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(2));
-
- ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(3));
- ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(3));
- ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(3));
- }
-}
-
-static void test_axis_aligned_all_clips(skiatest::Reporter* r, const SkMatrix& viewMatrix,
- const SkMatrix* localMatrix) {
- static const float kInsideEdge = SkScalarAbs(kDrawRect.fLeft) - 1.f;
- static const float kOutsideEdge = SkScalarAbs(kDrawRect.fBottom) + 1.f;
- static const float kIntersectEdge = SkScalarAbs(kDrawRect.fTop) + 1.f;
-
- static const SkRect kInsideClipRect = SkRect::MakeLTRB(-kInsideEdge, -kInsideEdge,
- kInsideEdge, kInsideEdge);
- static const SkRect kContainsClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kOutsideEdge,
- kOutsideEdge, kOutsideEdge);
- static const SkRect kXYAxesClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kIntersectEdge,
- kIntersectEdge, kIntersectEdge);
- static const SkRect kXAxisClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kOutsideEdge,
- kIntersectEdge, kOutsideEdge);
- static const SkRect kYAxisClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kIntersectEdge,
- kOutsideEdge, kIntersectEdge);
-
- run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kNo, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kNo, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kNo, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
-
- run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kYes, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kYes, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kYes, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
- run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
-}
-
-static void test_axis_aligned(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
- test_axis_aligned_all_clips(r, viewMatrix, nullptr);
-
- SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
- SkMatrix::kFill_ScaleToFit);
- test_axis_aligned_all_clips(r, viewMatrix, &normalized);
-
- SkMatrix rotated;
- rotated.setRotate(45.f);
- test_axis_aligned_all_clips(r, viewMatrix, &rotated);
-
- SkMatrix perspective;
- perspective.setPerspY(0.001f);
- perspective.setSkewX(8.f / 25.f);
- test_axis_aligned_all_clips(r, viewMatrix, &perspective);
-}
-
-static void test_crop_fully_covered(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
- run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, nullptr);
- run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, nullptr);
-
- SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
- SkMatrix::kFill_ScaleToFit);
- run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &normalized);
- run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &normalized);
-
- SkMatrix rotated;
- rotated.setRotate(45.f);
- run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &rotated);
- run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &rotated);
-
- SkMatrix perspective;
- perspective.setPerspY(0.001f);
- perspective.setSkewX(8.f / 25.f);
- run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &perspective);
- run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &perspective);
-}
-
-TEST(AxisAligned) {
- test_axis_aligned(r, SkMatrix::I());
- test_axis_aligned(r, SkMatrix::MakeScale(-1.f, 1.f));
- test_axis_aligned(r, SkMatrix::MakeScale(1.f, -1.f));
-
- SkMatrix rotation;
- rotation.setRotate(90.f);
- test_axis_aligned(r, rotation);
- rotation.setRotate(180.f);
- test_axis_aligned(r, rotation);
- rotation.setRotate(270.f);
- test_axis_aligned(r, rotation);
-}
-
-TEST(FullyCovered) {
- SkMatrix rotation;
- rotation.setRotate(34.f);
- test_crop_fully_covered(r, rotation);
-
- SkMatrix skew;
- skew.setSkewX(0.3f);
- skew.setSkewY(0.04f);
- test_crop_fully_covered(r, skew);
-
- SkMatrix perspective;
- perspective.setPerspX(0.001f);
- perspective.setSkewY(8.f / 25.f);
- test_crop_fully_covered(r, perspective);
-}