skia2/bench/QuadTreeBench.cpp
commit-bot@chromium.org c22d139808 Initial QuadTree implementation
In an effort to find a faster bounding box hierarchy than the R-Tree, a QuadTree has been implemented here.
For now, the QuadTree construction is generally faster than the R-Tree and the queries are a bit slower, so overall, SKP local tests showed QuadTree performance similar to the R-Tree performance.

Tests and bench are included in this cl.

At this point, I'd like to be able to commit this in order to more easily use the bots to test multiple configurations and a larger number of SKPs. The R-Tree BBH is still used by default so this change shouldn't affect chromium.

BUG=skia:
R=junov@chromium.org, junov@google.com, senorblanco@google.com, senorblanco@chromium.org, reed@google.com, sugoi@google.com, fmalita@google.com

Author: sugoi@chromium.org

Review URL: https://codereview.chromium.org/131343011

git-svn-id: http://skia.googlecode.com/svn/trunk@13282 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-02-03 18:08:33 +00:00

213 lines
7.3 KiB
C++

/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBenchmark.h"
#include "SkCanvas.h"
#include "SkQuadTree.h"
#include "SkRandom.h"
#include "SkString.h"
// confine rectangles to a smallish area, so queries generally hit something, and overlap occurs:
static const int GENERATE_EXTENTS = 1000;
static const int NUM_BUILD_RECTS = 500;
static const int NUM_QUERY_RECTS = 5000;
static const int GRID_WIDTH = 100;
static const SkIRect QUAD_TREE_BOUNDS = SkIRect::MakeLTRB(
-GENERATE_EXTENTS, -GENERATE_EXTENTS, 2 * GENERATE_EXTENTS, 2 * GENERATE_EXTENTS);
typedef SkIRect (*MakeRectProc)(SkRandom&, int, int);
// Time how long it takes to build an QuadTree
class BBoxBuildBench : public SkBenchmark {
public:
BBoxBuildBench(const char* name, MakeRectProc proc, SkBBoxHierarchy* tree)
: fTree(tree)
, fProc(proc) {
fName.append("quadtree_");
fName.append(name);
fName.append("_build");
}
virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
return backend == kNonRendering_Backend;
}
virtual ~BBoxBuildBench() {
fTree->unref();
}
protected:
virtual const char* onGetName() SK_OVERRIDE {
return fName.c_str();
}
virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
SkRandom rand;
for (int i = 0; i < loops; ++i) {
for (int j = 0; j < NUM_BUILD_RECTS; ++j) {
fTree->insert(reinterpret_cast<void*>(j), fProc(rand, j, NUM_BUILD_RECTS),
false);
}
fTree->clear();
}
}
private:
SkBBoxHierarchy* fTree;
MakeRectProc fProc;
SkString fName;
typedef SkBenchmark INHERITED;
};
// Time how long it takes to perform queries on an QuadTree
class BBoxQueryBench : public SkBenchmark {
public:
enum QueryType {
kSmall_QueryType, // small queries
kLarge_QueryType, // large queries
kRandom_QueryType,// randomly sized queries
kFull_QueryType // queries that cover everything
};
BBoxQueryBench(const char* name, MakeRectProc proc,
QueryType q, SkBBoxHierarchy* tree)
: fTree(tree)
, fProc(proc)
, fQuery(q) {
fName.append("quadtree_");
fName.append(name);
fName.append("_query");
}
virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
return backend == kNonRendering_Backend;
}
virtual ~BBoxQueryBench() {
fTree->unref();
}
protected:
virtual const char* onGetName() SK_OVERRIDE {
return fName.c_str();
}
virtual void onPreDraw() SK_OVERRIDE {
SkRandom rand;
for (int j = 0; j < NUM_QUERY_RECTS; ++j) {
fTree->insert(reinterpret_cast<void*>(j),
fProc(rand, j, NUM_QUERY_RECTS),
false);
}
fTree->flushDeferredInserts();
}
virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
SkRandom rand;
for (int i = 0; i < loops; ++i) {
SkTDArray<void*> hits;
SkIRect query;
switch(fQuery) {
case kSmall_QueryType:
query.fLeft = rand.nextU() % GENERATE_EXTENTS;
query.fTop = rand.nextU() % GENERATE_EXTENTS;
query.fRight = query.fLeft + (GENERATE_EXTENTS / 20);
query.fBottom = query.fTop + (GENERATE_EXTENTS / 20);
break;
case kLarge_QueryType:
query.fLeft = rand.nextU() % GENERATE_EXTENTS;
query.fTop = rand.nextU() % GENERATE_EXTENTS;
query.fRight = query.fLeft + (GENERATE_EXTENTS / 2);
query.fBottom = query.fTop + (GENERATE_EXTENTS / 2);
break;
case kFull_QueryType:
query.fLeft = -GENERATE_EXTENTS;
query.fTop = -GENERATE_EXTENTS;
query.fRight = 2 * GENERATE_EXTENTS;
query.fBottom = 2 * GENERATE_EXTENTS;
break;
default: // fallthrough
case kRandom_QueryType:
query.fLeft = rand.nextU() % GENERATE_EXTENTS;
query.fTop = rand.nextU() % GENERATE_EXTENTS;
query.fRight = query.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 2);
query.fBottom = query.fTop + 1 + rand.nextU() % (GENERATE_EXTENTS / 2);
break;
};
fTree->search(query, &hits);
}
}
private:
SkBBoxHierarchy* fTree;
MakeRectProc fProc;
SkString fName;
QueryType fQuery;
typedef SkBenchmark INHERITED;
};
static inline SkIRect make_concentric_rects_increasing(SkRandom&, int index, int numRects) {
SkIRect out = {0, 0, index + 1, index + 1};
return out;
}
static inline SkIRect make_XYordered_rects(SkRandom& rand, int index, int numRects) {
SkIRect out;
out.fLeft = index % GRID_WIDTH;
out.fTop = index / GRID_WIDTH;
out.fRight = out.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 3);
out.fBottom = out.fTop + 1 + rand.nextU() % (GENERATE_EXTENTS / 3);
return out;
}
static inline SkIRect make_YXordered_rects(SkRandom& rand, int index, int numRects) {
SkIRect out;
out.fLeft = index / GRID_WIDTH;
out.fTop = index % GRID_WIDTH;
out.fRight = out.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 3);
out.fBottom = out.fTop + 1 + rand.nextU() % (GENERATE_EXTENTS / 3);
return out;
}
static inline SkIRect make_random_rects(SkRandom& rand, int index, int numRects) {
SkIRect out;
out.fLeft = rand.nextS() % GENERATE_EXTENTS;
out.fTop = rand.nextS() % GENERATE_EXTENTS;
out.fRight = out.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 5);
out.fBottom = out.fTop + 1 + rand.nextU() % (GENERATE_EXTENTS / 5);
return out;
}
///////////////////////////////////////////////////////////////////////////////
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("XYordered", &make_XYordered_rects,
SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("XYordered", &make_XYordered_rects,
BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("YXordered", &make_YXordered_rects,
SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("YXordered", &make_YXordered_rects,
BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("random", &make_random_rects,
SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("random", &make_random_rects,
BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("concentric", &make_concentric_rects_increasing,
SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("concentric", &make_concentric_rects_increasing,
BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
)