diff options
-rw-r--r-- | gm/optimizations.cpp | 420 | ||||
-rw-r--r-- | gyp/SampleApp.gyp | 8 | ||||
-rw-r--r-- | gyp/gm.gyp | 9 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/core/SkPicture.h | 11 | ||||
-rw-r--r-- | src/core/SkPictureRecord.cpp | 25 |
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 |