diff options
-rw-r--r-- | bench/QuickRejectBench.cpp | 61 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 21 | ||||
-rw-r--r-- | include/core/SkPostConfig.h | 12 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 125 | ||||
-rw-r--r-- | tests/QuickRejectTest.cpp | 38 |
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); } |