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#include "SkPictureRecorder.h" 12 13#define WARN(msg) \ 14 SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg); 15 16// Do the commands in 'input' match the supplied pattern? Note: this is a pretty 17// heavy-weight operation since we are drawing the picture into a debug canvas 18// to extract the commands. 19static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) { 20 SkDebugCanvas debugCanvas(input.width(), input.height()); 21 debugCanvas.setBounds(input.width(), input.height()); 22 input.draw(&debugCanvas); 23 24 if (pattern.count() != debugCanvas.getSize()) { 25 return false; 26 } 27 28 for (int i = 0; i < pattern.count(); ++i) { 29 if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) { 30 return false; 31 } 32 } 33 34 return true; 35} 36 37// construct the pattern removed by the SkPictureRecord::remove_save_layer1 38// optimization, i.e.: 39// SAVE_LAYER 40// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT 41// RESTORE 42// 43// saveLayerHasPaint - control if the saveLayer has a paint (the optimization 44// takes a different path if this is false) 45// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization 46// takes a different path if this is false) 47// colorsMatch - control if the saveLayer and dbmr2r paint colors 48// match (the optimization will fail if they do not) 49static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern, 50 SkTDArray<DrawType>* postOptPattern, 51 const SkBitmap& checkerBoard, 52 bool saveLayerHasPaint, 53 bool dbmr2rHasPaint, 54 bool colorsMatch) { 55 // Create the pattern that should trigger the optimization 56 preOptPattern->setCount(5); 57 (*preOptPattern)[0] = SAVE; 58 (*preOptPattern)[1] = SAVE_LAYER; 59 (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; 60 (*preOptPattern)[3] = RESTORE; 61 (*preOptPattern)[4] = RESTORE; 62 63 if (colorsMatch) { 64 // Create the pattern that should appear after the optimization 65 postOptPattern->setCount(5); 66 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 67 (*postOptPattern)[1] = SAVE; 68 (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; 69 (*postOptPattern)[3] = RESTORE; 70 (*postOptPattern)[4] = RESTORE; 71 } else { 72 // Create the pattern that appears if the optimization doesn't fire 73 postOptPattern->setCount(7); 74 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 75 (*postOptPattern)[1] = SAVE; 76 (*postOptPattern)[2] = SAVE_LAYER; 77 (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT; 78 (*postOptPattern)[4] = RESTORE; 79 (*postOptPattern)[5] = RESTORE; 80 (*postOptPattern)[6] = RESTORE; 81 } 82 83 SkPictureRecorder recorder; 84 85 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 86 // have to disable the optimizations while generating the picture 87 recorder.internalOnly_EnableOpts(false); 88 89 SkPaint saveLayerPaint; 90 saveLayerPaint.setColor(0xCC000000); 91 92 // saveLayer's 'bounds' parameter must be NULL for this optimization 93 if (saveLayerHasPaint) { 94 canvas->saveLayer(NULL, &saveLayerPaint); 95 } else { 96 canvas->saveLayer(NULL, NULL); 97 } 98 99 SkRect rect = { 10, 10, 90, 90 }; 100 101 // The dbmr2r's paint must be opaque 102 SkPaint dbmr2rPaint; 103 if (colorsMatch) { 104 dbmr2rPaint.setColor(0xFF000000); 105 } else { 106 dbmr2rPaint.setColor(0xFFFF0000); 107 } 108 109 if (dbmr2rHasPaint) { 110 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); 111 } else { 112 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); 113 } 114 canvas->restore(); 115 116 return recorder.endRecording(); 117} 118 119// straight-ahead version that is seen in the skps 120static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern, 121 SkTDArray<DrawType>* postOptPattern, 122 const SkBitmap& checkerBoard) { 123 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 124 true, // saveLayer has a paint 125 true, // dbmr2r has a paint 126 true); // and the colors match 127} 128 129// alternate version that should still succeed 130static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern, 131 SkTDArray<DrawType>* postOptPattern, 132 const SkBitmap& checkerBoard) { 133 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 134 false, // saveLayer doesn't have a paint! 135 true, // dbmr2r has a paint 136 true); // color matching not really applicable 137} 138 139// alternate version that should still succeed 140static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern, 141 SkTDArray<DrawType>* postOptPattern, 142 const SkBitmap& checkerBoard) { 143 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 144 true, // saveLayer has a paint 145 false, // dbmr2r doesn't have a paint! 146 true); // color matching not really applicable 147} 148 149// version in which the optimization fails b.c. the colors don't match 150static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern, 151 SkTDArray<DrawType>* postOptPattern, 152 const SkBitmap& checkerBoard) { 153 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, 154 true, // saveLayer has a paint 155 true, // dbmr2r has a paint 156 false); // and the colors don't match! 157} 158 159// construct the pattern removed by the SkPictureRecord::remove_save_layer2 160// optimization, i.e.: 161// SAVE_LAYER (with NULL == bounds) 162// SAVE 163// CLIP_RECT 164// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT 165// RESTORE 166// RESTORE 167// 168// saveLayerHasPaint - control if the saveLayer has a paint (the optimization 169// takes a different path if this is false) 170// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization 171// takes a different path if this is false) 172// colorsMatch - control if the saveLayer and dbmr2r paint colors 173// match (the optimization will fail if they do not) 174static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern, 175 SkTDArray<DrawType>* postOptPattern, 176 const SkBitmap& checkerBoard, 177 bool saveLayerHasPaint, 178 bool dbmr2rHasPaint, 179 bool colorsMatch) { 180 // Create the pattern that should trigger the optimization 181 preOptPattern->setCount(8); 182 (*preOptPattern)[0] = SAVE; 183 (*preOptPattern)[1] = SAVE_LAYER; 184 (*preOptPattern)[2] = SAVE; 185 (*preOptPattern)[3] = CLIP_RECT; 186 (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; 187 (*preOptPattern)[5] = RESTORE; 188 (*preOptPattern)[6] = RESTORE; 189 (*preOptPattern)[7] = RESTORE; 190 191 if (colorsMatch) { 192 // Create the pattern that should appear after the optimization 193 postOptPattern->setCount(8); 194 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 195 (*postOptPattern)[1] = SAVE; 196 (*postOptPattern)[2] = SAVE; 197 (*postOptPattern)[3] = CLIP_RECT; 198 (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; 199 (*postOptPattern)[5] = RESTORE; 200 (*postOptPattern)[6] = RESTORE; 201 (*postOptPattern)[7] = RESTORE; 202 } else { 203 // Create the pattern that appears if the optimization doesn't fire 204 postOptPattern->setCount(10); 205 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw 206 (*postOptPattern)[1] = SAVE; 207 (*postOptPattern)[2] = SAVE_LAYER; 208 (*postOptPattern)[3] = SAVE; 209 (*postOptPattern)[4] = CLIP_RECT; 210 (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT; 211 (*postOptPattern)[6] = RESTORE; 212 (*postOptPattern)[7] = RESTORE; 213 (*postOptPattern)[8] = RESTORE; 214 (*postOptPattern)[9] = RESTORE; 215 } 216 217 SkPictureRecorder recorder; 218 219 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 220 // have to disable the optimizations while generating the picture 221 recorder.internalOnly_EnableOpts(false); 222 223 SkPaint saveLayerPaint; 224 saveLayerPaint.setColor(0xCC000000); 225 226 // saveLayer's 'bounds' parameter must be NULL for this optimization 227 if (saveLayerHasPaint) { 228 canvas->saveLayer(NULL, &saveLayerPaint); 229 } else { 230 canvas->saveLayer(NULL, NULL); 231 } 232 233 canvas->save(); 234 235 SkRect rect = { 10, 10, 90, 90 }; 236 canvas->clipRect(rect); 237 238 // The dbmr2r's paint must be opaque 239 SkPaint dbmr2rPaint; 240 if (colorsMatch) { 241 dbmr2rPaint.setColor(0xFF000000); 242 } else { 243 dbmr2rPaint.setColor(0xFFFF0000); 244 } 245 246 if (dbmr2rHasPaint) { 247 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); 248 } else { 249 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); 250 } 251 canvas->restore(); 252 canvas->restore(); 253 254 return recorder.endRecording(); 255} 256 257// straight-ahead version that is seen in the skps 258static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern, 259 SkTDArray<DrawType>* postOptPattern, 260 const SkBitmap& checkerBoard) { 261 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 262 true, // saveLayer has a paint 263 true, // dbmr2r has a paint 264 true); // and the colors match 265} 266 267// alternate version that should still succeed 268static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern, 269 SkTDArray<DrawType>* postOptPattern, 270 const SkBitmap& checkerBoard) { 271 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 272 false, // saveLayer doesn't have a paint! 273 true, // dbmr2r has a paint 274 true); // color matching not really applicable 275} 276 277// alternate version that should still succeed 278static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern, 279 SkTDArray<DrawType>* postOptPattern, 280 const SkBitmap& checkerBoard) { 281 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 282 true, // saveLayer has a paint 283 false, // dbmr2r doesn't have a paint! 284 true); // color matching not really applicable 285} 286 287// version in which the optimization fails b.c. the colors don't match 288static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern, 289 SkTDArray<DrawType>* postOptPattern, 290 const SkBitmap& checkerBoard) { 291 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, 292 true, // saveLayer has a paint 293 true, // dbmr2r has a paint 294 false); // and the colors don't match! 295} 296 297// As our .skp optimizations get folded into the captured skps our code will 298// no longer be locally exercised. This GM manually constructs the patterns 299// our optimizations will remove to test them. It acts as both a GM and a unit 300// test 301class OptimizationsGM : public skiagm::GM { 302public: 303 OptimizationsGM() { 304 this->makeCheckerboard(); 305 } 306 307 static const int kWidth = 800; 308 static const int kHeight = 800; 309 310protected: 311 uint32_t onGetFlags() const SK_OVERRIDE { 312 // One optimization changes the color drawn slightly in a 565 target. 313 // We've decided it's innocuous, so we disable this GM when targeting 565. 314 // Revisit this if we get finer-grained control: it'd be nice to keep drawing directly. 315 // For more, see skia:1994. 316 return skiagm::GM::kSkip565_Flag; 317 } 318 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 SkPictureRecorder recorder; 361 362 SkCanvas* recordCanvas = recorder.beginRecording(pre->width(), pre->height(), NULL, 0); 363 364 pre->draw(recordCanvas); 365 366 SkAutoTUnref<SkPicture> post(recorder.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 390private: 391 void makeCheckerboard() { 392 static const unsigned int kCheckerboardWidth = 16; 393 static const unsigned int kCheckerboardHeight = 16; 394 395 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight); 396 for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) { 397 SkPMColor* scanline = fCheckerboard.getAddr32(0, y); 398 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { 399 *scanline++ = 0xFFFFFFFF; 400 *scanline++ = 0xFF000000; 401 } 402 scanline = fCheckerboard.getAddr32(0, y + 1); 403 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { 404 *scanline++ = 0xFF000000; 405 *scanline++ = 0xFFFFFFFF; 406 } 407 } 408 } 409 410 SkBitmap fCheckerboard; 411 412 typedef skiagm::GM INHERITED; 413}; 414 415////////////////////////////////////////////////////////////////////////////// 416 417DEF_GM( return new OptimizationsGM; ) 418