Add testing of optimizations to GM

https://codereview.chromium.org/12843028/



git-svn-id: http://skia.googlecode.com/svn/trunk@8654 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
robertphillips@google.com 2013-04-12 14:53:29 +00:00
parent a53e946091
commit c10531b502
6 changed files with 462 additions and 12 deletions

420
gm/optimizations.cpp Normal file
View File

@ -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; )

View File

@ -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',

View File

@ -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',
],

View File

@ -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',

View File

@ -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.

View File

@ -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