Add R-Tree record flag to SkPicture, plus some cleanup/fixes in associated classes.
Review URL: https://codereview.appspot.com/6506103 git-svn-id: http://skia.googlecode.com/svn/trunk@5537 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
ad0c5d248c
commit
8515e79a76
@ -70,7 +70,25 @@ public:
|
||||
clip-query calls will reflect the path's bounds, not the actual
|
||||
path.
|
||||
*/
|
||||
kUsePathBoundsForClip_RecordingFlag = 0x01
|
||||
kUsePathBoundsForClip_RecordingFlag = 0x01,
|
||||
/* This flag causes the picture to compute bounding boxes and build
|
||||
up a spatial hierarchy (currently an R-Tree), plus a tree of Canvas'
|
||||
usually stack-based clip/etc state. This requires an increase in
|
||||
recording time (often ~2x; likely more for very complex pictures),
|
||||
but allows us to perform much faster culling at playback time, and
|
||||
completely avoid some unnecessary clips and other operations. This
|
||||
is ideal for tiled rendering, or any other situation where you're
|
||||
drawing a fraction of a large scene into a smaller viewport.
|
||||
|
||||
In most cases the record cost is offset by the playback improvement
|
||||
after a frame or two of tiled rendering (and complex pictures that
|
||||
induce the worst record times will generally get the largest
|
||||
speedups at playback time).
|
||||
|
||||
Note: Currently this is not serializable, the bounding data will be
|
||||
discarded if you serialize into a stream and then deserialize.
|
||||
*/
|
||||
kOptimizeForClippedPlayback_RecordingFlag = 0x02
|
||||
};
|
||||
|
||||
/** Returns the canvas that records the drawing commands.
|
||||
|
@ -163,8 +163,8 @@ void SkBBoxRecord::drawPosTextH(const void* text, size_t byteLength, const SkSca
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
|
||||
// pad horizontally by half max glyph height
|
||||
SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
|
||||
// pad horizontally by max glyph height
|
||||
SkScalar pad = (metrics.fTop - metrics.fBottom);
|
||||
bbox.fLeft += pad;
|
||||
bbox.fRight -= pad;
|
||||
|
||||
@ -217,8 +217,8 @@ void SkBBoxRecord::drawVertices(VertexMode mode, int vertexCount,
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawPicture(SkPicture& picture) {
|
||||
SkRect bbox = {0, 0, picture.width(), picture.height()};
|
||||
if (this->transformBounds(bbox, NULL)) {
|
||||
if (picture.width() > 0 && picture.height() > 0 &&
|
||||
this->transformBounds(SkRect::MakeWH(picture.width(), picture.height()), NULL)) {
|
||||
INHERITED::drawPicture(picture);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include "SkReader32.h"
|
||||
#include "SkWriter32.h"
|
||||
#include "SkRTree.h"
|
||||
#include "SkBBoxHierarchyRecord.h"
|
||||
|
||||
SK_DEFINE_INST_COUNT(SkPicture)
|
||||
|
||||
@ -183,7 +185,16 @@ SkCanvas* SkPicture::beginRecording(int width, int height,
|
||||
fRecord = NULL;
|
||||
}
|
||||
|
||||
fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
|
||||
if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
|
||||
SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(width),
|
||||
SkIntToScalar(height));
|
||||
SkRTree* tree = SkRTree::Create(6, 11, aspectRatio);
|
||||
SkASSERT(NULL != tree);
|
||||
fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree));
|
||||
tree->unref();
|
||||
} else {
|
||||
fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
|
||||
}
|
||||
|
||||
fWidth = width;
|
||||
fHeight = height;
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "SkOrderedReadBuffer.h"
|
||||
#include "SkOrderedWriteBuffer.h"
|
||||
#include <new>
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
#include "SkTSort.h"
|
||||
|
||||
template <typename T> int SafeCount(const T* obj) {
|
||||
return obj ? obj->count() : 0;
|
||||
@ -69,6 +72,16 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record, bool deepCop
|
||||
if (writer.size() == 0)
|
||||
return;
|
||||
|
||||
fBoundingHierarchy = record.fBoundingHierarchy;
|
||||
fStateTree = record.fStateTree;
|
||||
|
||||
SkSafeRef(fBoundingHierarchy);
|
||||
SkSafeRef(fStateTree);
|
||||
|
||||
if (NULL != fBoundingHierarchy) {
|
||||
fBoundingHierarchy->flushDeferredInserts();
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = writer.size();
|
||||
void* buffer = sk_malloc_throw(size);
|
||||
@ -140,6 +153,12 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInf
|
||||
fMatrices = SkSafeRef(src.fMatrices);
|
||||
fRegions = SkSafeRef(src.fRegions);
|
||||
fOpData = SkSafeRef(src.fOpData);
|
||||
|
||||
fBoundingHierarchy = src.fBoundingHierarchy;
|
||||
fStateTree = src.fStateTree;
|
||||
|
||||
SkSafeRef(fBoundingHierarchy);
|
||||
SkSafeRef(fStateTree);
|
||||
|
||||
if (deepCopyInfo) {
|
||||
|
||||
@ -203,6 +222,8 @@ void SkPicturePlayback::init() {
|
||||
fPictureCount = 0;
|
||||
fOpData = NULL;
|
||||
fFactoryPlayback = NULL;
|
||||
fBoundingHierarchy = NULL;
|
||||
fStateTree = NULL;
|
||||
}
|
||||
|
||||
SkPicturePlayback::~SkPicturePlayback() {
|
||||
@ -212,6 +233,8 @@ SkPicturePlayback::~SkPicturePlayback() {
|
||||
SkSafeUnref(fMatrices);
|
||||
SkSafeUnref(fPaints);
|
||||
SkSafeUnref(fRegions);
|
||||
SkSafeUnref(fBoundingHierarchy);
|
||||
SkSafeUnref(fStateTree);
|
||||
|
||||
for (int i = 0; i < fPictureCount; i++) {
|
||||
fPictureRefs[i]->unref();
|
||||
@ -560,6 +583,30 @@ void SkPicturePlayback::draw(SkCanvas& canvas) {
|
||||
|
||||
SkReader32 reader(fOpData->bytes(), fOpData->size());
|
||||
TextContainer text;
|
||||
SkTDArray<void*> results;
|
||||
|
||||
if (fStateTree && fBoundingHierarchy) {
|
||||
SkRect clipBounds;
|
||||
if (canvas.getClipBounds(&clipBounds)) {
|
||||
SkIRect query;
|
||||
clipBounds.roundOut(&query);
|
||||
fBoundingHierarchy->search(query, &results);
|
||||
if (results.count() == 0) { return; }
|
||||
SkTQSort<SkPictureStateTree::Draw>(
|
||||
reinterpret_cast<SkPictureStateTree::Draw**>(results.begin()),
|
||||
reinterpret_cast<SkPictureStateTree::Draw**>(results.end()-1));
|
||||
}
|
||||
}
|
||||
|
||||
SkPictureStateTree::Iterator it = (NULL == fStateTree) ?
|
||||
SkPictureStateTree::Iterator() :
|
||||
fStateTree->getIterator(results, &canvas);
|
||||
|
||||
if (it.isValid()) {
|
||||
uint32_t off = it.draw();
|
||||
if (off == SK_MaxU32) { return; }
|
||||
reader.setOffset(off);
|
||||
}
|
||||
|
||||
// Record this, so we can concat w/ it if we encounter a setMatrix()
|
||||
SkMatrix initialMatrix = canvas.getTotalMatrix();
|
||||
@ -807,6 +854,12 @@ void SkPicturePlayback::draw(SkCanvas& canvas) {
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
|
||||
if (it.isValid()) {
|
||||
uint32_t off = it.draw();
|
||||
if (off == SK_MaxU32) { break; }
|
||||
reader.setOffset(off);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SPEW_CLIP_SKIPPING
|
||||
|
@ -28,6 +28,8 @@
|
||||
class SkPictureRecord;
|
||||
class SkStream;
|
||||
class SkWStream;
|
||||
class SkBBoxHierarchy;
|
||||
class SkPictureStateTree;
|
||||
|
||||
struct SkPictInfo {
|
||||
enum Flags {
|
||||
@ -192,6 +194,9 @@ private:
|
||||
SkPicture** fPictureRefs;
|
||||
int fPictureCount;
|
||||
|
||||
SkBBoxHierarchy* fBoundingHierarchy;
|
||||
SkPictureStateTree* fStateTree;
|
||||
|
||||
SkTypefacePlayback fTFPlayback;
|
||||
SkFactoryPlayback* fFactoryPlayback;
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
|
@ -80,21 +80,23 @@ void SkPictureStateTree::appendNode(uint32_t offset) {
|
||||
}
|
||||
|
||||
SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
|
||||
: fDraws(draws)
|
||||
: fDraws(&draws)
|
||||
, fCanvas(canvas)
|
||||
, fCurrentNode(root)
|
||||
, fPlaybackMatrix(canvas->getTotalMatrix())
|
||||
, fCurrentMatrix(NULL)
|
||||
, fPlaybackIndex(0)
|
||||
, fSave(false) {
|
||||
, fSave(false)
|
||||
, fValid(true) {
|
||||
}
|
||||
|
||||
uint32_t SkPictureStateTree::Iterator::draw() {
|
||||
if (fPlaybackIndex >= fDraws.count()) {
|
||||
SkASSERT(this->isValid());
|
||||
if (fPlaybackIndex >= fDraws->count()) {
|
||||
// restore back to where we started
|
||||
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
|
||||
fCurrentNode = fCurrentNode->fParent;
|
||||
while (NULL != fCurrentNode->fParent) {
|
||||
while (NULL != fCurrentNode) {
|
||||
if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
|
||||
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
|
||||
fCurrentNode = fCurrentNode->fParent;
|
||||
@ -103,7 +105,7 @@ uint32_t SkPictureStateTree::Iterator::draw() {
|
||||
return kDrawComplete;
|
||||
}
|
||||
|
||||
Draw* draw = static_cast<Draw*>(fDraws[fPlaybackIndex]);
|
||||
Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
|
||||
Node* targetNode = draw->fNode;
|
||||
|
||||
if (fSave) {
|
||||
|
@ -70,10 +70,12 @@ public:
|
||||
/** Returns the next offset into the picture stream, or kDrawComplete if complete. */
|
||||
uint32_t draw();
|
||||
static const uint32_t kDrawComplete = SK_MaxU32;
|
||||
Iterator() : fValid(false) { }
|
||||
bool isValid() { return fValid; }
|
||||
private:
|
||||
Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
|
||||
// The draws this iterator is associated with
|
||||
const SkTDArray<void*>& fDraws;
|
||||
const SkTDArray<void*>* fDraws;
|
||||
|
||||
// canvas this is playing into (so we can insert saves/restores as necessary)
|
||||
SkCanvas* fCanvas;
|
||||
@ -95,6 +97,9 @@ public:
|
||||
// Whether or not we need to do a save next iteration
|
||||
bool fSave;
|
||||
|
||||
// Whether or not this is a valid iterator (the default public constructor sets this false)
|
||||
bool fValid;
|
||||
|
||||
friend class SkPictureStateTree;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user