Update GrRecordReplaceDraw to use SkTDynamicHash & add ReplaceDraw

Having hoisted layers from different pictures invalidates the assumptions of the old GrReplacements object. This is fixed by switching to a SkTDynamicHash-based back-end.

Sub-picture-layers also require that the replacement drawing occur for the sub-pictures too. The ReplaceDraw object is added to make this happen and limit the replacement lookup to saveLayer draw commands.

This is split out of (Fix sub-picture layer rendering bugs - https://codereview.chromium.org/597293002/).

BUG=skia:2315

Review URL: https://codereview.chromium.org/607763008
This commit is contained in:
robertphillips 2014-10-01 09:24:06 -07:00 committed by Commit bot
parent dbcb5ff125
commit 68cd2aa797
5 changed files with 203 additions and 116 deletions

View File

@ -277,11 +277,7 @@ private:
friend class GrLayerHoister; // access to fRecord
friend class CollectLayers; // access to fRecord
friend class SkPicturePlayback; // to get fData
friend void GrRecordReplaceDraw(const SkPicture* picture,
SkCanvas* canvas,
const GrReplacements* replacements,
const SkMatrix& initialMatrix,
SkDrawPictureCallback* callback);
friend class ReplaceDraw;
typedef SkRefCnt INHERITED;

View File

@ -135,9 +135,12 @@ static void convert_layers_to_replacements(const SkTDArray<GrLayerHoister::Hoist
// TODO: just replace GrReplacements::ReplacementInfo with GrCachedLayer?
for (int i = 0; i < layers.count(); ++i) {
GrCachedLayer* layer = layers[i].fLayer;
const SkPicture* picture = layers[i].fPicture;
GrReplacements::ReplacementInfo* layerInfo = replacements->push();
layerInfo->fStart = layer->start();
GrReplacements::ReplacementInfo* layerInfo =
replacements->newReplacement(picture->uniqueID(),
layer->start(),
layers[i].fCTM);
layerInfo->fStop = layer->stop();
layerInfo->fPos = layers[i].fOffset;

View File

@ -6,49 +6,34 @@
*/
#include "GrRecordReplaceDraw.h"
#include "SkCanvasPriv.h"
#include "SkImage.h"
#include "SkRecordDraw.h"
#include "SkRecords.h"
GrReplacements::ReplacementInfo* GrReplacements::push() {
SkDEBUGCODE(this->validate());
return fReplacements.push();
GrReplacements::ReplacementInfo* GrReplacements::newReplacement(uint32_t pictureID,
unsigned int start,
const SkMatrix& ctm) {
ReplacementInfo* replacement = SkNEW_ARGS(ReplacementInfo, (pictureID, start, ctm));
fReplacementHash.add(replacement);
return replacement;
}
void GrReplacements::freeAll() {
for (int i = 0; i < fReplacements.count(); ++i) {
fReplacements[i].fImage->unref();
SkDELETE(fReplacements[i].fPaint);
SkTDynamicHash<ReplacementInfo, ReplacementInfo::Key>::Iter iter(&fReplacementHash);
for (; !iter.done(); ++iter) {
ReplacementInfo* replacement = &(*iter);
SkDELETE(replacement);
}
fReplacements.reset();
fReplacementHash.reset();
}
#ifdef SK_DEBUG
void GrReplacements::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
const GrReplacements::ReplacementInfo*
GrReplacements::lookupByStart(size_t start, int* searchStart) const {
SkDEBUGCODE(this->validate());
for (int i = *searchStart; i < fReplacements.count(); ++i) {
if (start == fReplacements[i].fStart) {
*searchStart = i + 1;
return &fReplacements[i];
} else if (start < fReplacements[i].fStart) {
return NULL; // the ranges are monotonically increasing and non-overlapping
}
}
return NULL;
const GrReplacements::ReplacementInfo* GrReplacements::lookupByStart(uint32_t pictureID,
size_t start,
const SkMatrix& ctm) const {
return fReplacementHash.find(ReplacementInfo::Key(pictureID, start, ctm));
}
static inline void draw_replacement_bitmap(const GrReplacements::ReplacementInfo* ri,
@ -66,6 +51,122 @@ static inline void draw_replacement_bitmap(const GrReplacements::ReplacementInfo
canvas->restore();
}
// Used by GrRecordReplaceDraw. It intercepts nested drawPicture calls and
// also draws them with replaced layers.
class ReplaceDraw : public SkRecords::Draw {
public:
ReplaceDraw(SkCanvas* canvas,
const SkPicture* picture,
const GrReplacements* replacements,
const SkMatrix& initialMatrix,
SkDrawPictureCallback* callback)
: INHERITED(canvas)
, fCanvas(canvas)
, fPicture(picture)
, fReplacements(replacements)
, fInitialMatrix(initialMatrix)
, fCallback(callback)
, fIndex(0) {
}
void draw() {
const SkBBoxHierarchy* bbh = fPicture->fBBH.get();
const SkRecord* record = fPicture->fRecord.get();
if (NULL == record) {
return;
}
fOps.rewind();
if (bbh) {
// Draw only ops that affect pixels in the canvas's current clip.
// The SkRecord and BBH were recorded in identity space. This canvas
// is not necessarily in that same space. getClipBounds() returns us
// this canvas' clip bounds transformed back into identity space, which
// lets us query the BBH.
SkRect query = { 0, 0, 0, 0 };
(void)fCanvas->getClipBounds(&query);
bbh->search(query, &fOps);
for (fIndex = 0; fIndex < fOps.count(); ++fIndex) {
if (fCallback && fCallback->abortDrawing()) {
return;
}
record->visit<void>((uintptr_t)fOps[fIndex], *this);
}
} else {
for (fIndex = 0; fIndex < (int) record->count(); ++fIndex) {
if (fCallback && fCallback->abortDrawing()) {
return;
}
record->visit<void>(fIndex, *this);
}
}
}
// Same as Draw for all ops except DrawPicture and SaveLayer.
template <typename T> void operator()(const T& r) {
this->INHERITED::operator()(r);
}
void operator()(const SkRecords::DrawPicture& dp) {
SkAutoCanvasMatrixPaint acmp(fCanvas, dp.matrix, dp.paint, dp.picture->cullRect());
// Draw sub-pictures with the same replacement list but a different picture
ReplaceDraw draw(fCanvas, dp.picture, fReplacements, fInitialMatrix, fCallback);
draw.draw();
}
void operator()(const SkRecords::SaveLayer& sl) {
// For a saveLayer command, check if it can be replaced by a drawBitmap
// call and, if so, draw it and then update the current op index accordingly.
size_t startOffset;
if (fOps.count()) {
startOffset = (uintptr_t)fOps[fIndex];
} else {
startOffset = fIndex;
}
const GrReplacements::ReplacementInfo* ri = fReplacements->lookupByStart(
fPicture->uniqueID(),
startOffset,
fCanvas->getTotalMatrix());
if (ri) {
draw_replacement_bitmap(ri, fCanvas, fInitialMatrix);
if (fPicture->fBBH.get()) {
while ((uintptr_t)fOps[fIndex] < ri->fStop) {
++fIndex;
}
SkASSERT((uintptr_t)fOps[fIndex] == ri->fStop);
} else {
fIndex = ri->fStop;
}
return;
}
// This is a fail for layer hoisting
this->INHERITED::operator()(sl);
}
private:
SkCanvas* fCanvas;
const SkPicture* fPicture;
const GrReplacements* fReplacements;
const SkMatrix fInitialMatrix;
SkDrawPictureCallback* fCallback;
SkTDArray<void*> fOps;
int fIndex;
typedef Draw INHERITED;
};
void GrRecordReplaceDraw(const SkPicture* picture,
SkCanvas* canvas,
const GrReplacements* replacements,
@ -73,58 +174,7 @@ void GrRecordReplaceDraw(const SkPicture* picture,
SkDrawPictureCallback* callback) {
SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
const SkBBoxHierarchy* bbh = picture->fBBH.get();
const SkRecord* record = picture->fRecord.get();
if (NULL == record) {
return;
}
ReplaceDraw draw(canvas, picture, replacements, initialMatrix, callback);
SkRecords::Draw draw(canvas);
const GrReplacements::ReplacementInfo* ri = NULL;
int searchStart = 0;
if (bbh) {
// Draw only ops that affect pixels in the canvas's current clip.
// The SkRecord and BBH were recorded in identity space. This canvas
// is not necessarily in that same space. getClipBounds() returns us
// this canvas' clip bounds transformed back into identity space, which
// lets us query the BBH.
SkRect query = { 0, 0, 0, 0 };
(void)canvas->getClipBounds(&query);
SkTDArray<void*> ops;
bbh->search(query, &ops);
for (int i = 0; i < ops.count(); i++) {
if (callback && callback->abortDrawing()) {
return;
}
ri = replacements->lookupByStart((uintptr_t)ops[i], &searchStart);
if (ri) {
draw_replacement_bitmap(ri, canvas, initialMatrix);
while ((uintptr_t)ops[i] < ri->fStop) {
++i;
}
SkASSERT((uintptr_t)ops[i] == ri->fStop);
continue;
}
record->visit<void>((uintptr_t)ops[i], draw);
}
} else {
for (unsigned int i = 0; i < record->count(); ++i) {
if (callback && callback->abortDrawing()) {
return;
}
ri = replacements->lookupByStart(i, &searchStart);
if (ri) {
draw_replacement_bitmap(ri, canvas, initialMatrix);
i = ri->fStop;
continue;
}
record->visit<void>(i, draw);
}
}
draw.draw();
}

View File

@ -8,9 +8,11 @@
#ifndef GrRecordReplaceDraw_DEFINED
#define GrRecordReplaceDraw_DEFINED
#include "SkChecksum.h"
#include "SkDrawPictureCallback.h"
#include "SkImage.h"
#include "SkRect.h"
#include "SkTDArray.h"
#include "SkTDynamicHash.h"
class SkBBoxHierarchy;
class SkBitmap;
@ -26,9 +28,53 @@ class SkRecord;
class GrReplacements {
public:
// All the operations between fStart and fStop (inclusive) will be replaced with
// a single drawBitmap call using fPos, fBM and fPaint.
struct ReplacementInfo {
unsigned fStart;
// a single drawBitmap call using fPos, fImage and fPaint.
class ReplacementInfo {
public:
struct Key {
Key(uint32_t pictureID, unsigned int start, const SkMatrix& ctm)
: fPictureID(pictureID)
, fStart(start)
, fCTM(ctm) {
fCTM.getType(); // force initialization of type so hashes match
// Key needs to be tightly packed.
GR_STATIC_ASSERT(sizeof(Key) == sizeof(uint32_t) + // picture ID
sizeof(int) + // start
9 * sizeof(SkScalar) // 3x3 from CTM
+sizeof(uint32_t)); // matrix's type
}
bool operator==(const Key& other) const {
return fPictureID == other.fPictureID &&
fStart == other.fStart &&
fCTM.cheapEqualTo(other.fCTM); // TODO: should be fuzzy
}
uint32_t pictureID() const { return fPictureID; }
unsigned int start() const { return fStart; }
private:
const uint32_t fPictureID;
const unsigned int fStart;
const SkMatrix fCTM;
};
static const Key& GetKey(const ReplacementInfo& layer) { return layer.fKey; }
static uint32_t Hash(const Key& key) {
return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
}
ReplacementInfo(uint32_t pictureID, unsigned int start, const SkMatrix& ctm)
: fKey(pictureID, start, ctm)
, fImage(NULL)
, fPaint(NULL) {
}
~ReplacementInfo() { fImage->unref(); SkDELETE(fPaint); }
unsigned int start() const { return fKey.start(); }
const Key fKey;
unsigned fStop;
SkIPoint fPos;
SkImage* fImage; // Owns a ref
@ -39,25 +85,18 @@ public:
~GrReplacements() { 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();
// Add a new replacement range.
ReplacementInfo* newReplacement(uint32_t pictureID, unsigned int start, const SkMatrix& ctm);
// look up a replacement range by its start offset.
// lastLookedUp is an in/out parameter that is used to speed up the search.
// It should be initialized to 0 on the first call and then passed back in
// unmodified on subsequent calls.
const ReplacementInfo* lookupByStart(size_t start, int* lastLookedUp) const;
// look up a replacement range by its pictureID, start offset and the CTM
// TODO: also need to add clip to lookup
const ReplacementInfo* lookupByStart(uint32_t pictureID, size_t start,
const SkMatrix& ctm) const;
private:
SkTDArray<ReplacementInfo> fReplacements;
SkTDynamicHash<ReplacementInfo, ReplacementInfo::Key> fReplacementHash;
void freeAll();
#ifdef SK_DEBUG
void validate() const;
#endif
};
// Draw an SkPicture into an SkCanvas replacing saveLayer/restore blocks with
@ -65,7 +104,7 @@ private:
void GrRecordReplaceDraw(const SkPicture*,
SkCanvas*,
const GrReplacements*,
const SkMatrix&,
const SkMatrix& initialMatrix,
SkDrawPictureCallback*);
#endif // GrRecordReplaceDraw_DEFINED

View File

@ -112,13 +112,12 @@ void test_replacements(skiatest::Reporter* r, bool useBBH) {
canvas->restore();
canvas->drawRect(SkRect::MakeWH(SkIntToScalar(kWidth / 2), SkIntToScalar(kHeight / 2)),
SkPaint());
pic.reset(recorder.endRecording());
}
GrReplacements replacements;
GrReplacements::ReplacementInfo* ri = replacements.push();
ri->fStart = 0;
GrReplacements::ReplacementInfo* ri = replacements.newReplacement(pic->uniqueID(),
0, SkMatrix::I());
ri->fStop = 2;
ri->fPos.set(0, 0);
ri->fImage = make_image(SK_ColorRED);