aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/QuickRejectBench.cpp61
-rw-r--r--include/core/SkCanvas.h21
-rw-r--r--include/core/SkPostConfig.h12
-rw-r--r--src/core/SkCanvas.cpp125
-rw-r--r--tests/QuickRejectTest.cpp38
5 files changed, 47 insertions, 210 deletions
diff --git a/bench/QuickRejectBench.cpp b/bench/QuickRejectBench.cpp
deleted file mode 100644
index 1feade41f5..0000000000
--- a/bench/QuickRejectBench.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Benchmark.h"
-#include "SkCanvas.h"
-#include "SkRandom.h"
-
-class QuickRejectBench : public Benchmark {
- enum { N = 1000000 };
- float fFloats[N];
- int fInts [N];
-
- const char* onGetName() override { return "quick_reject"; }
- bool isSuitableFor(Backend backend) override { return backend != kNonRendering_Backend; }
-
- void onDelayedSetup() override {
- SkRandom rand;
- for (int i = 0; i < N; ++i) {
- fFloats[i] = 300.0f * (rand.nextSScalar1() + 0.5f);
- }
- }
-
- void onDraw(int loops, SkCanvas* canvas) override {
- while (loops --> 0) {
- for (int i = 0; i < N - 4; i++) {
- if (canvas->quickReject(*(SkRect*)(fFloats+i))) {
- fInts[i] = 11;
- } else {
- fInts[i] = 24;
- }
- }
- }
- }
-};
-DEF_BENCH( return new QuickRejectBench; )
-
-class ConcatBench : public Benchmark {
- SkMatrix fMatrix;
-
- const char* onGetName() override { return "concat"; }
- bool isSuitableFor(Backend backend) override { return backend != kNonRendering_Backend; }
-
- void onDelayedSetup() override {
- SkRandom r;
- fMatrix.setScale(5.0f, 5.0f);
- fMatrix.setTranslateX(10.0f);
- fMatrix.setTranslateY(10.0f);
- }
-
- void onDraw(int loops, SkCanvas* canvas) override {
- while (loops --> 0) {
- canvas->setMatrix(SkMatrix::MakeScale(3.0f));
- canvas->concat(fMatrix);
- }
- }
-};
-DEF_BENCH( return new ConcatBench; )
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 73fcf43a5f..ef2d7dad70 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1609,18 +1609,25 @@ private:
*/
bool canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint&);
-
- /**
- * Keep track of the device clip bounds and if the matrix is scale-translate. This allows
- * us to do a fast quick reject in the common case.
+ /* These maintain a cache of the clip bounds in local coordinates,
+ (converted to 2s-compliment if floats are slow).
*/
- bool fConservativeIsScaleTranslate;
- SkRect fDeviceClipBounds;
-
+ mutable SkRect fCachedLocalClipBounds;
+ mutable bool fCachedLocalClipBoundsDirty;
bool fAllowSoftClip;
bool fAllowSimplifyClip;
const bool fConservativeRasterClip;
+ const SkRect& getLocalClipBounds() const {
+ if (fCachedLocalClipBoundsDirty) {
+ if (!this->getClipBounds(&fCachedLocalClipBounds)) {
+ fCachedLocalClipBounds.setEmpty();
+ }
+ fCachedLocalClipBoundsDirty = false;
+ }
+ return fCachedLocalClipBounds;
+ }
+
class AutoValidateClip : ::SkNoncopyable {
public:
explicit AutoValidateClip(SkCanvas* canvas) : fCanvas(canvas) {
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index 97c868f30f..b67c863887 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -275,18 +275,6 @@
# endif
#endif
-/**
- * If your judgment is better than the compiler's (i.e. you've profiled it),
- * you can use SK_NEVER_INLINE to prevent inlining.
- */
-#if !defined(SK_NEVER_INLINE)
-# if defined(SK_BUILD_FOR_WIN)
-# define SK_NEVER_INLINE __declspec(noinline)
-# else
-# define SK_NEVER_INLINE SK_ATTRIBUTE(noinline)
-# endif
-#endif
-
//////////////////////////////////////////////////////////////////////
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE1
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index ce786bfe8a..24d9506bd7 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -22,7 +22,6 @@
#include "SkLatticeIter.h"
#include "SkMatrixUtils.h"
#include "SkMetaData.h"
-#include "SkNx.h"
#include "SkPaintPriv.h"
#include "SkPatchUtils.h"
#include "SkPicture.h"
@@ -633,24 +632,16 @@ bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
////////////////////////////////////////////////////////////////////////////
-static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
- // Expand bounds out by 1 in case we are anti-aliasing. We store the
- // bounds as floats to enable a faster quick reject implementation.
- SkRect dst;
- SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
- return dst;
-}
-
void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
this->restoreToCount(1);
+ fCachedLocalClipBounds.setEmpty();
+ fCachedLocalClipBoundsDirty = true;
fClipStack->reset();
fMCRec->reset(bounds);
// We're peering through a lot of structs here. Only at this scope do we
// know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
- fDeviceClipBounds = qr_clip_bounds(bounds);
- fConservativeIsScaleTranslate = true;
}
SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
@@ -661,6 +652,8 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
// const-cast.
*const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
+ fCachedLocalClipBounds.setEmpty();
+ fCachedLocalClipBoundsDirty = true;
fAllowSoftClip = true;
fAllowSimplifyClip = false;
fDeviceCMDirty = true;
@@ -689,10 +682,7 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
fMCRec->fLayer->fDevice = SkRef(device);
fMCRec->fRasterClip.setRect(device->getGlobalBounds());
- fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
- fConservativeIsScaleTranslate = true;
}
-
return device;
}
@@ -1107,8 +1097,8 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag
// early exit if the layer's bounds are clipped out
if (!ir.intersect(clipBounds)) {
if (BoundsAffectsClip(saveLayerFlags)) {
+ fCachedLocalClipBoundsDirty = true;
fMCRec->fRasterClip.setEmpty();
- fDeviceClipBounds.setEmpty();
}
return false;
}
@@ -1119,9 +1109,9 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag
if (BoundsAffectsClip(saveLayerFlags)) {
// Simplify the current clips since they will be applied properly during restore()
+ fCachedLocalClipBoundsDirty = true;
fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
fMCRec->fRasterClip.setRect(ir);
- fDeviceClipBounds = qr_clip_bounds(ir);
}
if (intersection) {
@@ -1313,6 +1303,7 @@ void SkCanvas::internalRestore() {
SkASSERT(fMCStack.count() != 0);
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
fClipStack->restore();
@@ -1346,11 +1337,6 @@ void SkCanvas::internalRestore() {
// no need to update fMCRec, 'cause we're killing the canvas
}
}
-
- if (fMCRec) {
- fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
- }
}
sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
@@ -1505,20 +1491,21 @@ void SkCanvas::concat(const SkMatrix& matrix) {
this->checkForDeferredSave();
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
fMCRec->fMatrix.preConcat(matrix);
- fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
+
this->didConcat(matrix);
}
void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
fMCRec->fMatrix = matrix;
}
void SkCanvas::setMatrix(const SkMatrix& matrix) {
this->checkForDeferredSave();
this->internalSetMatrix(matrix);
- fConservativeIsScaleTranslate = matrix.isScaleTranslate();
this->didSetMatrix(matrix);
}
@@ -1590,7 +1577,6 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg
fClipStack->clipEmpty();
(void)fMCRec->fRasterClip.setEmpty();
- fDeviceClipBounds.setEmpty();
return;
}
}
@@ -1620,6 +1606,7 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg
AutoValidateClip avc(this);
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
if (isScaleTrans) {
const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
@@ -1635,8 +1622,6 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg
path.addRect(rect);
this->SkCanvas::onClipPath(path, op, edgeStyle);
}
-
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
}
void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
@@ -1655,6 +1640,7 @@ void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle
AutoValidateClip avc(this);
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
if (!fAllowSoftClip) {
edgeStyle = kHard_ClipEdgeStyle;
}
@@ -1663,7 +1649,6 @@ void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle
fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
kSoft_ClipEdgeStyle == edgeStyle);
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
return;
}
@@ -1711,7 +1696,6 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg
fClipStack->clipEmpty();
(void)fMCRec->fRasterClip.setEmpty();
- fDeviceClipBounds.setEmpty();
return;
}
}
@@ -1720,6 +1704,7 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg
AutoValidateClip avc(this);
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
if (!fAllowSoftClip) {
edgeStyle = kHard_ClipEdgeStyle;
}
@@ -1750,7 +1735,6 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg
}
fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
}
void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
@@ -1762,13 +1746,13 @@ void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
AutoValidateClip avc(this);
fDeviceCMDirty = true;
+ fCachedLocalClipBoundsDirty = true;
// todo: signal fClipStack that we have a region, and therefore (I guess)
// we have to ignore it, and use the region directly?
fClipStack->clipDevRect(rgn.getBounds(), op);
fMCRec->fRasterClip.op(rgn, op);
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
}
#ifdef SK_DEBUG
@@ -1825,74 +1809,31 @@ bool SkCanvas::isClipRect() const {
return fMCRec->fRasterClip.isRect();
}
-static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
- __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
- __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
- __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
- return 0xF != _mm_movemask_ps(mask);
-#elif defined(SK_ARM_HAS_NEON)
- float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
- float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
- uint32x4_t mask = vcltq_f32(lLtT, RrBb);
- return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
-#else
- SkRect devRectAsRect;
- SkRect devClipAsRect;
- devRect.store(&devRectAsRect.fLeft);
- devClip.store(&devClipAsRect.fLeft);
- return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
-#endif
-}
-
-// It's important for this function to not be inlined. Otherwise the compiler will share code
-// between the fast path and the slow path, resulting in two slow paths.
-static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
- const SkMatrix& matrix) {
- SkRect deviceRect;
- matrix.mapRect(&deviceRect, src);
- return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
-}
+bool SkCanvas::quickReject(const SkRect& rect) const {
+ if (!rect.isFinite())
+ return true;
-bool SkCanvas::quickReject(const SkRect& src) const {
-#ifdef SK_DEBUG
- // Verify that fDeviceClipBounds are set properly.
- SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
if (fMCRec->fRasterClip.isEmpty()) {
- SkASSERT(fDeviceClipBounds.isEmpty() || tmp == fDeviceClipBounds);
- } else {
- SkASSERT(tmp == fDeviceClipBounds);
+ return true;
}
- // Verify that fConservativeIsScaleTranslate is set properly.
- SkASSERT(!fConservativeIsScaleTranslate || fMCRec->fMatrix.isScaleTranslate());
-#endif
+ if (fMCRec->fMatrix.hasPerspective()) {
+ SkRect dst;
+ fMCRec->fMatrix.mapRect(&dst, rect);
+ return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
+ } else {
+ const SkRect& clipR = this->getLocalClipBounds();
- if (!fConservativeIsScaleTranslate) {
- return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
+ // for speed, do the most likely reject compares first
+ // TODO: should we use | instead, or compare all 4 at once?
+ if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
+ return true;
+ }
+ if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
+ return true;
+ }
+ return false;
}
-
- // We inline the implementation of mapScaleTranslate() for the fast path.
- float sx = fMCRec->fMatrix.getScaleX();
- float sy = fMCRec->fMatrix.getScaleY();
- float tx = fMCRec->fMatrix.getTranslateX();
- float ty = fMCRec->fMatrix.getTranslateY();
- Sk4f scale(sx, sy, sx, sy);
- Sk4f trans(tx, ty, tx, ty);
-
- // Apply matrix.
- Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
-
- // Make sure left < right, top < bottom.
- Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
- Sk4f min = Sk4f::Min(ltrb, rblt);
- Sk4f max = Sk4f::Max(ltrb, rblt);
- // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
- // ARM this sequence generates the fastest (a single instruction).
- Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
-
- // Check if the device rect is NaN or outside the clip.
- return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
}
bool SkCanvas::quickReject(const SkPath& path) const {
diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp
index b39484bd89..6a978743f6 100644
--- a/tests/QuickRejectTest.cpp
+++ b/tests/QuickRejectTest.cpp
@@ -103,45 +103,7 @@ static void test_layers(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, false == canvas.quickReject(SkRect::MakeWH(60, 60)));
}
-static void test_quick_reject(skiatest::Reporter* reporter) {
- SkCanvas canvas(100, 100);
- SkRect r0 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
- SkRect r1 = SkRect::MakeLTRB(-50.0f, 110.0f, 50.0f, 120.0f);
- SkRect r2 = SkRect::MakeLTRB(110.0f, -50.0f, 120.0f, 50.0f);
- SkRect r3 = SkRect::MakeLTRB(-120.0f, -50.0f, 120.0f, 50.0f);
- SkRect r4 = SkRect::MakeLTRB(-50.0f, -120.0f, 50.0f, 120.0f);
- SkRect r5 = SkRect::MakeLTRB(-120.0f, -120.0f, 120.0f, 120.0f);
- SkRect r6 = SkRect::MakeLTRB(-120.0f, -120.0f, -110.0f, -110.0f);
- SkRect r7 = SkRect::MakeLTRB(SK_ScalarNaN, -50.0f, 50.0f, 50.0f);
- SkRect r8 = SkRect::MakeLTRB(-50.0f, SK_ScalarNaN, 50.0f, 50.0f);
- SkRect r9 = SkRect::MakeLTRB(-50.0f, -50.0f, SK_ScalarNaN, 50.0f);
- SkRect r10 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, SK_ScalarNaN);
- REPORTER_ASSERT(reporter, false == canvas.quickReject(r0));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r1));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r2));
- REPORTER_ASSERT(reporter, false == canvas.quickReject(r3));
- REPORTER_ASSERT(reporter, false == canvas.quickReject(r4));
- REPORTER_ASSERT(reporter, false == canvas.quickReject(r5));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r6));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r7));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r8));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r9));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r10));
-
- SkMatrix m = SkMatrix::MakeScale(2.0f);
- m.setTranslateX(10.0f);
- m.setTranslateY(10.0f);
- canvas.setMatrix(m);
- SkRect r11 = SkRect::MakeLTRB(5.0f, 5.0f, 100.0f, 100.0f);
- SkRect r12 = SkRect::MakeLTRB(5.0f, 50.0f, 100.0f, 100.0f);
- SkRect r13 = SkRect::MakeLTRB(50.0f, 5.0f, 100.0f, 100.0f);
- REPORTER_ASSERT(reporter, false == canvas.quickReject(r11));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r12));
- REPORTER_ASSERT(reporter, true == canvas.quickReject(r13));
-}
-
DEF_TEST(QuickReject, reporter) {
test_drawBitmap(reporter);
test_layers(reporter);
- test_quick_reject(reporter);
}