aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/optimizations.cpp420
-rw-r--r--gyp/SampleApp.gyp8
-rw-r--r--gyp/gm.gyp9
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkPicture.h11
-rw-r--r--src/core/SkPictureRecord.cpp25
6 files changed, 462 insertions, 12 deletions
diff --git a/gm/optimizations.cpp b/gm/optimizations.cpp
new file mode 100644
index 0000000000..7a9f1f67f2
--- /dev/null
+++ b/gm/optimizations.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "..\debugger\SkDebugCanvas.h"
+#include "SkPictureFlat.h"
+
+#define WARN(msg) \
+ SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
+
+namespace {
+
+// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
+// heavy-weight operation since we are drawing the picture into a debug canvas
+// to extract the commands.
+bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
+ SkDebugCanvas debugCanvas(input.width(), input.height());
+ debugCanvas.setBounds(input.width(), input.height());
+ input.draw(&debugCanvas);
+
+ if (pattern.count() != debugCanvas.getSize()) {
+ return false;
+ }
+
+ for (int i = 0; i < pattern.count(); ++i) {
+ if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// construct the pattern removed by the SkPictureRecord::remove_save_layer1
+// optimization, i.e.:
+// SAVE_LAYER
+// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
+// RESTORE
+//
+// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
+// takes a different path if this is false)
+// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
+// takes a different path if this is false)
+// colorsMatch - control if the saveLayer and dbmr2r paint colors
+// match (the optimization will fail if they do not)
+SkPicture* create_save_layer_opt_1(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard,
+ bool saveLayerHasPaint,
+ bool dbmr2rHasPaint,
+ bool colorsMatch) {
+ // Create the pattern that should trigger the optimization
+ preOptPattern->setCount(5);
+ (*preOptPattern)[0] = SAVE;
+ (*preOptPattern)[1] = SAVE_LAYER;
+ (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
+ (*preOptPattern)[3] = RESTORE;
+ (*preOptPattern)[4] = RESTORE;
+
+ if (colorsMatch) {
+ // Create the pattern that should appear after the optimization
+ postOptPattern->setCount(5);
+ (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+ (*postOptPattern)[1] = SAVE;
+ (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
+ (*postOptPattern)[3] = RESTORE;
+ (*postOptPattern)[4] = RESTORE;
+ } else {
+ // Create the pattern that appears if the optimization doesn't fire
+ postOptPattern->setCount(7);
+ (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+ (*postOptPattern)[1] = SAVE;
+ (*postOptPattern)[2] = SAVE_LAYER;
+ (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
+ (*postOptPattern)[4] = RESTORE;
+ (*postOptPattern)[5] = RESTORE;
+ (*postOptPattern)[6] = RESTORE;
+ }
+
+ SkPicture* result = new SkPicture;
+
+ // have to disable the optimizations while generating the picture
+ SkCanvas* canvas = result->beginRecording(100, 100,
+ SkPicture::kDisableRecordOptimizations_RecordingFlag);
+
+ SkPaint saveLayerPaint;
+ saveLayerPaint.setColor(0xCC000000);
+
+ // saveLayer's 'bounds' parameter must be NULL for this optimization
+ if (saveLayerHasPaint) {
+ canvas->saveLayer(NULL, &saveLayerPaint);
+ } else {
+ canvas->saveLayer(NULL, NULL);
+ }
+
+ SkRect rect = { 10, 10, 90, 90 };
+
+ // The dbmr2r's paint must be opaque
+ SkPaint dbmr2rPaint;
+ if (colorsMatch) {
+ dbmr2rPaint.setColor(0xFF000000);
+ } else {
+ dbmr2rPaint.setColor(0xFFFF0000);
+ }
+
+ if (dbmr2rHasPaint) {
+ canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
+ } else {
+ canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
+ }
+ canvas->restore();
+
+ result->endRecording();
+
+ return result;
+}
+
+// straight-ahead version that is seen in the skps
+SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ true, // dbmr2r has a paint
+ true); // and the colors match
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+ false, // saveLayer doesn't have a paint!
+ true, // dbmr2r has a paint
+ true); // color matching not really applicable
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ false, // dbmr2r doesn't have a paint!
+ true); // color matching not really applicable
+}
+
+// version in which the optimization fails b.c. the colors don't match
+SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ true, // dbmr2r has a paint
+ false); // and the colors don't match!
+}
+
+// construct the pattern removed by the SkPictureRecord::remove_save_layer2
+// optimization, i.e.:
+// SAVE_LAYER (with NULL == bounds)
+// SAVE
+// CLIP_RECT
+// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
+// RESTORE
+// RESTORE
+//
+// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
+// takes a different path if this is false)
+// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
+// takes a different path if this is false)
+// colorsMatch - control if the saveLayer and dbmr2r paint colors
+// match (the optimization will fail if they do not)
+SkPicture* create_save_layer_opt_2(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard,
+ bool saveLayerHasPaint,
+ bool dbmr2rHasPaint,
+ bool colorsMatch) {
+ // Create the pattern that should trigger the optimization
+ preOptPattern->setCount(8);
+ (*preOptPattern)[0] = SAVE;
+ (*preOptPattern)[1] = SAVE_LAYER;
+ (*preOptPattern)[2] = SAVE;
+ (*preOptPattern)[3] = CLIP_RECT;
+ (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
+ (*preOptPattern)[5] = RESTORE;
+ (*preOptPattern)[6] = RESTORE;
+ (*preOptPattern)[7] = RESTORE;
+
+ if (colorsMatch) {
+ // Create the pattern that should appear after the optimization
+ postOptPattern->setCount(8);
+ (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+ (*postOptPattern)[1] = SAVE;
+ (*postOptPattern)[2] = SAVE;
+ (*postOptPattern)[3] = CLIP_RECT;
+ (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
+ (*postOptPattern)[5] = RESTORE;
+ (*postOptPattern)[6] = RESTORE;
+ (*postOptPattern)[7] = RESTORE;
+ } else {
+ // Create the pattern that appears if the optimization doesn't fire
+ postOptPattern->setCount(10);
+ (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
+ (*postOptPattern)[1] = SAVE;
+ (*postOptPattern)[2] = SAVE_LAYER;
+ (*postOptPattern)[3] = SAVE;
+ (*postOptPattern)[4] = CLIP_RECT;
+ (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
+ (*postOptPattern)[6] = RESTORE;
+ (*postOptPattern)[7] = RESTORE;
+ (*postOptPattern)[8] = RESTORE;
+ (*postOptPattern)[9] = RESTORE;
+ }
+
+ SkPicture* result = new SkPicture;
+
+ // have to disable the optimizations while generating the picture
+ SkCanvas* canvas = result->beginRecording(100, 100,
+ SkPicture::kDisableRecordOptimizations_RecordingFlag);
+
+ SkPaint saveLayerPaint;
+ saveLayerPaint.setColor(0xCC000000);
+
+ // saveLayer's 'bounds' parameter must be NULL for this optimization
+ if (saveLayerHasPaint) {
+ canvas->saveLayer(NULL, &saveLayerPaint);
+ } else {
+ canvas->saveLayer(NULL, NULL);
+ }
+
+ canvas->save();
+
+ SkRect rect = { 10, 10, 90, 90 };
+ canvas->clipRect(rect);
+
+ // The dbmr2r's paint must be opaque
+ SkPaint dbmr2rPaint;
+ if (colorsMatch) {
+ dbmr2rPaint.setColor(0xFF000000);
+ } else {
+ dbmr2rPaint.setColor(0xFFFF0000);
+ }
+
+ if (dbmr2rHasPaint) {
+ canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
+ } else {
+ canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
+ }
+ canvas->restore();
+ canvas->restore();
+
+ result->endRecording();
+
+ return result;
+}
+
+// straight-ahead version that is seen in the skps
+SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ true, // dbmr2r has a paint
+ true); // and the colors match
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+ false, // saveLayer doesn't have a paint!
+ true, // dbmr2r has a paint
+ true); // color matching not really applicable
+}
+
+// alternate version that should still succeed
+SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ false, // dbmr2r doesn't have a paint!
+ true); // color matching not really applicable
+}
+
+// version in which the optimization fails b.c. the colors don't match
+SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard) {
+ return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
+ true, // saveLayer has a paint
+ true, // dbmr2r has a paint
+ false); // and the colors don't match!
+}
+
+};
+
+
+// As our .skp optimizations get folded into the captured skps our code will
+// no longer be locally exercised. This GM manually constructs the patterns
+// our optimizations will remove to test them. It acts as both a GM and a unit
+// test
+class OptimizationsGM : public skiagm::GM {
+public:
+ OptimizationsGM() {
+ this->makeCheckerboard();
+ }
+
+ static const int kWidth = 800;
+ static const int kHeight = 800;
+
+protected:
+ SkString onShortName() {
+ return SkString("optimizations");
+ }
+
+ SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
+
+ typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
+ SkTDArray<DrawType> *postOptPattern,
+ const SkBitmap& checkerBoard);
+
+ virtual void onDraw(SkCanvas* canvas) {
+
+ PFCreateOpt gOpts[] = {
+ create_save_layer_opt_1_v1,
+ create_save_layer_opt_1_v2,
+ create_save_layer_opt_1_v3,
+ create_save_layer_opt_1_v4,
+ create_save_layer_opt_2_v1,
+ create_save_layer_opt_2_v2,
+ create_save_layer_opt_2_v3,
+ create_save_layer_opt_2_v4,
+ };
+
+ SkTDArray<DrawType> prePattern, postPattern;
+ int xPos = 0, yPos = 0;
+
+ for (int i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
+ SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
+
+ if (!(check_pattern(*pre, prePattern))) {
+ WARN("Pre optimization pattern mismatch");
+ SkASSERT(0);
+ }
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
+ pre->draw(canvas);
+ xPos += pre->width();
+ canvas->restore();
+
+ // re-render the 'pre' picture and thus 'apply' the optimization
+ SkAutoTUnref<SkPicture> post(new SkPicture);
+
+ SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height());
+
+ pre->draw(recordCanvas);
+
+ post->endRecording();
+
+ if (!(check_pattern(*post, postPattern))) {
+ WARN("Post optimization pattern mismatch");
+ SkASSERT(0);
+ }
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
+ post->draw(canvas);
+ xPos += post->width();
+ canvas->restore();
+
+ if (xPos >= kWidth) {
+ // start a new line
+ xPos = 0;
+ yPos += post->height();
+ }
+
+ // TODO: we could also render the pre and post pictures to bitmaps
+ // and manually compare them in this method
+ }
+ }
+
+private:
+ void makeCheckerboard() {
+ static const unsigned int kCheckerboardWidth = 16;
+ static const unsigned int kCheckerboardHeight = 16;
+
+ fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config,
+ kCheckerboardWidth, kCheckerboardHeight);
+ fCheckerboard.allocPixels();
+ SkAutoLockPixels lock(fCheckerboard);
+ for (int y = 0; y < kCheckerboardHeight; y += 2) {
+ SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
+ for (int x = 0; x < kCheckerboardWidth; x += 2) {
+ *scanline++ = 0xFFFFFFFF;
+ *scanline++ = 0xFF000000;
+ }
+ scanline = fCheckerboard.getAddr32(0, y + 1);
+ for (int x = 0; x < kCheckerboardWidth; x += 2) {
+ *scanline++ = 0xFF000000;
+ *scanline++ = 0xFFFFFFFF;
+ }
+ }
+ }
+
+ SkBitmap fCheckerboard;
+
+ typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new OptimizationsGM; )
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index b4104ed2e2..9c2b6430ec 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -5,6 +5,7 @@
'type': 'executable',
'mac_bundle' : 1,
'include_dirs' : [
+ '../debugger',
'../src/core',
'../src/effects', #needed for BlurMask.h
'../gm', # needed to pull gm.h
@@ -15,6 +16,13 @@
'gmslides.gypi',
],
'sources': [
+ '../debugger/SkDrawCommand.h',
+ '../debugger/SkDrawCommand.cpp',
+ '../debugger/SkDebugCanvas.h',
+ '../debugger/SkDebugCanvas.cpp',
+ '../debugger/SkObjectParser.h',
+ '../debugger/SkObjectParser.cpp',
+
'../gm/gm.cpp',
'../gm/gm.h',
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 940ab84361..3458107dbc 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -8,6 +8,7 @@
'target_name': 'gm',
'type': 'executable',
'include_dirs' : [
+ '../debugger',
'../src/core',
'../src/effects',
'../src/pipe/utils/',
@@ -17,9 +18,17 @@
'gmslides.gypi',
],
'sources': [
+ '../debugger/SkDrawCommand.h',
+ '../debugger/SkDrawCommand.cpp',
+ '../debugger/SkDebugCanvas.h',
+ '../debugger/SkDebugCanvas.cpp',
+ '../debugger/SkObjectParser.h',
+ '../debugger/SkObjectParser.cpp',
+
'../gm/gm.cpp',
'../gm/gmmain.cpp',
'../gm/system_preferences_default.cpp',
+
'../src/pipe/utils/SamplePipeControllers.h',
'../src/pipe/utils/SamplePipeControllers.cpp',
],
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index f04098c3b7..6bd07bde5c 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -60,6 +60,7 @@
'../gm/morphology.cpp',
'../gm/ninepatchstretch.cpp',
'../gm/nocolorbleed.cpp',
+ '../gm/optimizations.cpp',
'../gm/patheffects.cpp',
'../gm/pathfill.cpp',
'../gm/pathinterior.cpp',
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 7929557270..1b5783d99c 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -115,7 +115,16 @@ public:
Note: Currently this is not serializable, the bounding data will be
discarded if you serialize into a stream and then deserialize.
*/
- kOptimizeForClippedPlayback_RecordingFlag = 0x02
+ kOptimizeForClippedPlayback_RecordingFlag = 0x02,
+ /*
+ This flag disables all the picture recording optimizations (i.e.,
+ those in SkPictureRecord). It is mainly intended for testing the
+ existing optimizations (i.e., to actually have the pattern
+ appear in an .skp we have to disable the optimization). This
+ option doesn't affect the optimizations controlled by
+ 'kOptimizeForClippedPlayback_RecordingFlag'.
+ */
+ kDisableRecordOptimizations_RecordingFlag = 0x04
};
/** Returns the canvas that records the drawing commands.
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 97ceeb15bf..7fd8a1f0b8 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -412,7 +412,7 @@ static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
/*
* 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_LAYER (with NULL == bounds)
* SAVE
* CLIP_RECT
* DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
@@ -565,18 +565,21 @@ void SkPictureRecord::restore() {
uint32_t initialOffset, size;
size_t opt;
- for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
- if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
- // Some optimization fired so don't add the RESTORE
- size = 0;
- initialOffset = fWriter.size();
- apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
- fStateTree, fBoundingHierarchy);
- break;
+ if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
+ for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
+ if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+ // Some optimization fired so don't add the RESTORE
+ size = 0;
+ initialOffset = fWriter.size();
+ apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
+ fStateTree, fBoundingHierarchy);
+ break;
+ }
}
- }
+ }
- if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
+ if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
+ SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
// No optimization fired so add the RESTORE
fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code