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