aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-12 15:39:44 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-12 15:39:44 +0000
commitb8f9610ac6efb5426cb799ab9b1ab5d985b7b05a (patch)
tree86881ae513b064efeff5a430708ce0dbc1d262b1
parente428f9b1132c12299c204a333192495d7e748511 (diff)
Refactor PictureRecord optimization & added saveLayer/save/clipR/DBM*/restore/restore optimization
-rw-r--r--src/core/SkPictureFlat.h3
-rw-r--r--src/core/SkPictureRecord.cpp224
2 files changed, 174 insertions, 53 deletions
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 2d44e1ea6f..b0446bb93e 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -66,6 +66,9 @@ enum DrawType {
LAST_DRAWTYPE_ENUM = NOOP
};
+// In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
+static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1;
+
enum DrawVertexFlags {
DRAW_VERTICES_HAS_TEXS = 0x01,
DRAW_VERTICES_HAS_COLORS = 0x02,
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index b0da855845..b3bede412f 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -226,8 +226,71 @@ static bool is_simple(const SkPaint& p) {
return 0 == orAccum;
}
+// CommandInfos are fed to the 'match' method and filled in with command
+// information.
+struct CommandInfo {
+ DrawType fActualOp;
+ uint32_t fOffset;
+ uint32_t fSize;
+};
+
+/*
+ * Attempt to match the provided pattern of commands starting at 'offset'
+ * in the byte stream and stopping at the end of the stream. Upon success,
+ * return true with all the pattern information filled out in the result
+ * array (i.e., actual ops, offsets and sizes).
+ * Note this method skips any NOOPs seen in the stream
+ */
+static bool match(SkWriter32* writer, uint32_t offset,
+ int* pattern, CommandInfo* result, int numCommands) {
+ SkASSERT(offset <= writer->size());
+
+ uint32_t curOffset = offset;
+ uint32_t curSize = 0;
+ for (int i = 0; i < numCommands; ++i) {
+ DrawType op = peek_op_and_size(writer, curOffset, &curSize);
+ while (NOOP == op && curOffset < writer->size()) {
+ curOffset += curSize;
+ op = peek_op_and_size(writer, curOffset, &curSize);
+ }
+
+ if (curOffset >= writer->size()) {
+ return false; // ran out of byte stream
+ }
+
+ if (kDRAW_BITMAP_FLAVOR == pattern[i]) {
+ if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
+ DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
+ return false;
+ }
+ } else if (op != pattern[i]) {
+ return false;
+ }
+
+ result[i].fActualOp = op;
+ result[i].fOffset = curOffset;
+ result[i].fSize = curSize;
+
+ curOffset += curSize;
+ }
+
+ curOffset += curSize;
+ if (curOffset < writer->size()) {
+ // Something else between the last command and the end of the stream
+ return false;
+ }
+
+ return true;
+}
+
+// temporarily here to make code review easier
+static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
+ SkPaintDictionary* paintDict,
+ const CommandInfo& saveLayerInfo,
+ const CommandInfo& dbmInfo);
+
/*
- * Restore has just been called (but not recorded), look back at the
+ * Restore has just been called (but not recorded), look back at the
* matching save* and see if we are in the configuration:
* SAVE_LAYER
* DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
@@ -236,74 +299,71 @@ static bool is_simple(const SkPaint& p) {
*/
static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
SkPaintDictionary* paintDict) {
-
-#ifdef SK_IGNORE_PICTURE_RECORD_SAVE_LAYER_OPT
- return false;
-#endif
-
- int32_t restoreOffset = (int32_t)writer->size();
-
// back up to the save block
// TODO: add a stack to track save*/restore offsets rather than searching backwards
while (offset > 0) {
offset = *writer->peek32(offset);
}
- // now offset points to a save
- uint32_t saveLayerOffset = -offset;
- uint32_t saveLayerSize;
- DrawType op = peek_op_and_size(writer, saveLayerOffset, &saveLayerSize);
- if (SAVE_LAYER != op) {
- SkASSERT(SAVE == op);
- return false; // not a match
+ int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
+ CommandInfo result[SK_ARRAY_COUNT(pattern)];
+
+ if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
+ return false;
}
- if (kSaveLayerWithBoundsSize == saveLayerSize) {
+ if (kSaveLayerWithBoundsSize == result[0].fSize) {
// The saveLayer's bound can offset where the dbm is drawn
return false;
}
- // step forward one - check if it is a DRAW_BITMAP*
- int32_t dbmOffset = saveLayerOffset + saveLayerSize;
- if (dbmOffset >= restoreOffset) {
- // Just a saveLayer and a restore? Remove it.
- writer->rewindToOffset(saveLayerOffset);
- return true;
- }
- uint32_t dbmSize;
- op = peek_op_and_size(writer, dbmOffset, &dbmSize);
- if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
- DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
- return false; // not a match
- }
+ return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
+ result[0], result[1]);
+}
- offset = dbmOffset + dbmSize;
- if (offset < restoreOffset) {
- return false; // something else between the dbm* and the restore
- }
+/*
+ * Convert the command code located at 'offset' to a NOOP. Leave the size
+ * field alone so the NOOP can be skipped later.
+ */
+static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
+ uint32_t* ptr = writer->peek32(offset);
+ *ptr = (*ptr & MASK_24) | (NOOP << 24);
+}
- uint32_t dbmPaintOffset = getPaintOffset(op, dbmSize);
- uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerSize);
+/*
+ * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
+ * Return true on success; false otherwise.
+ */
+static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
+ SkPaintDictionary* paintDict,
+ const CommandInfo& saveLayerInfo,
+ const CommandInfo& dbmInfo) {
+ SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
+ SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
+ DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
+ DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
+ DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
+
+ uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
+ uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
// we have a match, now we need to get the paints involved
- int dbmPaintId = *((int32_t*)writer->peek32(dbmOffset+dbmPaintOffset));
- int saveLayerPaintId = *((int32_t*)writer->peek32(saveLayerOffset+slPaintOffset));
+ int dbmPaintId = *((int32_t*)writer->peek32(dbmInfo.fOffset+dbmPaintOffset));
+ int saveLayerPaintId = *((int32_t*)writer->peek32(saveLayerInfo.fOffset+slPaintOffset));
if (0 == saveLayerPaintId) {
// In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
// and signal the caller (by returning true) to not add the RESTORE op
- uint32_t* ptr = writer->peek32(saveLayerOffset);
- *ptr = (*ptr & MASK_24) | (NOOP << 24);
+ convert_command_to_noop(writer, saveLayerInfo.fOffset);
return true;
}
if (0 == dbmPaintId) {
// In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
// and signal the caller (by returning true) to not add the RESTORE op
- uint32_t* ptr = writer->peek32(saveLayerOffset);
- *ptr = (*ptr & MASK_24) | (NOOP << 24);
- ptr = writer->peek32(dbmOffset+dbmPaintOffset);
+ convert_command_to_noop(writer, saveLayerInfo.fOffset);
+ uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
SkASSERT(0 == *ptr);
*ptr = saveLayerPaintId;
return true;
@@ -337,13 +397,48 @@ static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
}
// kill the saveLayer and alter the DBMR2R's paint to be the modified one
- uint32_t* ptr = writer->peek32(saveLayerOffset);
- *ptr = (*ptr & MASK_24) | (NOOP << 24);
- ptr = writer->peek32(dbmOffset+dbmPaintOffset);
+ convert_command_to_noop(writer, saveLayerInfo.fOffset);
+ uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
+ SkASSERT(dbmPaintId == *ptr);
*ptr = data->index();
return true;
}
+/*
+ * Restore has just been called (but not recorded), look back at the
+ * matching save* and see if we are in the configuration:
+ * SAVE_LAYER
+ * SAVE
+ * CLIP_RECT
+ * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
+ * RESTORE
+ * RESTORE
+ * where the saveLayer's color can be moved into the drawBitmap*'s paint
+ */
+static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
+ SkPaintDictionary* paintDict) {
+
+ // back up to the save block
+ // TODO: add a stack to track save*/restore offsets rather than searching backwards
+ while (offset > 0) {
+ offset = *writer->peek32(offset);
+ }
+
+ int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
+ CommandInfo result[SK_ARRAY_COUNT(pattern)];
+
+ if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
+ return false;
+ }
+
+ if (kSaveLayerWithBoundsSize == result[0].fSize) {
+ // The saveLayer's bound can offset where the dbm is drawn
+ return false;
+ }
+
+ return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
+ result[0], result[3]);
+}
/*
* Restore has just been called (but not recorded), so look back at the
@@ -353,7 +448,8 @@ static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
* If so, update the writer and return true, in which case we won't even record
* the restore() call. If we still need the restore(), return false.
*/
-static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
+static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
+ SkPaintDictionary* paintDict) {
#ifdef TRACK_COLLAPSE_STATS
gCollapseCalls += 1;
#endif
@@ -399,6 +495,22 @@ static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
return true;
}
+typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
+ SkPaintDictionary* paintDict);
+
+/*
+ * A list of the optimizations that are tried upon seeing a restore
+ * TODO: add a real API for such optimizations
+ * Add the ability to fire optimizations on any op (not just RESTORE)
+ */
+static const PictureRecordOptProc gPictureRecordOpts[] = {
+ collapse_save_clip_restore,
+#ifndef SK_IGNORE_PICTURE_RECORD_SAVE_LAYER_OPT
+ remove_save_layer1,
+ remove_save_layer2,
+#endif
+};
+
void SkPictureRecord::restore() {
// FIXME: SkDeferredCanvas needs to be refactored to respect
// save/restore balancing so that the following test can be
@@ -417,15 +529,21 @@ void SkPictureRecord::restore() {
}
uint32_t initialOffset, size;
- if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top()) &&
- !remove_save_layer1(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+ int opt;
+ for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
+ if ((*gPictureRecordOpts[opt])(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+ // Some optimization fired so don't add the RESTORE
+ size = 0;
+ initialOffset = fWriter.size();
+ break;
+ }
+ }
+
+ if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
+ // No optimization fired so add the RESTORE
fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
- // op
- size = 1 * kUInt32Size;
+ size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
initialOffset = this->addDraw(RESTORE, &size);
- } else {
- size = 0;
- initialOffset = fWriter.size();
}
fRestoreOffsetStack.pop();