1 /* 2 * Copyright 2012 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 #ifndef PictureRenderer_DEFINED 9 #define PictureRenderer_DEFINED 10 11 #include "SkCanvas.h" 12 #include "SkCountdown.h" 13 #include "SkDrawFilter.h" 14 #include "SkMath.h" 15 #include "SkPaint.h" 16 #include "SkPicture.h" 17 #include "SkPictureRecorder.h" 18 #include "SkRect.h" 19 #include "SkRefCnt.h" 20 #include "SkRunnable.h" 21 #include "SkString.h" 22 #include "SkTDArray.h" 23 #include "SkThreadPool.h" 24 #include "SkTypes.h" 25 26 #if SK_SUPPORT_GPU 27 #include "GrContextFactory.h" 28 #include "GrContext.h" 29 #endif 30 31 #include "image_expectations.h" 32 33 class SkBitmap; 34 class SkCanvas; 35 class SkGLContextHelper; 36 class SkThread; 37 38 namespace sk_tools { 39 40 class TiledPictureRenderer; 41 42 class PictureRenderer : public SkRefCnt { 43 44 public: 45 enum SkDeviceTypes { 46 #if SK_ANGLE 47 kAngle_DeviceType, 48 #endif 49 #if SK_MESA 50 kMesa_DeviceType, 51 #endif 52 kBitmap_DeviceType, 53 #if SK_SUPPORT_GPU 54 kGPU_DeviceType, 55 kNVPR_DeviceType, 56 #endif 57 }; 58 59 enum BBoxHierarchyType { 60 kNone_BBoxHierarchyType = 0, 61 kQuadTree_BBoxHierarchyType, 62 kRTree_BBoxHierarchyType, 63 kTileGrid_BBoxHierarchyType, 64 65 kLast_BBoxHierarchyType = kTileGrid_BBoxHierarchyType, 66 }; 67 68 // this uses SkPaint::Flags as a base and adds additional flags 69 enum DrawFilterFlags { 70 kNone_DrawFilterFlag = 0, 71 kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting 72 kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting 73 kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip 74 kMaskFilter_DrawFilterFlag = 0x80000, // toggles on/off mask filters (e.g., blurs) 75 }; 76 77 SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater); 78 SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags), 79 hinting_flag_must_be_greater); 80 SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags), 81 slight_hinting_flag_must_be_greater); 82 83 /** 84 * Called with each new SkPicture to render. 85 * 86 * @param pict The SkPicture to render. 87 * @param writePath The output directory within which this renderer should write all images, 88 * or NULL if this renderer should not write all images. 89 * @param mismatchPath The output directory within which this renderer should write any images 90 * which do not match expectations, or NULL if this renderer should not write mismatches. 91 * @param inputFilename The name of the input file we are rendering. 92 * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing 93 * bitmap images to disk. 94 */ 95 virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, 96 const SkString* inputFilename, bool useChecksumBasedFilenames); 97 98 /** 99 * TODO(epoger): Temporary hack, while we work on http://skbug.com/2584 ('bench_pictures is 100 * timing reading pixels and writing json files'), such that: 101 * - render_pictures can call this method and continue to work 102 * - any other callers (bench_pictures) will skip calls to write() by default 103 */ 104 void enableWrites() { fEnableWrites = true; } 105 106 /** 107 * Set the viewport so that only the portion listed gets drawn. 108 */ 109 void setViewport(SkISize size) { fViewport = size; } 110 111 /** 112 * Set the scale factor at which draw the picture. 113 */ 114 void setScaleFactor(SkScalar scale) { fScaleFactor = scale; } 115 116 /** 117 * Perform any setup that should done prior to each iteration of render() which should not be 118 * timed. 119 */ 120 virtual void setup() {} 121 122 /** 123 * Perform the work. If this is being called within the context of bench_pictures, 124 * this is the step that will be timed. 125 * 126 * Typically "the work" is rendering an SkPicture into a bitmap, but in some subclasses 127 * it is recording the source SkPicture into another SkPicture. 128 * 129 * If fWritePath has been specified, the result of the work will be written to that dir. 130 * If fMismatchPath has been specified, and the actual image result differs from its 131 * expectation, the result of the work will be written to that dir. 132 * 133 * @param out If non-null, the implementing subclass MAY allocate an SkBitmap, copy the 134 * output image into it, and return it here. (Some subclasses ignore this parameter) 135 * @return bool True if rendering succeeded and, if fWritePath had been specified, the output 136 * was successfully written to a file. 137 */ 138 virtual bool render(SkBitmap** out = NULL) = 0; 139 140 /** 141 * Called once finished with a particular SkPicture, before calling init again, and before 142 * being done with this Renderer. 143 */ 144 virtual void end(); 145 146 /** 147 * If this PictureRenderer is actually a TiledPictureRender, return a pointer to this as a 148 * TiledPictureRender so its methods can be called. 149 */ 150 virtual TiledPictureRenderer* getTiledRenderer() { return NULL; } 151 152 /** 153 * Resets the GPU's state. Does nothing if the backing is raster. For a GPU renderer, calls 154 * flush, swapBuffers and, if callFinish is true, finish. 155 * @param callFinish Whether to call finish. 156 */ 157 void resetState(bool callFinish); 158 159 /** 160 * Remove all decoded textures from the CPU caches and all uploaded textures 161 * from the GPU. 162 */ 163 void purgeTextures(); 164 165 /** 166 * Set the backend type. Returns true on success and false on failure. 167 */ 168 bool setDeviceType(SkDeviceTypes deviceType) { 169 fDeviceType = deviceType; 170 #if SK_SUPPORT_GPU 171 // In case this function is called more than once 172 SkSafeUnref(fGrContext); 173 fGrContext = NULL; 174 // Set to Native so it will have an initial value. 175 GrContextFactory::GLContextType glContextType = GrContextFactory::kNative_GLContextType; 176 #endif 177 switch(deviceType) { 178 case kBitmap_DeviceType: 179 return true; 180 #if SK_SUPPORT_GPU 181 case kGPU_DeviceType: 182 // Already set to GrContextFactory::kNative_GLContextType, above. 183 break; 184 case kNVPR_DeviceType: 185 glContextType = GrContextFactory::kNVPR_GLContextType; 186 break; 187 #if SK_ANGLE 188 case kAngle_DeviceType: 189 glContextType = GrContextFactory::kANGLE_GLContextType; 190 break; 191 #endif 192 #if SK_MESA 193 case kMesa_DeviceType: 194 glContextType = GrContextFactory::kMESA_GLContextType; 195 break; 196 #endif 197 #endif 198 default: 199 // Invalid device type. 200 return false; 201 } 202 #if SK_SUPPORT_GPU 203 fGrContext = fGrContextFactory.get(glContextType); 204 if (NULL == fGrContext) { 205 return false; 206 } else { 207 fGrContext->ref(); 208 return true; 209 } 210 #endif 211 } 212 213 #if SK_SUPPORT_GPU 214 void setSampleCount(int sampleCount) { 215 fSampleCount = sampleCount; 216 } 217 #endif 218 219 void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) { 220 memcpy(fDrawFilters, filters, sizeof(fDrawFilters)); 221 fDrawFiltersConfig = configName; 222 } 223 224 void setBBoxHierarchyType(BBoxHierarchyType bbhType) { 225 fBBoxHierarchyType = bbhType; 226 } 227 228 BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; } 229 230 void setGridSize(int width, int height) { 231 fGridInfo.fTileInterval.set(width, height); 232 } 233 234 void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) { 235 fJsonSummaryPtr = jsonSummaryPtr; 236 } 237 238 bool isUsingBitmapDevice() { 239 return kBitmap_DeviceType == fDeviceType; 240 } 241 242 virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); } 243 244 virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); } 245 246 /** 247 * Reports the configuration of this PictureRenderer. 248 */ 249 SkString getConfigName() { 250 SkString config = this->getConfigNameInternal(); 251 if (!fViewport.isEmpty()) { 252 config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height()); 253 } 254 if (fScaleFactor != SK_Scalar1) { 255 config.appendf("_scalar_%f", SkScalarToFloat(fScaleFactor)); 256 } 257 if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) { 258 config.append("_rtree"); 259 } else if (kQuadTree_BBoxHierarchyType == fBBoxHierarchyType) { 260 config.append("_quadtree"); 261 } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) { 262 config.append("_grid"); 263 config.append("_"); 264 config.appendS32(fGridInfo.fTileInterval.width()); 265 config.append("x"); 266 config.appendS32(fGridInfo.fTileInterval.height()); 267 } 268 #if SK_SUPPORT_GPU 269 switch (fDeviceType) { 270 case kGPU_DeviceType: 271 if (fSampleCount) { 272 config.appendf("_msaa%d", fSampleCount); 273 } else { 274 config.append("_gpu"); 275 } 276 break; 277 case kNVPR_DeviceType: 278 config.appendf("_nvprmsaa%d", fSampleCount); 279 break; 280 #if SK_ANGLE 281 case kAngle_DeviceType: 282 config.append("_angle"); 283 break; 284 #endif 285 #if SK_MESA 286 case kMesa_DeviceType: 287 config.append("_mesa"); 288 break; 289 #endif 290 default: 291 // Assume that no extra info means bitmap. 292 break; 293 } 294 #endif 295 config.append(fDrawFiltersConfig.c_str()); 296 return config; 297 } 298 299 #if SK_SUPPORT_GPU 300 bool isUsingGpuDevice() { 301 switch (fDeviceType) { 302 case kGPU_DeviceType: 303 case kNVPR_DeviceType: 304 // fall through 305 #if SK_ANGLE 306 case kAngle_DeviceType: 307 // fall through 308 #endif 309 #if SK_MESA 310 case kMesa_DeviceType: 311 #endif 312 return true; 313 default: 314 return false; 315 } 316 } 317 318 SkGLContextHelper* getGLContext() { 319 GrContextFactory::GLContextType glContextType 320 = GrContextFactory::kNull_GLContextType; 321 switch(fDeviceType) { 322 case kGPU_DeviceType: 323 glContextType = GrContextFactory::kNative_GLContextType; 324 break; 325 case kNVPR_DeviceType: 326 glContextType = GrContextFactory::kNVPR_GLContextType; 327 break; 328 #if SK_ANGLE 329 case kAngle_DeviceType: 330 glContextType = GrContextFactory::kANGLE_GLContextType; 331 break; 332 #endif 333 #if SK_MESA 334 case kMesa_DeviceType: 335 glContextType = GrContextFactory::kMESA_GLContextType; 336 break; 337 #endif 338 default: 339 return NULL; 340 } 341 return fGrContextFactory.getGLContext(glContextType); 342 } 343 344 GrContext* getGrContext() { 345 return fGrContext; 346 } 347 #endif 348 349 SkCanvas* getCanvas() { 350 return fCanvas; 351 } 352 353 SkPicture* getPicture() { 354 return fPicture; 355 } 356 357 PictureRenderer() 358 : fJsonSummaryPtr(NULL) 359 , fDeviceType(kBitmap_DeviceType) 360 , fEnableWrites(false) 361 , fBBoxHierarchyType(kNone_BBoxHierarchyType) 362 , fScaleFactor(SK_Scalar1) 363 #if SK_SUPPORT_GPU 364 , fGrContext(NULL) 365 , fSampleCount(0) 366 #endif 367 { 368 fGridInfo.fMargin.setEmpty(); 369 fGridInfo.fOffset.setZero(); 370 fGridInfo.fTileInterval.set(1, 1); 371 sk_bzero(fDrawFilters, sizeof(fDrawFilters)); 372 fViewport.set(0, 0); 373 } 374 375 #if SK_SUPPORT_GPU 376 virtual ~PictureRenderer() { 377 SkSafeUnref(fGrContext); 378 } 379 #endif 380 381 protected: 382 SkAutoTUnref<SkCanvas> fCanvas; 383 SkAutoTUnref<SkPicture> fPicture; 384 bool fUseChecksumBasedFilenames; 385 ImageResultsAndExpectations* fJsonSummaryPtr; 386 SkDeviceTypes fDeviceType; 387 bool fEnableWrites; 388 BBoxHierarchyType fBBoxHierarchyType; 389 DrawFilterFlags fDrawFilters[SkDrawFilter::kTypeCount]; 390 SkString fDrawFiltersConfig; 391 SkString fWritePath; 392 SkString fMismatchPath; 393 SkString fInputFilename; 394 SkTileGridFactory::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid 395 396 void buildBBoxHierarchy(); 397 398 /** 399 * Return the total width that should be drawn. If the viewport width has been set greater than 400 * 0, this will be the minimum of the current SkPicture's width and the viewport's width. 401 */ 402 int getViewWidth(); 403 404 /** 405 * Return the total height that should be drawn. If the viewport height has been set greater 406 * than 0, this will be the minimum of the current SkPicture's height and the viewport's height. 407 */ 408 int getViewHeight(); 409 410 /** 411 * Scales the provided canvas to the scale factor set by setScaleFactor. 412 */ 413 void scaleToScaleFactor(SkCanvas*); 414 415 SkBBHFactory* getFactory(); 416 uint32_t recordFlags() const { return 0; } 417 SkCanvas* setupCanvas(); 418 virtual SkCanvas* setupCanvas(int width, int height); 419 420 /** 421 * Copy src to dest; if src==NULL, set dest to empty string. 422 */ 423 static void CopyString(SkString* dest, const SkString* src); 424 425 private: 426 SkISize fViewport; 427 SkScalar fScaleFactor; 428 #if SK_SUPPORT_GPU 429 GrContextFactory fGrContextFactory; 430 GrContext* fGrContext; 431 int fSampleCount; 432 #endif 433 434 virtual SkString getConfigNameInternal() = 0; 435 436 typedef SkRefCnt INHERITED; 437 }; 438 439 /** 440 * This class does not do any rendering, but its render function executes recording, which we want 441 * to time. 442 */ 443 class RecordPictureRenderer : public PictureRenderer { 444 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 445 446 virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } 447 448 virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); } 449 450 protected: 451 virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; 452 453 private: 454 virtual SkString getConfigNameInternal() SK_OVERRIDE; 455 }; 456 457 class PipePictureRenderer : public PictureRenderer { 458 public: 459 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 460 461 private: 462 virtual SkString getConfigNameInternal() SK_OVERRIDE; 463 464 typedef PictureRenderer INHERITED; 465 }; 466 467 class SimplePictureRenderer : public PictureRenderer { 468 public: 469 virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, 470 const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; 471 472 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 473 474 private: 475 virtual SkString getConfigNameInternal() SK_OVERRIDE; 476 477 typedef PictureRenderer INHERITED; 478 }; 479 480 class TiledPictureRenderer : public PictureRenderer { 481 public: 482 TiledPictureRenderer(); 483 484 virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, 485 const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; 486 487 /** 488 * Renders to tiles, rather than a single canvas. 489 * If fWritePath was provided, a separate file is 490 * created for each tile, named "path0.png", "path1.png", etc. 491 * Multithreaded mode currently does not support writing to a file. 492 */ 493 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 494 495 virtual void end() SK_OVERRIDE; 496 497 void setTileWidth(int width) { 498 fTileWidth = width; 499 } 500 501 int getTileWidth() const { 502 return fTileWidth; 503 } 504 505 void setTileHeight(int height) { 506 fTileHeight = height; 507 } 508 509 int getTileHeight() const { 510 return fTileHeight; 511 } 512 513 void setTileWidthPercentage(double percentage) { 514 fTileWidthPercentage = percentage; 515 } 516 517 double getTileWidthPercentage() const { 518 return fTileWidthPercentage; 519 } 520 521 void setTileHeightPercentage(double percentage) { 522 fTileHeightPercentage = percentage; 523 } 524 525 double getTileHeightPercentage() const { 526 return fTileHeightPercentage; 527 } 528 529 void setTileMinPowerOf2Width(int width) { 530 SkASSERT(SkIsPow2(width) && width > 0); 531 if (!SkIsPow2(width) || width <= 0) { 532 return; 533 } 534 535 fTileMinPowerOf2Width = width; 536 } 537 538 int getTileMinPowerOf2Width() const { 539 return fTileMinPowerOf2Width; 540 } 541 542 virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; } 543 544 virtual bool supportsTimingIndividualTiles() { return true; } 545 546 /** 547 * Report the number of tiles in the x and y directions. Must not be called before init. 548 * @param x Output parameter identifying the number of tiles in the x direction. 549 * @param y Output parameter identifying the number of tiles in the y direction. 550 * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are 551 * unmodified. 552 */ 553 bool tileDimensions(int& x, int&y); 554 555 /** 556 * Move to the next tile and return its indices. Must be called before calling drawCurrentTile 557 * for the first time. 558 * @param i Output parameter identifying the column of the next tile to be drawn on the next 559 * call to drawNextTile. 560 * @param j Output parameter identifying the row of the next tile to be drawn on the next call 561 * to drawNextTile. 562 * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile 563 * is within the range of tiles. If false, i and j are unmodified. 564 */ 565 bool nextTile(int& i, int& j); 566 567 /** 568 * Render one tile. This will draw the same tile each time it is called until nextTile is 569 * called. The tile rendered will depend on how many calls have been made to nextTile. 570 * It is an error to call this without first calling nextTile, or if nextTile returns false. 571 */ 572 void drawCurrentTile(); 573 574 protected: 575 SkTDArray<SkRect> fTileRects; 576 577 virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; 578 virtual SkString getConfigNameInternal() SK_OVERRIDE; 579 580 private: 581 int fTileWidth; 582 int fTileHeight; 583 double fTileWidthPercentage; 584 double fTileHeightPercentage; 585 int fTileMinPowerOf2Width; 586 587 // These variables are only used for timing individual tiles. 588 // Next tile to draw in fTileRects. 589 int fCurrentTileOffset; 590 // Number of tiles in the x direction. 591 int fTilesX; 592 // Number of tiles in the y direction. 593 int fTilesY; 594 595 void setupTiles(); 596 void setupPowerOf2Tiles(); 597 598 typedef PictureRenderer INHERITED; 599 }; 600 601 class CloneData; 602 603 class MultiCorePictureRenderer : public TiledPictureRenderer { 604 public: 605 explicit MultiCorePictureRenderer(int threadCount); 606 607 ~MultiCorePictureRenderer(); 608 609 virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, 610 const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; 611 612 /** 613 * Behaves like TiledPictureRenderer::render(), only using multiple threads. 614 */ 615 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 616 617 virtual void end() SK_OVERRIDE; 618 619 virtual bool supportsTimingIndividualTiles() SK_OVERRIDE { return false; } 620 621 private: 622 virtual SkString getConfigNameInternal() SK_OVERRIDE; 623 624 const int fNumThreads; 625 SkTDArray<SkCanvas*> fCanvasPool; 626 SkThreadPool fThreadPool; 627 SkPicture* fPictureClones; 628 CloneData** fCloneData; 629 SkCountdown fCountdown; 630 631 typedef TiledPictureRenderer INHERITED; 632 }; 633 634 /** 635 * This class does not do any rendering, but its render function executes turning an SkPictureRecord 636 * into an SkPicturePlayback, which we want to time. 637 */ 638 class PlaybackCreationRenderer : public PictureRenderer { 639 public: 640 virtual void setup() SK_OVERRIDE; 641 642 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 643 644 virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } 645 646 virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); } 647 648 private: 649 SkAutoTDelete<SkPictureRecorder> fRecorder; 650 651 virtual SkString getConfigNameInternal() SK_OVERRIDE; 652 653 typedef PictureRenderer INHERITED; 654 }; 655 656 extern PictureRenderer* CreateGatherPixelRefsRenderer(); 657 extern PictureRenderer* CreatePictureCloneRenderer(); 658 659 } 660 661 #endif // PictureRenderer_DEFINED 662