Split SkPictureReplacementPlayback out of SkPicturePlayback

R=mtklein@google.com, reed@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/383733002
This commit is contained in:
robertphillips 2014-07-10 07:21:27 -07:00 committed by Commit bot
parent 67a3271f0d
commit c26d991bf2
8 changed files with 271 additions and 177 deletions

View File

@ -140,6 +140,8 @@
'<(skia_src_path)/core/SkPictureRecord.cpp',
'<(skia_src_path)/core/SkPictureRecord.h',
'<(skia_src_path)/core/SkPictureRecorder.cpp',
'<(skia_src_path)/core/SkPictureReplacementPlayback.cpp',
'<(skia_src_path)/core/SkPictureReplacementPlayback.h',
'<(skia_src_path)/core/SkPictureShader.cpp',
'<(skia_src_path)/core/SkPictureShader.h',
'<(skia_src_path)/core/SkPictureStateTree.cpp',

View File

@ -288,6 +288,7 @@ private:
friend class GrGatherDevice;
friend class SkDebugCanvas;
friend class SkPicturePlayback; // to get fData
friend class SkPictureReplacementPlayback; // to access OperationList
typedef SkRefCnt INHERITED;

View File

@ -14,48 +14,6 @@
#include "SkTDArray.h"
#include "SkTypes.h"
SkPicturePlayback::PlaybackReplacements::ReplacementInfo*
SkPicturePlayback::PlaybackReplacements::push() {
SkDEBUGCODE(this->validate());
return fReplacements.push();
}
void SkPicturePlayback::PlaybackReplacements::freeAll() {
for (int i = 0; i < fReplacements.count(); ++i) {
SkDELETE(fReplacements[i].fBM);
}
fReplacements.reset();
}
#ifdef SK_DEBUG
void SkPicturePlayback::PlaybackReplacements::validate() const {
// Check that the ranges are monotonically increasing and non-overlapping
if (fReplacements.count() > 0) {
SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop);
for (int i = 1; i < fReplacements.count(); ++i) {
SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop);
SkASSERT(fReplacements[i - 1].fStop < fReplacements[i].fStart);
}
}
}
#endif
// TODO: Replace with hash or pass in "lastLookedUp" hint
SkPicturePlayback::PlaybackReplacements::ReplacementInfo*
SkPicturePlayback::PlaybackReplacements::lookupByStart(size_t start) {
SkDEBUGCODE(this->validate());
for (int i = 0; i < fReplacements.count(); ++i) {
if (start == fReplacements[i].fStart) {
return &fReplacements[i];
} else if (start < fReplacements[i].fStart) {
return NULL; // the ranges are monotonically increasing and non-overlapping
}
}
return NULL;
}
/*
* Read the next op code and chunk size from 'reader'. The returned size
* is the entire size of the chunk (including the opcode). Thus, the
@ -137,79 +95,6 @@ bool SkPicturePlayback::initIterator(SkPictureStateTree::Iterator* iter,
return true;
}
bool SkPicturePlayback::replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix) {
if (NULL != fReplacements) {
// Potentially replace a block of operations with a single drawBitmap call
SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp =
fReplacements->lookupByStart(reader->offset());
if (NULL != temp) {
SkASSERT(NULL != temp->fBM);
SkASSERT(NULL != temp->fPaint);
canvas->save();
canvas->setMatrix(initialMatrix);
SkRect src = SkRect::Make(temp->fSrcRect);
SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY,
temp->fSrcRect.width(),
temp->fSrcRect.height());
canvas->drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint);
canvas->restore();
if (iter->isValid()) {
// This save is needed since the BBH will automatically issue
// a restore to balanced the saveLayer we're skipping
canvas->save();
// At this point we know that the PictureStateTree was aiming
// for some draw op within temp's saveLayer (although potentially
// in a separate saveLayer nested inside it).
// We need to skip all the operations inside temp's range
// along with all the associated state changes but update
// the state tree to the first operation outside temp's range.
uint32_t skipTo;
do {
skipTo = iter->nextDraw();
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
break;
}
if (skipTo <= temp->fStop) {
reader->setOffset(skipTo);
uint32_t size;
DrawType op = ReadOpAndSize(reader, &size);
// Since we are relying on the normal SkPictureStateTree
// playback we need to convert any nested saveLayer calls
// it may issue into saves (so that all its internal
// restores will be balanced).
if (SAVE_LAYER == op) {
canvas->save();
}
}
} while (skipTo <= temp->fStop);
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
reader->setOffset(reader->size()); // skip to end
return true;
}
reader->setOffset(skipTo);
} else {
reader->setOffset(temp->fStop);
uint32_t size;
SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
SkASSERT(RESTORE == op);
}
return true;
}
}
return false;
}
// If 'iter' is valid use it to skip forward through the picture.
void SkPicturePlayback::StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader) {
if (iter->isValid()) {
@ -270,10 +155,6 @@ void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback)
return;
}
if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
continue;
}
fCurOffset = reader.offset();
uint32_t size;
DrawType op = ReadOpAndSize(&reader, &size);

View File

@ -17,71 +17,29 @@ class SkDrawPictureCallback;
class SkPaint;
class SkPictureData;
// The basic picture playback class replays the provided picture into a canvas.
// If the picture was generated with a BBH it is used to accelerate drawing
// unless disabled via setUseBBH.
class SkPicturePlayback : SkNoncopyable {
public:
SkPicturePlayback(const SkPicture* picture)
: fPictureData(picture->fData.get())
, fCurOffset(0)
, fUseBBH(true)
, fReplacements(NULL) {
, fUseBBH(true) {
}
virtual ~SkPicturePlayback() { }
virtual void draw(SkCanvas* canvas, SkDrawPictureCallback*);
// TODO: remove the curOp calls after cleaning up GrGatherDevice
// Return the ID of the operation currently being executed when playing
// back. 0 indicates no call is active.
size_t curOpID() const { return fCurOffset; }
void resetOpID() { fCurOffset = 0; }
// TODO: remove setUseBBH after cleaning up GrGatherCanvas
void setUseBBH(bool useBBH) { fUseBBH = useBBH; }
// PlaybackReplacements collects op ranges that can be replaced with
// a single drawBitmap call (using a precomputed bitmap).
class PlaybackReplacements {
public:
// All the operations between fStart and fStop (inclusive) will be replaced with
// a single drawBitmap call using fPos, fBM and fPaint.
// fPaint will be NULL if the picture's paint wasn't copyable
struct ReplacementInfo {
size_t fStart;
size_t fStop;
SkIPoint fPos;
SkBitmap* fBM; // fBM is allocated so ReplacementInfo can remain POD
const SkPaint* fPaint; // Note: this object doesn't own the paint
SkIRect fSrcRect;
};
~PlaybackReplacements() { this->freeAll(); }
// Add a new replacement range. The replacement ranges should be
// sorted in increasing order and non-overlapping (esp. no nested
// saveLayers).
ReplacementInfo* push();
private:
friend class SkPicturePlayback; // for access to lookupByStart
// look up a replacement range by its start offset
ReplacementInfo* lookupByStart(size_t start);
void freeAll();
#ifdef SK_DEBUG
void validate() const;
#endif
SkTDArray<ReplacementInfo> fReplacements;
};
// Replace all the draw ops in the replacement ranges in 'replacements' with
// the associated drawBitmap call
// Draw replacing cannot be enabled at the same time as draw limiting
void setReplacements(PlaybackReplacements* replacements) {
fReplacements = replacements;
}
protected:
const SkPictureData* fPictureData;
@ -89,7 +47,6 @@ protected:
size_t fCurOffset;
bool fUseBBH;
PlaybackReplacements* fReplacements;
void handleOp(SkReader32* reader,
DrawType op,
@ -104,10 +61,6 @@ protected:
static void StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader);
static void SkipIterTo(SkPictureStateTree::Iterator* iter,
SkReader32* reader, uint32_t skipTo);
bool replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix);
static DrawType ReadOpAndSize(SkReader32* reader, uint32_t* size);

View File

@ -1,3 +1,4 @@
/*
* Copyright 2014 Google Inc.
*

View File

@ -0,0 +1,171 @@
/*
* 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 "SkCanvas.h"
#include "SkPicture.h"
#include "SkPictureData.h"
#include "SkPictureReplacementPlayback.h"
SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo*
SkPictureReplacementPlayback::PlaybackReplacements::push() {
SkDEBUGCODE(this->validate());
return fReplacements.push();
}
void SkPictureReplacementPlayback::PlaybackReplacements::freeAll() {
for (int i = 0; i < fReplacements.count(); ++i) {
SkDELETE(fReplacements[i].fBM);
}
fReplacements.reset();
}
#ifdef SK_DEBUG
void SkPictureReplacementPlayback::PlaybackReplacements::validate() const {
// Check that the ranges are monotonically increasing and non-overlapping
if (fReplacements.count() > 0) {
SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop);
for (int i = 1; i < fReplacements.count(); ++i) {
SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop);
SkASSERT(fReplacements[i - 1].fStop < fReplacements[i].fStart);
}
}
}
#endif
// TODO: Replace with hash or pass in "lastLookedUp" hint
SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo*
SkPictureReplacementPlayback::PlaybackReplacements::lookupByStart(size_t start) {
SkDEBUGCODE(this->validate());
for (int i = 0; i < fReplacements.count(); ++i) {
if (start == fReplacements[i].fStart) {
return &fReplacements[i];
} else if (start < fReplacements[i].fStart) {
return NULL; // the ranges are monotonically increasing and non-overlapping
}
}
return NULL;
}
bool SkPictureReplacementPlayback::replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix) {
if (NULL != fReplacements) {
// Potentially replace a block of operations with a single drawBitmap call
PlaybackReplacements::ReplacementInfo* temp =
fReplacements->lookupByStart(reader->offset());
if (NULL != temp) {
SkASSERT(NULL != temp->fBM);
SkASSERT(NULL != temp->fPaint);
canvas->save();
canvas->setMatrix(initialMatrix);
SkRect src = SkRect::Make(temp->fSrcRect);
SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY,
temp->fSrcRect.width(),
temp->fSrcRect.height());
canvas->drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint);
canvas->restore();
if (iter->isValid()) {
// This save is needed since the BBH will automatically issue
// a restore to balanced the saveLayer we're skipping
canvas->save();
// At this point we know that the PictureStateTree was aiming
// for some draw op within temp's saveLayer (although potentially
// in a separate saveLayer nested inside it).
// We need to skip all the operations inside temp's range
// along with all the associated state changes but update
// the state tree to the first operation outside temp's range.
uint32_t skipTo;
do {
skipTo = iter->nextDraw();
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
break;
}
if (skipTo <= temp->fStop) {
reader->setOffset(skipTo);
uint32_t size;
DrawType op = ReadOpAndSize(reader, &size);
// Since we are relying on the normal SkPictureStateTree
// playback we need to convert any nested saveLayer calls
// it may issue into saves (so that all its internal
// restores will be balanced).
if (SAVE_LAYER == op) {
canvas->save();
}
}
} while (skipTo <= temp->fStop);
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
reader->setOffset(reader->size()); // skip to end
return true;
}
reader->setOffset(skipTo);
} else {
reader->setOffset(temp->fStop);
uint32_t size;
SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
SkASSERT(RESTORE == op);
}
return true;
}
}
return false;
}
void SkPictureReplacementPlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
AutoResetOpID aroi(this);
SkASSERT(0 == fCurOffset);
SkPictureStateTree::Iterator it;
if (!this->initIterator(&it, canvas, fActiveOpsList)) {
return; // nothing to draw
}
SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size());
StepIterator(&it, &reader);
// Record this, so we can concat w/ it if we encounter a setMatrix()
SkMatrix initialMatrix = canvas->getTotalMatrix();
SkAutoCanvasRestore acr(canvas, false);
while (!reader.eof()) {
if (NULL != callback && callback->abortDrawing()) {
return;
}
if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
continue;
}
fCurOffset = reader.offset();
uint32_t size;
DrawType op = ReadOpAndSize(&reader, &size);
if (NOOP == op) {
// NOOPs are to be ignored - do not propagate them any further
SkipIterTo(&it, &reader, fCurOffset + size);
continue;
}
this->handleOp(&reader, op, size, canvas, initialMatrix);
StepIterator(&it, &reader);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPictureReplacementPlayback_DEFINED
#define SkPictureReplacementPlayback_DEFINED
#include "SkPicturePlayback.h"
// This playback class replaces complete "saveLayer ... restore" runs with a
// single drawBitmap call.
class SkPictureReplacementPlayback : public SkPicturePlayback {
public:
// PlaybackReplacements collects op ranges that can be replaced with
// a single drawBitmap call (using a precomputed bitmap).
class PlaybackReplacements {
public:
// All the operations between fStart and fStop (inclusive) will be replaced with
// a single drawBitmap call using fPos, fBM and fPaint.
// fPaint will be NULL if the picture's paint wasn't copyable
struct ReplacementInfo {
size_t fStart;
size_t fStop;
SkIPoint fPos;
SkBitmap* fBM; // fBM is allocated so ReplacementInfo can remain POD
const SkPaint* fPaint; // Note: this object doesn't own the paint
SkIRect fSrcRect;
};
~PlaybackReplacements() { this->freeAll(); }
// Add a new replacement range. The replacement ranges should be
// sorted in increasing order and non-overlapping (esp. no nested
// saveLayers).
ReplacementInfo* push();
// look up a replacement range by its start offset
ReplacementInfo* lookupByStart(size_t start);
private:
SkTDArray<ReplacementInfo> fReplacements;
void freeAll();
#ifdef SK_DEBUG
void validate() const;
#endif
};
// This class doesn't take ownership of either 'replacements' or 'activeOpsList'
// The caller must guarantee they exist across any calls to 'draw'.
// 'activeOpsList' can be NULL but in that case BBH acceleration will not
// be used ('replacements' can be NULL too but that defeats the purpose
// of using this class).
SkPictureReplacementPlayback(const SkPicture* picture,
PlaybackReplacements* replacements,
const SkPicture::OperationList* activeOpsList)
: INHERITED(picture)
, fReplacements(replacements)
, fActiveOpsList(activeOpsList) {
}
virtual void draw(SkCanvas* canvas, SkDrawPictureCallback*) SK_OVERRIDE;
private:
PlaybackReplacements* fReplacements;
const SkPicture::OperationList* fActiveOpsList;
// This method checks if the current op pointed at by 'iter' and 'reader'
// is within a replacement range. If so, it issues the drawBitmap call,
// updates 'iter' and 'reader' to be after the restore operation, and
// returns true. If the operation is not in a replacement range (and thus
// needs to be drawn normally) false is returned.
bool replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix);
typedef SkPicturePlayback INHERITED;
};
#endif

View File

@ -30,8 +30,8 @@
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkPictureData.h"
#include "SkPicturePlayback.h"
#include "SkPictureRangePlayback.h"
#include "SkPictureReplacementPlayback.h"
#include "SkRRect.h"
#include "SkStroke.h"
#include "SkSurface.h"
@ -1938,7 +1938,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi
}
}
SkPicturePlayback::PlaybackReplacements replacements;
SkPictureReplacementPlayback::PlaybackReplacements replacements;
// Generate the layer and/or ensure it is locked
for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
@ -1947,7 +1947,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi
const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
SkPicturePlayback::PlaybackReplacements::ReplacementInfo* layerInfo =
SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo* layerInfo =
replacements.push();
layerInfo->fStart = info.fSaveLayerOpID;
layerInfo->fStop = info.fRestoreOpID;
@ -2030,9 +2030,8 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi
}
// Playback using new layers
SkPicturePlayback playback(picture);
SkPictureReplacementPlayback playback(picture, &replacements, ops.get());
playback.setReplacements(&replacements);
playback.draw(canvas, NULL);
// unlock the layers