1 /* 2 * Copyright 2014 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 "SKPBench.h" 9 #include "SkCommandLineFlags.h" 10 #include "SkMultiPictureDraw.h" 11 #include "SkSurface.h" 12 13 #if SK_SUPPORT_GPU 14 #include "GrContext.h" 15 #include "GrContextPriv.h" 16 #endif 17 18 // These CPU tile sizes are not good per se, but they are similar to what Chrome uses. 19 DEFINE_int32(CPUbenchTileW, 256, "Tile width used for CPU SKP playback."); 20 DEFINE_int32(CPUbenchTileH, 256, "Tile height used for CPU SKP playback."); 21 22 DEFINE_int32(GPUbenchTileW, 1600, "Tile width used for GPU SKP playback."); 23 DEFINE_int32(GPUbenchTileH, 512, "Tile height used for GPU SKP playback."); 24 25 SKPBench::SKPBench(const char* name, const SkPicture* pic, const SkIRect& clip, SkScalar scale, 26 bool useMultiPictureDraw, bool doLooping) 27 : fPic(SkRef(pic)) 28 , fClip(clip) 29 , fScale(scale) 30 , fName(name) 31 , fUseMultiPictureDraw(useMultiPictureDraw) 32 , fDoLooping(doLooping) { 33 fUniqueName.printf("%s_%.2g", name, scale); // Scale makes this unqiue for perf.skia.org traces. 34 if (useMultiPictureDraw) { 35 fUniqueName.append("_mpd"); 36 } 37 } 38 39 SKPBench::~SKPBench() { 40 for (int i = 0; i < fSurfaces.count(); ++i) { 41 fSurfaces[i]->unref(); 42 } 43 } 44 45 const char* SKPBench::onGetName() { 46 return fName.c_str(); 47 } 48 49 const char* SKPBench::onGetUniqueName() { 50 return fUniqueName.c_str(); 51 } 52 53 void SKPBench::onPerCanvasPreDraw(SkCanvas* canvas) { 54 SkIRect bounds = canvas->getDeviceClipBounds(); 55 SkAssertResult(!bounds.isEmpty()); 56 57 const bool gpu = canvas->getGrContext() != nullptr; 58 int tileW = gpu ? FLAGS_GPUbenchTileW : FLAGS_CPUbenchTileW, 59 tileH = gpu ? FLAGS_GPUbenchTileH : FLAGS_CPUbenchTileH; 60 61 tileW = SkTMin(tileW, bounds.width()); 62 tileH = SkTMin(tileH, bounds.height()); 63 64 int xTiles = SkScalarCeilToInt(bounds.width() / SkIntToScalar(tileW)); 65 int yTiles = SkScalarCeilToInt(bounds.height() / SkIntToScalar(tileH)); 66 67 fSurfaces.setReserve(xTiles * yTiles); 68 fTileRects.setReserve(xTiles * yTiles); 69 70 SkImageInfo ii = canvas->imageInfo().makeWH(tileW, tileH); 71 72 for (int y = bounds.fTop; y < bounds.fBottom; y += tileH) { 73 for (int x = bounds.fLeft; x < bounds.fRight; x += tileW) { 74 const SkIRect tileRect = SkIRect::MakeXYWH(x, y, tileW, tileH); 75 *fTileRects.append() = tileRect; 76 *fSurfaces.push() = canvas->makeSurface(ii).release(); 77 78 // Never want the contents of a tile to include stuff the parent 79 // canvas clips out 80 SkRect clip = SkRect::Make(bounds); 81 clip.offset(-SkIntToScalar(tileRect.fLeft), -SkIntToScalar(tileRect.fTop)); 82 fSurfaces.top()->getCanvas()->clipRect(clip); 83 84 fSurfaces.top()->getCanvas()->setMatrix(canvas->getTotalMatrix()); 85 fSurfaces.top()->getCanvas()->scale(fScale, fScale); 86 } 87 } 88 } 89 90 void SKPBench::onPerCanvasPostDraw(SkCanvas* canvas) { 91 // Draw the last set of tiles into the master canvas in case we're 92 // saving the images 93 for (int i = 0; i < fTileRects.count(); ++i) { 94 sk_sp<SkImage> image(fSurfaces[i]->makeImageSnapshot()); 95 canvas->drawImage(image, 96 SkIntToScalar(fTileRects[i].fLeft), SkIntToScalar(fTileRects[i].fTop)); 97 SkSafeSetNull(fSurfaces[i]); 98 } 99 100 fSurfaces.rewind(); 101 fTileRects.rewind(); 102 } 103 104 bool SKPBench::isSuitableFor(Backend backend) { 105 return backend != kNonRendering_Backend; 106 } 107 108 SkIPoint SKPBench::onGetSize() { 109 return SkIPoint::Make(fClip.width(), fClip.height()); 110 } 111 112 void SKPBench::onDraw(int loops, SkCanvas* canvas) { 113 SkASSERT(fDoLooping || 1 == loops); 114 while (1) { 115 if (fUseMultiPictureDraw) { 116 this->drawMPDPicture(); 117 } else { 118 this->drawPicture(); 119 } 120 if (0 == --loops) { 121 break; 122 } 123 #if SK_SUPPORT_GPU 124 // Ensure the GrContext doesn't combine ops across draw loops. 125 if (GrContext* context = canvas->getGrContext()) { 126 context->flush(); 127 } 128 #endif 129 } 130 } 131 132 void SKPBench::drawMPDPicture() { 133 SkMultiPictureDraw mpd; 134 135 for (int j = 0; j < fTileRects.count(); ++j) { 136 SkMatrix trans; 137 trans.setTranslate(-fTileRects[j].fLeft/fScale, 138 -fTileRects[j].fTop/fScale); 139 mpd.add(fSurfaces[j]->getCanvas(), fPic.get(), &trans); 140 } 141 142 mpd.draw(); 143 144 for (int j = 0; j < fTileRects.count(); ++j) { 145 fSurfaces[j]->getCanvas()->flush(); 146 } 147 } 148 149 void SKPBench::drawPicture() { 150 for (int j = 0; j < fTileRects.count(); ++j) { 151 const SkMatrix trans = SkMatrix::MakeTrans(-fTileRects[j].fLeft / fScale, 152 -fTileRects[j].fTop / fScale); 153 fSurfaces[j]->getCanvas()->drawPicture(fPic.get(), &trans, nullptr); 154 } 155 156 for (int j = 0; j < fTileRects.count(); ++j) { 157 fSurfaces[j]->getCanvas()->flush(); 158 } 159 } 160 161 #if SK_SUPPORT_GPU 162 #include "GrGpu.h" 163 static void draw_pic_for_stats(SkCanvas* canvas, GrContext* context, const SkPicture* picture, 164 SkTArray<SkString>* keys, SkTArray<double>* values, 165 const char* tag) { 166 context->resetGpuStats(); 167 canvas->drawPicture(picture); 168 canvas->flush(); 169 170 int offset = keys->count(); 171 context->dumpGpuStatsKeyValuePairs(keys, values); 172 context->dumpCacheStatsKeyValuePairs(keys, values); 173 174 // append tag, but only to new tags 175 for (int i = offset; i < keys->count(); i++, offset++) { 176 (*keys)[i].appendf("_%s", tag); 177 } 178 } 179 #endif 180 181 void SKPBench::getGpuStats(SkCanvas* canvas, SkTArray<SkString>* keys, SkTArray<double>* values) { 182 #if SK_SUPPORT_GPU 183 // we do a special single draw and then dump the key / value pairs 184 GrContext* context = canvas->getGrContext(); 185 if (!context) { 186 return; 187 } 188 189 // TODO refactor this out if we want to test other subclasses of skpbench 190 context->flush(); 191 context->freeGpuResources(); 192 context->resetContext(); 193 context->contextPriv().getGpu()->resetShaderCacheForTesting(); 194 draw_pic_for_stats(canvas, context, fPic.get(), keys, values, "first_frame"); 195 196 // draw second frame 197 draw_pic_for_stats(canvas, context, fPic.get(), keys, values, "second_frame"); 198 199 #endif 200 } 201