1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm.h" 9 #include "SkDebugCanvas.h" 10 #include "SkPictureFlat.h" 11 12 #define WARN(msg) \ 13 SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg); 14 15 // Do the commands in 'input' match the supplied pattern? Note: this is a pretty 16 // heavy-weight operation since we are drawing the picture into a debug canvas 17 // to extract the commands. 18 static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) { 19 SkDebugCanvas debugCanvas(input.width(), input.height()); 20 debugCanvas.setBounds(input.width(), input.height()); 21 input.draw(&debugCanvas); 22 23 if (pattern.count() != debugCanvas.getSize()) { 24 return false; 25 } 26 27 for (int i = 0; i < pattern.count(); ++i) { 28 if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) { 29 return false; 30 } 31 } 32 33 return true; 34 } 35 36 // construct the pattern removed by the SkPictureRecord::remove_save_layer1 37 // optimization, i.e.: 38 // SAVE_LAYER 39 // DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT 40 // RESTORE 41 // 42 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization 43 // takes a different path if this is false) 44 // dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization 45 // takes a different path if this is false) 46 // colorsMatch - control if the saveLayer and dbmr2r paint colors 47 // match (the optimization will fail if they do not) 48 static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern, 49 SkTDArray<DrawType>* postOptPattern, 50 const SkBitmap& checkerBoard, 51 bool saveLayerHasPaint, 52 bool dbmr2rHasPaint, 53 bool colorsMatch) { 54 // Create the pattern that should trigger the optimization 55 preOptPattern->setCount(5); 56 (*preOptPattern)[0] = SAVE; 57 (*preOptPattern)[1] = SAVE_LAYER; 58 (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; 59 (*preOptPattern)[3] = RESTORE; 60 (*preOptPattern)[4] = RESTORE; 61 62 if (colorsMatch) { 63 // Create the pattern that should appear after the optimization 64 postOptPattern->setCount(5); 65 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 66 (*postOptPattern)[1] = SAVE; 67 (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; 68 (*postOptPattern)[3] = RESTORE; 69 (*postOptPattern)[4] = RESTORE; 70 } else { 71 // Create the pattern that appears if the optimization doesn't fire 72 postOptPattern->setCount(7); 73 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 74 (*postOptPattern)[1] = SAVE; 75 (*postOptPattern)[2] = SAVE_LAYER; 76 (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT; 77 (*postOptPattern)[4] = RESTORE; 78 (*postOptPattern)[5] = RESTORE; 79 (*postOptPattern)[6] = RESTORE; 80 } 81 82 SkPicture* result = new SkPicture; 83 84 // have to disable the optimizations while generating the picture 85 SkCanvas* canvas = result->beginRecording(100, 100, 86 SkPicture::kDisableRecordOptimizations_RecordingFlag); 87 88 SkPaint saveLayerPaint; 89 saveLayerPaint.setColor(0xCC000000); 90 91 // saveLayer's 'bounds' parameter must be NULL for this optimization 92 if (saveLayerHasPaint) { 93 canvas->saveLayer(NULL, &saveLayerPaint); 94 } else { 95 canvas->saveLayer(NULL, NULL); 96 } 97 98 SkRect rect = { 10, 10, 90, 90 }; 99 100 // The dbmr2r's paint must be opaque 101 SkPaint dbmr2rPaint; 102 if (colorsMatch) { 103 dbmr2rPaint.setColor(0xFF000000); 104 } else { 105 dbmr2rPaint.setColor(0xFFFF0000); 106 } 107 108 if (dbmr2rHasPaint) { 109 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); 110 } else { 111 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); 112 } 113 canvas->restore(); 114 115 result->endRecording(); 116 117 return result; 118 } 119 120 // straight-ahead version that is seen in the skps 121 static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern, 122 SkTDArray<DrawType>* postOptPattern, 123 const SkBitmap& checkerBoard) { 124 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 125 true, // saveLayer has a paint 126 true, // dbmr2r has a paint 127 true); // and the colors match 128 } 129 130 // alternate version that should still succeed 131 static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern, 132 SkTDArray<DrawType>* postOptPattern, 133 const SkBitmap& checkerBoard) { 134 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 135 false, // saveLayer doesn't have a paint! 136 true, // dbmr2r has a paint 137 true); // color matching not really applicable 138 } 139 140 // alternate version that should still succeed 141 static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern, 142 SkTDArray<DrawType>* postOptPattern, 143 const SkBitmap& checkerBoard) { 144 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 145 true, // saveLayer has a paint 146 false, // dbmr2r doesn't have a paint! 147 true); // color matching not really applicable 148 } 149 150 // version in which the optimization fails b.c. the colors don't match 151 static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern, 152 SkTDArray<DrawType>* postOptPattern, 153 const SkBitmap& checkerBoard) { 154 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 155 true, // saveLayer has a paint 156 true, // dbmr2r has a paint 157 false); // and the colors don't match! 158 } 159 160 // construct the pattern removed by the SkPictureRecord::remove_save_layer2 161 // optimization, i.e.: 162 // SAVE_LAYER (with NULL == bounds) 163 // SAVE 164 // CLIP_RECT 165 // DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT 166 // RESTORE 167 // RESTORE 168 // 169 // saveLayerHasPaint - control if the saveLayer has a paint (the optimization 170 // takes a different path if this is false) 171 // dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization 172 // takes a different path if this is false) 173 // colorsMatch - control if the saveLayer and dbmr2r paint colors 174 // match (the optimization will fail if they do not) 175 static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern, 176 SkTDArray<DrawType>* postOptPattern, 177 const SkBitmap& checkerBoard, 178 bool saveLayerHasPaint, 179 bool dbmr2rHasPaint, 180 bool colorsMatch) { 181 // Create the pattern that should trigger the optimization 182 preOptPattern->setCount(8); 183 (*preOptPattern)[0] = SAVE; 184 (*preOptPattern)[1] = SAVE_LAYER; 185 (*preOptPattern)[2] = SAVE; 186 (*preOptPattern)[3] = CLIP_RECT; 187 (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; 188 (*preOptPattern)[5] = RESTORE; 189 (*preOptPattern)[6] = RESTORE; 190 (*preOptPattern)[7] = RESTORE; 191 192 if (colorsMatch) { 193 // Create the pattern that should appear after the optimization 194 postOptPattern->setCount(8); 195 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 196 (*postOptPattern)[1] = SAVE; 197 (*postOptPattern)[2] = SAVE; 198 (*postOptPattern)[3] = CLIP_RECT; 199 (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; 200 (*postOptPattern)[5] = RESTORE; 201 (*postOptPattern)[6] = RESTORE; 202 (*postOptPattern)[7] = RESTORE; 203 } else { 204 // Create the pattern that appears if the optimization doesn't fire 205 postOptPattern->setCount(10); 206 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 207 (*postOptPattern)[1] = SAVE; 208 (*postOptPattern)[2] = SAVE_LAYER; 209 (*postOptPattern)[3] = SAVE; 210 (*postOptPattern)[4] = CLIP_RECT; 211 (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT; 212 (*postOptPattern)[6] = RESTORE; 213 (*postOptPattern)[7] = RESTORE; 214 (*postOptPattern)[8] = RESTORE; 215 (*postOptPattern)[9] = RESTORE; 216 } 217 218 SkPicture* result = new SkPicture; 219 220 // have to disable the optimizations while generating the picture 221 SkCanvas* canvas = result->beginRecording(100, 100, 222 SkPicture::kDisableRecordOptimizations_RecordingFlag); 223 224 SkPaint saveLayerPaint; 225 saveLayerPaint.setColor(0xCC000000); 226 227 // saveLayer's 'bounds' parameter must be NULL for this optimization 228 if (saveLayerHasPaint) { 229 canvas->saveLayer(NULL, &saveLayerPaint); 230 } else { 231 canvas->saveLayer(NULL, NULL); 232 } 233 234 canvas->save(); 235 236 SkRect rect = { 10, 10, 90, 90 }; 237 canvas->clipRect(rect); 238 239 // The dbmr2r's paint must be opaque 240 SkPaint dbmr2rPaint; 241 if (colorsMatch) { 242 dbmr2rPaint.setColor(0xFF000000); 243 } else { 244 dbmr2rPaint.setColor(0xFFFF0000); 245 } 246 247 if (dbmr2rHasPaint) { 248 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); 249 } else { 250 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); 251 } 252 canvas->restore(); 253 canvas->restore(); 254 255 result->endRecording(); 256 257 return result; 258 } 259 260 // straight-ahead version that is seen in the skps 261 static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern, 262 SkTDArray<DrawType>* postOptPattern, 263 const SkBitmap& checkerBoard) { 264 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 265 true, // saveLayer has a paint 266 true, // dbmr2r has a paint 267 true); // and the colors match 268 } 269 270 // alternate version that should still succeed 271 static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern, 272 SkTDArray<DrawType>* postOptPattern, 273 const SkBitmap& checkerBoard) { 274 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 275 false, // saveLayer doesn't have a paint! 276 true, // dbmr2r has a paint 277 true); // color matching not really applicable 278 } 279 280 // alternate version that should still succeed 281 static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern, 282 SkTDArray<DrawType>* postOptPattern, 283 const SkBitmap& checkerBoard) { 284 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 285 true, // saveLayer has a paint 286 false, // dbmr2r doesn't have a paint! 287 true); // color matching not really applicable 288 } 289 290 // version in which the optimization fails b.c. the colors don't match 291 static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern, 292 SkTDArray<DrawType>* postOptPattern, 293 const SkBitmap& checkerBoard) { 294 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 295 true, // saveLayer has a paint 296 true, // dbmr2r has a paint 297 false); // and the colors don't match! 298 } 299 300 // As our .skp optimizations get folded into the captured skps our code will 301 // no longer be locally exercised. This GM manually constructs the patterns 302 // our optimizations will remove to test them. It acts as both a GM and a unit 303 // test 304 class OptimizationsGM : public skiagm::GM { 305 public: 306 OptimizationsGM() { 307 this->makeCheckerboard(); 308 } 309 310 static const int kWidth = 800; 311 static const int kHeight = 800; 312 313 protected: 314 SkString onShortName() { 315 return SkString("optimizations"); 316 } 317 318 SkISize onISize() { return SkISize::Make(kWidth, kHeight); } 319 320 typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern, 321 SkTDArray<DrawType> *postOptPattern, 322 const SkBitmap& checkerBoard); 323 324 virtual void onDraw(SkCanvas* canvas) { 325 326 PFCreateOpt gOpts[] = { 327 create_save_layer_opt_1_v1, 328 create_save_layer_opt_1_v2, 329 create_save_layer_opt_1_v3, 330 create_save_layer_opt_1_v4, 331 create_save_layer_opt_2_v1, 332 create_save_layer_opt_2_v2, 333 create_save_layer_opt_2_v3, 334 create_save_layer_opt_2_v4, 335 }; 336 337 SkTDArray<DrawType> prePattern, postPattern; 338 int xPos = 0, yPos = 0; 339 340 for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) { 341 SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard)); 342 343 if (!(check_pattern(*pre, prePattern))) { 344 WARN("Pre optimization pattern mismatch"); 345 SkASSERT(0); 346 } 347 348 canvas->save(); 349 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos)); 350 pre->draw(canvas); 351 xPos += pre->width(); 352 canvas->restore(); 353 354 // re-render the 'pre' picture and thus 'apply' the optimization 355 SkAutoTUnref<SkPicture> post(new SkPicture); 356 357 SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height()); 358 359 pre->draw(recordCanvas); 360 361 post->endRecording(); 362 363 if (!(check_pattern(*post, postPattern))) { 364 WARN("Post optimization pattern mismatch"); 365 SkASSERT(0); 366 } 367 368 canvas->save(); 369 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos)); 370 post->draw(canvas); 371 xPos += post->width(); 372 canvas->restore(); 373 374 if (xPos >= kWidth) { 375 // start a new line 376 xPos = 0; 377 yPos += post->height(); 378 } 379 380 // TODO: we could also render the pre and post pictures to bitmaps 381 // and manually compare them in this method 382 } 383 } 384 385 private: 386 void makeCheckerboard() { 387 static const unsigned int kCheckerboardWidth = 16; 388 static const unsigned int kCheckerboardHeight = 16; 389 390 fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 391 kCheckerboardWidth, kCheckerboardHeight); 392 fCheckerboard.allocPixels(); 393 SkAutoLockPixels lock(fCheckerboard); 394 for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) { 395 SkPMColor* scanline = fCheckerboard.getAddr32(0, y); 396 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { 397 *scanline++ = 0xFFFFFFFF; 398 *scanline++ = 0xFF000000; 399 } 400 scanline = fCheckerboard.getAddr32(0, y + 1); 401 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { 402 *scanline++ = 0xFF000000; 403 *scanline++ = 0xFFFFFFFF; 404 } 405 } 406 } 407 408 SkBitmap fCheckerboard; 409 410 typedef skiagm::GM INHERITED; 411 }; 412 413 ////////////////////////////////////////////////////////////////////////////// 414 415 DEF_GM( return new OptimizationsGM; ) 416