aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Yuqian Li <liyuqian@google.com>2018-03-23 14:36:27 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-03-23 19:06:50 +0000
commit1875e053845c4d377a0f64f7233bdb9dc00bdce1 (patch)
tree0c745bf606309f9460cb5ccc1f645819754843ae /src
parent12a68422adf67ea604a2942847ae399a728824f8 (diff)
Use DAA for small cubics and non-convex paths that fit into a mask
I forgot to benchmark svgs and it turns out that DAA is specifically good for the small cubics and small non-convex paths in svgs. This should make our svg performance fast again: 2.84% faster in svgparse_Florida-StateSeal.svg_1 2.90% faster in svgparse_NewYork-StateSeal.svg_1 2.95% faster in svgparse_Seal_of_Texas.svg_1 3.05% faster in car.svg_1 3.53% faster in svgparse_Vermont_state_seal.svg_1 3.68% faster in svgparse_Wyoming-StateSeal.svg_1 4.88% faster in svgparse_Minnesota-StateSeal.svg_1 5.22% faster in svgparse_NewMexico-StateSeal.svg_1 6.49% faster in svgparse_fsm.svg_1 Bug: skia: Change-Id: Ia149944443d72c12c3dda178cb5ebc89d6d0bf18 Reviewed-on: https://skia-review.googlesource.com/116185 Reviewed-by: Cary Clark <caryclark@google.com> Commit-Queue: Yuqian Li <liyuqian@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/core/SkCoverageDelta.cpp4
-rw-r--r--src/core/SkScan_AntiPath.cpp51
2 files changed, 41 insertions, 14 deletions
diff --git a/src/core/SkCoverageDelta.cpp b/src/core/SkCoverageDelta.cpp
index a6bb1dd5f9..defdcf2b7b 100644
--- a/src/core/SkCoverageDelta.cpp
+++ b/src/core/SkCoverageDelta.cpp
@@ -46,6 +46,10 @@ int SkCoverageDeltaMask::ExpandWidth(int width) {
}
bool SkCoverageDeltaMask::CanHandle(const SkIRect& bounds) {
+ // Return early if either width or height is very large because width * height might overflow.
+ if (bounds.width() >= MAX_MASK_SIZE || bounds.height() >= MAX_MASK_SIZE) {
+ return false;
+ }
// Expand width so we don't have to worry about the boundary
return ExpandWidth(bounds.width()) * bounds.height() + PADDING * 2 < MAX_MASK_SIZE;
}
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index ad181a77f8..2eaa89ed8a 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -12,6 +12,7 @@
#include "SkBlitter.h"
#include "SkRegion.h"
#include "SkAntiRun.h"
+#include "SkCoverageDelta.h"
#define SHIFT SK_SUPERSAMPLE_SHIFT
#define SCALE (1 << SHIFT)
@@ -583,6 +584,19 @@ void MaskSuperBlitter::blitH(int x, int y, int width) {
///////////////////////////////////////////////////////////////////////////////
+static SkIRect safeRoundOut(const SkRect& src) {
+ // roundOut will pin huge floats to max/min int
+ SkIRect dst = src.roundOut();
+
+ // intersect with a smaller huge rect, so the rect will not be considered empty for being
+ // too large. e.g. { -SK_MaxS32 ... SK_MaxS32 } is considered empty because its width
+ // exceeds signed 32bit.
+ const int32_t limit = SK_MaxS32 >> SK_SUPERSAMPLE_SHIFT;
+ (void)dst.intersect({ -limit, -limit, limit, limit});
+
+ return dst;
+}
+
static bool ShouldUseDAA(const SkPath& path) {
if (gSkForceDeltaAA) {
return true;
@@ -597,12 +611,18 @@ static bool ShouldUseDAA(const SkPath& path) {
#else
constexpr int kSampleSize = 8;
constexpr SkScalar kComplexityThreshold = 0.25;
+ constexpr SkScalar kSmallCubicThreshold = 16;
int n = path.countPoints();
if (path.isConvex() || n < kSampleSize) {
return false;
}
+ // DAA is fast with mask
+ if (SkCoverageDeltaMask::CanHandle(safeRoundOut(path.getBounds()))) {
+ return true;
+ }
+
SkScalar sumLength = 0;
SkPoint lastPoint = path.getPoint(0);
for(int i = 1; i < kSampleSize; ++i) {
@@ -610,11 +630,27 @@ static bool ShouldUseDAA(const SkPath& path) {
sumLength += SkPoint::Distance(lastPoint, point);
lastPoint = point;
}
+ SkScalar avgLength = sumLength / (kSampleSize - 1);
+
+ // DAA is much faster in small cubics (since we don't have to chop them).
+ // If there are many cubics, and the average length if small, use DAA.
+ if (avgLength < kSmallCubicThreshold) {
+ uint8_t sampleVerbs[kSampleSize];
+ int verbCount = SkTMin(kSampleSize, path.getVerbs(sampleVerbs, kSampleSize));
+ int cubicCount = 0;
+ for(int i = 0; i < verbCount; ++i) {
+ cubicCount += (sampleVerbs[i] == SkPath::kCubic_Verb);
+ }
+ if (cubicCount * 2 >= verbCount) {
+ return true;
+ }
+ }
+
SkScalar diagonal = SkPoint::Length(path.getBounds().width(), path.getBounds().height());
// On average, what's the distance between two consecutive points; the number is normalized
// to a range of [0, 1] where 1 corresponds to the maximum length of the diagonal.
- SkScalar sampleSpan = sumLength / (kSampleSize - 1) / diagonal;
+ SkScalar sampleSpan = avgLength / diagonal;
// If the path is consist of random line segments, the number of intersections should be
@@ -687,19 +723,6 @@ static int rect_overflows_short_shift(SkIRect rect, int shift) {
overflows_short_shift(rect.fBottom, shift);
}
-static SkIRect safeRoundOut(const SkRect& src) {
- // roundOut will pin huge floats to max/min int
- SkIRect dst = src.roundOut();
-
- // intersect with a smaller huge rect, so the rect will not be considered empty for being
- // too large. e.g. { -SK_MaxS32 ... SK_MaxS32 } is considered empty because its width
- // exceeds signed 32bit.
- const int32_t limit = SK_MaxS32 >> SK_SUPERSAMPLE_SHIFT;
- (void)dst.intersect({ -limit, -limit, limit, limit});
-
- return dst;
-}
-
void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip,
SkBlitter* blitter, bool forceRLE, SkDAARecord* daaRecord) {
if (origClip.isEmpty()) {