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 "SkDrawFilter.h" 13#include "SkJSONCPP.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 "SkString.h" 21#include "SkTDArray.h" 22#include "SkTypes.h" 23 24#if SK_SUPPORT_GPU 25#include "GrContextFactory.h" 26#include "GrContext.h" 27#endif 28 29#include "image_expectations.h" 30 31class SkBitmap; 32class SkCanvas; 33class SkGLContextHelper; 34class SkThread; 35 36namespace sk_tools { 37 38class TiledPictureRenderer; 39 40class PictureRenderer : public SkRefCnt { 41 42public: 43 enum SkDeviceTypes { 44#if SK_ANGLE 45 kAngle_DeviceType, 46#endif 47#if SK_MESA 48 kMesa_DeviceType, 49#endif 50 kBitmap_DeviceType, 51#if SK_SUPPORT_GPU 52 kGPU_DeviceType, 53 kNVPR_DeviceType, 54#endif 55 }; 56 57 enum BBoxHierarchyType { 58 kNone_BBoxHierarchyType = 0, 59 kRTree_BBoxHierarchyType, 60 kTileGrid_BBoxHierarchyType, 61 62 kLast_BBoxHierarchyType = kTileGrid_BBoxHierarchyType, 63 }; 64 65 // this uses SkPaint::Flags as a base and adds additional flags 66 enum DrawFilterFlags { 67 kNone_DrawFilterFlag = 0, 68 kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting 69 kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting 70 kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip 71 kMaskFilter_DrawFilterFlag = 0x80000, // toggles on/off mask filters (e.g., blurs) 72 }; 73 74 SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater); 75 SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags), 76 hinting_flag_must_be_greater); 77 SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags), 78 slight_hinting_flag_must_be_greater); 79 80 /** 81 * Called with each new SkPicture to render. 82 * 83 * @param pict The SkPicture to render. 84 * @param writePath The output directory within which this renderer should write all images, 85 * or NULL if this renderer should not write all images. 86 * @param mismatchPath The output directory within which this renderer should write any images 87 * which do not match expectations, or NULL if this renderer should not write mismatches. 88 * @param inputFilename The name of the input file we are rendering. 89 * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing 90 * bitmap images to disk. 91 */ 92 virtual void init(const SkPicture* pict, 93 const SkString* writePath, 94 const SkString* mismatchPath, 95 const SkString* inputFilename, 96 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#if SK_SUPPORT_GPU 169 bool setDeviceType(SkDeviceTypes deviceType, GrGLStandard gpuAPI = kNone_GrGLStandard) { 170#else 171 bool setDeviceType(SkDeviceTypes deviceType) { 172#endif 173 fDeviceType = deviceType; 174#if SK_SUPPORT_GPU 175 // In case this function is called more than once 176 SkSafeUnref(fGrContext); 177 fGrContext = NULL; 178 // Set to Native so it will have an initial value. 179 GrContextFactory::GLContextType glContextType = GrContextFactory::kNative_GLContextType; 180#endif 181 switch(deviceType) { 182 case kBitmap_DeviceType: 183 return true; 184#if SK_SUPPORT_GPU 185 case kGPU_DeviceType: 186 // Already set to GrContextFactory::kNative_GLContextType, above. 187 break; 188 case kNVPR_DeviceType: 189 glContextType = GrContextFactory::kNVPR_GLContextType; 190 break; 191#if SK_ANGLE 192 case kAngle_DeviceType: 193 glContextType = GrContextFactory::kANGLE_GLContextType; 194 break; 195#endif 196#if SK_MESA 197 case kMesa_DeviceType: 198 glContextType = GrContextFactory::kMESA_GLContextType; 199 break; 200#endif 201#endif 202 default: 203 // Invalid device type. 204 return false; 205 } 206#if SK_SUPPORT_GPU 207 fGrContext = fGrContextFactory.get(glContextType, gpuAPI); 208 if (NULL == fGrContext) { 209 return false; 210 } else { 211 fGrContext->ref(); 212 return true; 213 } 214#endif 215 } 216 217#if SK_SUPPORT_GPU 218 void setSampleCount(int sampleCount) { 219 fSampleCount = sampleCount; 220 } 221#endif 222 223 void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) { 224 memcpy(fDrawFilters, filters, sizeof(fDrawFilters)); 225 fDrawFiltersConfig = configName; 226 } 227 228 void setBBoxHierarchyType(BBoxHierarchyType bbhType) { 229 fBBoxHierarchyType = bbhType; 230 } 231 232 BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; } 233 234 void setGridSize(int width, int height) { 235 fGridInfo.fTileInterval.set(width, height); 236 } 237 238 void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) { 239 fJsonSummaryPtr = jsonSummaryPtr; 240 } 241 242 bool isUsingBitmapDevice() { 243 return kBitmap_DeviceType == fDeviceType; 244 } 245 246 virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); } 247 248 virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); } 249 250 /** 251 * Reports the configuration of this PictureRenderer. 252 */ 253 SkString getConfigName() { 254 SkString config = this->getConfigNameInternal(); 255 if (!fViewport.isEmpty()) { 256 config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height()); 257 } 258 if (fScaleFactor != SK_Scalar1) { 259 config.appendf("_scalar_%f", SkScalarToFloat(fScaleFactor)); 260 } 261 if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) { 262 config.append("_rtree"); 263 } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) { 264 config.append("_grid"); 265 config.append("_"); 266 config.appendS32(fGridInfo.fTileInterval.width()); 267 config.append("x"); 268 config.appendS32(fGridInfo.fTileInterval.height()); 269 } 270#if SK_SUPPORT_GPU 271 switch (fDeviceType) { 272 case kGPU_DeviceType: 273 if (fSampleCount) { 274 config.appendf("_msaa%d", fSampleCount); 275 } else { 276 config.append("_gpu"); 277 } 278 break; 279 case kNVPR_DeviceType: 280 config.appendf("_nvprmsaa%d", fSampleCount); 281 break; 282#if SK_ANGLE 283 case kAngle_DeviceType: 284 config.append("_angle"); 285 break; 286#endif 287#if SK_MESA 288 case kMesa_DeviceType: 289 config.append("_mesa"); 290 break; 291#endif 292 default: 293 // Assume that no extra info means bitmap. 294 break; 295 } 296#endif 297 config.append(fDrawFiltersConfig.c_str()); 298 return config; 299 } 300 301 Json::Value getJSONConfig() { 302 Json::Value result; 303 304 result["mode"] = this->getConfigNameInternal().c_str(); 305 result["scale"] = 1.0f; 306 if (SK_Scalar1 != fScaleFactor) { 307 result["scale"] = SkScalarToFloat(fScaleFactor); 308 } 309 if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) { 310 result["bbh"] = "rtree"; 311 } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) { 312 SkString tmp("grid_"); 313 tmp.appendS32(fGridInfo.fTileInterval.width()); 314 tmp.append("x"); 315 tmp.appendS32(fGridInfo.fTileInterval.height()); 316 result["bbh"] = tmp.c_str(); 317 } 318#if SK_SUPPORT_GPU 319 SkString tmp; 320 switch (fDeviceType) { 321 case kGPU_DeviceType: 322 if (0 != fSampleCount) { 323 tmp = "msaa"; 324 tmp.appendS32(fSampleCount); 325 result["config"] = tmp.c_str(); 326 } else { 327 result["config"] = "gpu"; 328 } 329 break; 330 case kNVPR_DeviceType: 331 tmp = "nvprmsaa"; 332 tmp.appendS32(fSampleCount); 333 result["config"] = tmp.c_str(); 334 break; 335#if SK_ANGLE 336 case kAngle_DeviceType: 337 result["config"] = "angle"; 338 break; 339#endif 340#if SK_MESA 341 case kMesa_DeviceType: 342 result["config"] = "mesa"; 343 break; 344#endif 345 default: 346 // Assume that no extra info means bitmap. 347 break; 348 } 349#endif 350 return result; 351 } 352 353#if SK_SUPPORT_GPU 354 bool isUsingGpuDevice() { 355 switch (fDeviceType) { 356 case kGPU_DeviceType: 357 case kNVPR_DeviceType: 358 // fall through 359#if SK_ANGLE 360 case kAngle_DeviceType: 361 // fall through 362#endif 363#if SK_MESA 364 case kMesa_DeviceType: 365#endif 366 return true; 367 default: 368 return false; 369 } 370 } 371 372 SkGLContextHelper* getGLContext() { 373 GrContextFactory::GLContextType glContextType 374 = GrContextFactory::kNull_GLContextType; 375 switch(fDeviceType) { 376 case kGPU_DeviceType: 377 glContextType = GrContextFactory::kNative_GLContextType; 378 break; 379 case kNVPR_DeviceType: 380 glContextType = GrContextFactory::kNVPR_GLContextType; 381 break; 382#if SK_ANGLE 383 case kAngle_DeviceType: 384 glContextType = GrContextFactory::kANGLE_GLContextType; 385 break; 386#endif 387#if SK_MESA 388 case kMesa_DeviceType: 389 glContextType = GrContextFactory::kMESA_GLContextType; 390 break; 391#endif 392 default: 393 return NULL; 394 } 395 return fGrContextFactory.getGLContext(glContextType); 396 } 397 398 GrContext* getGrContext() { 399 return fGrContext; 400 } 401 402 const GrContext::Options& getGrContextOptions() { 403 return fGrContextFactory.getGlobalOptions(); 404 } 405#endif 406 407 SkCanvas* getCanvas() { 408 return fCanvas; 409 } 410 411 const SkPicture* getPicture() { 412 return fPicture; 413 } 414 415#if SK_SUPPORT_GPU 416 explicit PictureRenderer(const GrContext::Options &opts) 417#else 418 PictureRenderer() 419#endif 420 : fJsonSummaryPtr(NULL) 421 , fDeviceType(kBitmap_DeviceType) 422 , fEnableWrites(false) 423 , fBBoxHierarchyType(kNone_BBoxHierarchyType) 424 , fScaleFactor(SK_Scalar1) 425#if SK_SUPPORT_GPU 426 , fGrContextFactory(opts) 427 , fGrContext(NULL) 428 , fSampleCount(0) 429#endif 430 { 431 fGridInfo.fMargin.setEmpty(); 432 fGridInfo.fOffset.setZero(); 433 fGridInfo.fTileInterval.set(1, 1); 434 sk_bzero(fDrawFilters, sizeof(fDrawFilters)); 435 fViewport.set(0, 0); 436 } 437 438#if SK_SUPPORT_GPU 439 virtual ~PictureRenderer() { 440 SkSafeUnref(fGrContext); 441 } 442#endif 443 444protected: 445 SkAutoTUnref<SkCanvas> fCanvas; 446 SkAutoTUnref<const SkPicture> fPicture; 447 bool fUseChecksumBasedFilenames; 448 ImageResultsAndExpectations* fJsonSummaryPtr; 449 SkDeviceTypes fDeviceType; 450 bool fEnableWrites; 451 BBoxHierarchyType fBBoxHierarchyType; 452 DrawFilterFlags fDrawFilters[SkDrawFilter::kTypeCount]; 453 SkString fDrawFiltersConfig; 454 SkString fWritePath; 455 SkString fMismatchPath; 456 SkString fInputFilename; 457 SkTileGridFactory::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid 458 459 void buildBBoxHierarchy(); 460 461 /** 462 * Return the total width that should be drawn. If the viewport width has been set greater than 463 * 0, this will be the minimum of the current SkPicture's width and the viewport's width. 464 */ 465 int getViewWidth(); 466 467 /** 468 * Return the total height that should be drawn. If the viewport height has been set greater 469 * than 0, this will be the minimum of the current SkPicture's height and the viewport's height. 470 */ 471 int getViewHeight(); 472 473 /** 474 * Scales the provided canvas to the scale factor set by setScaleFactor. 475 */ 476 void scaleToScaleFactor(SkCanvas*); 477 478 SkBBHFactory* getFactory(); 479 uint32_t recordFlags() const { return 0; } 480 SkCanvas* setupCanvas(); 481 virtual SkCanvas* setupCanvas(int width, int height); 482 483 /** 484 * Copy src to dest; if src==NULL, set dest to empty string. 485 */ 486 static void CopyString(SkString* dest, const SkString* src); 487 488private: 489 SkISize fViewport; 490 SkScalar fScaleFactor; 491#if SK_SUPPORT_GPU 492 GrContextFactory fGrContextFactory; 493 GrContext* fGrContext; 494 int fSampleCount; 495#endif 496 497 virtual SkString getConfigNameInternal() = 0; 498 499 typedef SkRefCnt INHERITED; 500}; 501 502/** 503 * This class does not do any rendering, but its render function executes recording, which we want 504 * to time. 505 */ 506class RecordPictureRenderer : public PictureRenderer { 507public: 508#if SK_SUPPORT_GPU 509 RecordPictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { } 510#endif 511 512 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 513 514 virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } 515 516 virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); } 517 518protected: 519 virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; 520 521private: 522 virtual SkString getConfigNameInternal() SK_OVERRIDE; 523 524 typedef PictureRenderer INHERITED; 525}; 526 527class PipePictureRenderer : public PictureRenderer { 528public: 529#if SK_SUPPORT_GPU 530 PipePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { } 531#endif 532 533 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 534 535private: 536 virtual SkString getConfigNameInternal() SK_OVERRIDE; 537 538 typedef PictureRenderer INHERITED; 539}; 540 541class SimplePictureRenderer : public PictureRenderer { 542public: 543#if SK_SUPPORT_GPU 544 SimplePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { } 545#endif 546 547 virtual void init(const SkPicture* pict, 548 const SkString* writePath, 549 const SkString* mismatchPath, 550 const SkString* inputFilename, 551 bool useChecksumBasedFilenames) SK_OVERRIDE; 552 553 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 554 555private: 556 virtual SkString getConfigNameInternal() SK_OVERRIDE; 557 558 typedef PictureRenderer INHERITED; 559}; 560 561class TiledPictureRenderer : public PictureRenderer { 562public: 563#if SK_SUPPORT_GPU 564 TiledPictureRenderer(const GrContext::Options &opts); 565#else 566 TiledPictureRenderer(); 567#endif 568 569 virtual void init(const SkPicture* pict, 570 const SkString* writePath, 571 const SkString* mismatchPath, 572 const SkString* inputFilename, 573 bool useChecksumBasedFilenames) SK_OVERRIDE; 574 575 /** 576 * Renders to tiles, rather than a single canvas. 577 * If fWritePath was provided, a separate file is 578 * created for each tile, named "path0.png", "path1.png", etc. 579 */ 580 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 581 582 virtual void end() SK_OVERRIDE; 583 584 void setTileWidth(int width) { 585 fTileWidth = width; 586 } 587 588 int getTileWidth() const { 589 return fTileWidth; 590 } 591 592 void setTileHeight(int height) { 593 fTileHeight = height; 594 } 595 596 int getTileHeight() const { 597 return fTileHeight; 598 } 599 600 void setTileWidthPercentage(double percentage) { 601 fTileWidthPercentage = percentage; 602 } 603 604 double getTileWidthPercentage() const { 605 return fTileWidthPercentage; 606 } 607 608 void setTileHeightPercentage(double percentage) { 609 fTileHeightPercentage = percentage; 610 } 611 612 double getTileHeightPercentage() const { 613 return fTileHeightPercentage; 614 } 615 616 void setTileMinPowerOf2Width(int width) { 617 SkASSERT(SkIsPow2(width) && width > 0); 618 if (!SkIsPow2(width) || width <= 0) { 619 return; 620 } 621 622 fTileMinPowerOf2Width = width; 623 } 624 625 int getTileMinPowerOf2Width() const { 626 return fTileMinPowerOf2Width; 627 } 628 629 virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; } 630 631 virtual bool supportsTimingIndividualTiles() { return true; } 632 633 /** 634 * Report the number of tiles in the x and y directions. Must not be called before init. 635 * @param x Output parameter identifying the number of tiles in the x direction. 636 * @param y Output parameter identifying the number of tiles in the y direction. 637 * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are 638 * unmodified. 639 */ 640 bool tileDimensions(int& x, int&y); 641 642 /** 643 * Move to the next tile and return its indices. Must be called before calling drawCurrentTile 644 * for the first time. 645 * @param i Output parameter identifying the column of the next tile to be drawn on the next 646 * call to drawNextTile. 647 * @param j Output parameter identifying the row of the next tile to be drawn on the next call 648 * to drawNextTile. 649 * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile 650 * is within the range of tiles. If false, i and j are unmodified. 651 */ 652 bool nextTile(int& i, int& j); 653 654 /** 655 * Render one tile. This will draw the same tile each time it is called until nextTile is 656 * called. The tile rendered will depend on how many calls have been made to nextTile. 657 * It is an error to call this without first calling nextTile, or if nextTile returns false. 658 */ 659 void drawCurrentTile(); 660 661protected: 662 SkTDArray<SkRect> fTileRects; 663 664 virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; 665 virtual SkString getConfigNameInternal() SK_OVERRIDE; 666 667private: 668 int fTileWidth; 669 int fTileHeight; 670 double fTileWidthPercentage; 671 double fTileHeightPercentage; 672 int fTileMinPowerOf2Width; 673 674 // These variables are only used for timing individual tiles. 675 // Next tile to draw in fTileRects. 676 int fCurrentTileOffset; 677 // Number of tiles in the x direction. 678 int fTilesX; 679 // Number of tiles in the y direction. 680 int fTilesY; 681 682 void setupTiles(); 683 void setupPowerOf2Tiles(); 684 685 typedef PictureRenderer INHERITED; 686}; 687 688/** 689 * This class does not do any rendering, but its render function executes turning an SkPictureRecord 690 * into an SkPicturePlayback, which we want to time. 691 */ 692class PlaybackCreationRenderer : public PictureRenderer { 693public: 694#if SK_SUPPORT_GPU 695 PlaybackCreationRenderer(const GrContext::Options &opts) : INHERITED(opts) { } 696#endif 697 698 virtual void setup() SK_OVERRIDE; 699 700 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; 701 702 virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } 703 704 virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); } 705 706private: 707 SkAutoTDelete<SkPictureRecorder> fRecorder; 708 709 virtual SkString getConfigNameInternal() SK_OVERRIDE; 710 711 typedef PictureRenderer INHERITED; 712}; 713 714#if SK_SUPPORT_GPU 715extern PictureRenderer* CreateGatherPixelRefsRenderer(const GrContext::Options& opts); 716#else 717extern PictureRenderer* CreateGatherPixelRefsRenderer(); 718#endif 719 720} 721 722#endif // PictureRenderer_DEFINED 723