blob: cb68f396a352683c825a36db36e56af05ff89570 [file] [log] [blame]
keyar@chromium.orgb3fb7c12012-08-20 21:02:49 +00001/*
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
keyar@chromium.org451bb9f2012-07-26 17:27:57 +00008#include "PictureRenderer.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +00009#include "picture_utils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000010#include "SamplePipeControllers.h"
11#include "SkCanvas.h"
12#include "SkDevice.h"
13#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000014#if SK_SUPPORT_GPU
15#include "SkGpuDevice.h"
16#endif
17#include "SkGraphics.h"
18#include "SkImageEncoder.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000019#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000020#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000021#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000022#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000023#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000024#include "SkTemplates.h"
junov@chromium.org7b537062012-11-06 18:58:43 +000025#include "SkTileGrid.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000026#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000027#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000028#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000029
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000030namespace sk_tools {
31
32enum {
33 kDefaultTileWidth = 256,
34 kDefaultTileHeight = 256
35};
36
keyar@chromium.org9d696c02012-08-07 17:11:33 +000037void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000038 SkASSERT(NULL == fPicture);
39 SkASSERT(NULL == fCanvas.get());
40 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000041 return;
42 }
43
44 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000045 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000046 return;
47 }
48
49 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000050 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000051 fCanvas.reset(this->setupCanvas());
52}
53
54SkCanvas* PictureRenderer::setupCanvas() {
55 return this->setupCanvas(fPicture->width(), fPicture->height());
56}
57
58SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000059 switch(fDeviceType) {
60 case kBitmap_DeviceType: {
61 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000062 sk_tools::setup_bitmap(&bitmap, width, height);
63 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000064 break;
65 }
66#if SK_SUPPORT_GPU
67 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000068 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000069 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000070 width, height)));
71 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000072 break;
73 }
74#endif
75 default:
76 SkASSERT(0);
77 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000078
79 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000080}
81
82void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000083 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +000084 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +000085 fPicture = NULL;
86 fCanvas.reset(NULL);
87}
88
junov@chromium.org9313ca42012-11-02 18:11:49 +000089/** Converts fPicture to a picture that uses a BBoxHierarchy.
90 * PictureRenderer subclasses that are used to test picture playback
91 * should call this method during init.
92 */
93void PictureRenderer::buildBBoxHierarchy() {
94 SkASSERT(NULL != fPicture);
95 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
96 SkPicture* newPicture = this->createPicture();
97 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
98 this->recordFlags());
99 fPicture->draw(recorder);
100 newPicture->endRecording();
101 fPicture->unref();
102 fPicture = newPicture;
103 }
104}
105
keyar@chromium.org77a55222012-08-20 15:03:47 +0000106void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000107#if SK_SUPPORT_GPU
108 if (this->isUsingGpuDevice()) {
109 SkGLContext* glContext = fGrContextFactory.getGLContext(
110 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000111
112 SkASSERT(glContext != NULL);
113 if (NULL == glContext) {
114 return;
115 }
116
scroggo@google.com9a412522012-09-07 15:21:18 +0000117 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000118 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000119 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000120#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000121}
122
junov@chromium.org9313ca42012-11-02 18:11:49 +0000123uint32_t PictureRenderer::recordFlags() {
124 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
125 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
126}
127
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000128/**
129 * Write the canvas to the specified path.
130 * @param canvas Must be non-null. Canvas to be written to a file.
131 * @param path Path for the file to be written. Should have no extension; write() will append
132 * an appropriate one. Passed in by value so it can be modified.
133 * @return bool True if the Canvas is written to a file.
134 */
135static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000136 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000137 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000138 return false;
139 }
140
141 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000142 SkISize size = canvas->getDeviceSize();
143 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000144
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000145 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000146 sk_tools::force_all_opaque(bitmap);
147
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000148 // Since path is passed in by value, it is okay to modify it.
149 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000150 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
151}
152
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000153/**
154 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
155 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
156 */
157static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
158 if (NULL == path) {
159 return true;
160 }
161 SkString pathWithNumber(*path);
162 pathWithNumber.appendf("%i", number);
163 return write(canvas, pathWithNumber);
164}
165
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000166///////////////////////////////////////////////////////////////////////////////////////////////
167
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000168SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
169 // defer the canvas setup until the render step
170 return NULL;
171}
172
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000173bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000174 SkAutoTUnref<SkPicture> replayer(this->createPicture());
175 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
176 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000177 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000178 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000179 // Since this class does not actually render, return false.
180 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000181}
182
scroggo@google.com0a049b82012-11-02 22:01:26 +0000183SkString RecordPictureRenderer::getConfigNameInternal() {
184 return SkString("record");
185}
186
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000187///////////////////////////////////////////////////////////////////////////////////////////////
188
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000189bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000190 SkASSERT(fCanvas.get() != NULL);
191 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000192 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000193 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000194 }
195
196 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000197 SkGPipeWriter writer;
198 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000199 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000200 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000201 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000202 if (NULL != path) {
203 return write(fCanvas, *path);
204 }
205 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000206}
207
scroggo@google.com0a049b82012-11-02 22:01:26 +0000208SkString PipePictureRenderer::getConfigNameInternal() {
209 return SkString("pipe");
210}
211
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000212///////////////////////////////////////////////////////////////////////////////////////////////
213
junov@chromium.org9313ca42012-11-02 18:11:49 +0000214void SimplePictureRenderer::init(SkPicture* picture) {
215 INHERITED::init(picture);
216 this->buildBBoxHierarchy();
217}
218
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000219bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000220 SkASSERT(fCanvas.get() != NULL);
221 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000222 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000223 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000224 }
225
226 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000227 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000228 if (NULL != path) {
229 return write(fCanvas, *path);
230 }
231 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000232}
233
scroggo@google.com0a049b82012-11-02 22:01:26 +0000234SkString SimplePictureRenderer::getConfigNameInternal() {
235 return SkString("simple");
236}
237
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000238///////////////////////////////////////////////////////////////////////////////////////////////
239
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000240TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000241 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000242 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000243 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000244 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000245 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000246
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000247void TiledPictureRenderer::init(SkPicture* pict) {
248 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000249 SkASSERT(0 == fTileRects.count());
250 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000251 return;
252 }
253
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000254 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
255 // used by bench_pictures.
256 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000257 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000258 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000259
260 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000261 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000262 }
263 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000264 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000265 }
266
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000267 if (fTileMinPowerOf2Width > 0) {
268 this->setupPowerOf2Tiles();
269 } else {
270 this->setupTiles();
271 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000272}
273
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000274void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000275 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000276 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000277}
278
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000279void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000280 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
281 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
282 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
283 SkIntToScalar(tile_y_start),
284 SkIntToScalar(fTileWidth),
285 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000286 }
287 }
288}
289
290// The goal of the powers of two tiles is to minimize the amount of wasted tile
291// space in the width-wise direction and then minimize the number of tiles. The
292// constraints are that every tile must have a pixel width that is a power of
293// two and also be of some minimal width (that is also a power of two).
294//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000295// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000296// multiple of the minimal width. The binary representation of this rounded
297// value gives us the tiles we need: a bit of value one means we need a tile of
298// that size.
299void TiledPictureRenderer::setupPowerOf2Tiles() {
300 int rounded_value = fPicture->width();
301 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
302 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
303 + fTileMinPowerOf2Width;
304 }
305
robertphillips@google.com94acc702012-09-06 18:43:21 +0000306 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000307 int largest_possible_tile_size = 1 << num_bits;
308
309 // The tile height is constant for a particular picture.
310 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
311 int tile_x_start = 0;
312 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000313 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
314 // to draw each tile.
315 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000316
317 while (current_width >= fTileMinPowerOf2Width) {
318 // It is very important this is a bitwise AND.
319 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000320 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
321 SkIntToScalar(tile_y_start),
322 SkIntToScalar(current_width),
323 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000324 tile_x_start += current_width;
325 }
326
327 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000328 }
329 }
330}
331
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000332/**
333 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
334 * canvas represents the rectangle's portion of the overall picture.
335 * Saves and restores so that the initial clip and matrix return to their state before this function
336 * is called.
337 */
338template<class T>
339static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
340 int saveCount = canvas->save();
341 // Translate so that we draw the correct portion of the picture
342 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
343 playback->draw(canvas);
344 canvas->restoreToCount(saveCount);
345 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000346}
347
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000348///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000349
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000350bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000351 SkASSERT(fPicture != NULL);
352 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000353 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000354 }
355
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000356 // Reuse one canvas for all tiles.
357 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
358 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000359
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000360 bool success = true;
361 for (int i = 0; i < fTileRects.count(); ++i) {
362 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
363 if (NULL != path) {
364 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000365 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000366 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000367 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000368}
369
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000370SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
371 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
372 SkASSERT(fPicture != NULL);
373 // Clip the tile to an area that is completely in what the SkPicture says is the
374 // drawn-to area. This is mostly important for tiles on the right and bottom edges
375 // as they may go over this area and the picture may have some commands that
376 // draw outside of this area and so should not actually be written.
377 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
378 SkIntToScalar(fPicture->height()));
379 canvas->clipRect(clip);
380 return canvas;
381}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000382
383SkString TiledPictureRenderer::getConfigNameInternal() {
384 SkString name;
385 if (fTileMinPowerOf2Width > 0) {
386 name.append("pow2tile_");
387 name.appendf("%i", fTileMinPowerOf2Width);
388 } else {
389 name.append("tile_");
390 if (fTileWidthPercentage > 0) {
391 name.appendf("%.f%%", fTileWidthPercentage);
392 } else {
393 name.appendf("%i", fTileWidth);
394 }
395 }
396 name.append("x");
397 if (fTileHeightPercentage > 0) {
398 name.appendf("%.f%%", fTileHeightPercentage);
399 } else {
400 name.appendf("%i", fTileHeight);
401 }
402 return name;
403}
404
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000405///////////////////////////////////////////////////////////////////////////////////////////////
406
407// Holds all of the information needed to draw a set of tiles.
408class CloneData : public SkRunnable {
409
410public:
411 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
412 SkRunnable* done)
413 : fClone(clone)
414 , fCanvas(canvas)
415 , fPath(NULL)
416 , fRects(rects)
417 , fStart(start)
418 , fEnd(end)
419 , fSuccess(NULL)
420 , fDone(done) {
421 SkASSERT(fDone != NULL);
422 }
423
424 virtual void run() SK_OVERRIDE {
425 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
426 for (int i = fStart; i < fEnd; i++) {
427 DrawTileToCanvas(fCanvas, fRects[i], fClone);
428 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
429 && fSuccess != NULL) {
430 *fSuccess = false;
431 // If one tile fails to write to a file, do not continue drawing the rest.
432 break;
433 }
434 }
435 fDone->run();
436 }
437
438 void setPathAndSuccess(const SkString* path, bool* success) {
439 fPath = path;
440 fSuccess = success;
441 }
442
443private:
444 // All pointers unowned.
445 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
446 // is threadsafe.
447 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
448 const SkString* fPath; // If non-null, path to write the result to as a PNG.
449 SkTDArray<SkRect>& fRects; // All tiles of the picture.
450 const int fStart; // Range of tiles drawn by this thread.
451 const int fEnd;
452 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
453 // and only set to false upon failure to write to a PNG.
454 SkRunnable* fDone;
455};
456
457MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
458: fNumThreads(threadCount)
459, fThreadPool(threadCount)
460, fCountdown(threadCount) {
461 // Only need to create fNumThreads - 1 clones, since one thread will use the base
462 // picture.
463 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
464 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
465}
466
467void MultiCorePictureRenderer::init(SkPicture *pict) {
468 // Set fPicture and the tiles.
469 this->INHERITED::init(pict);
470 for (int i = 0; i < fNumThreads; ++i) {
471 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
472 }
473 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
474 fPicture->clone(fPictureClones, fNumThreads - 1);
475 // Populate each thread with the appropriate data.
476 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
477 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
478
479 for (int i = 0; i < fNumThreads; i++) {
480 SkPicture* pic;
481 if (i == fNumThreads-1) {
482 // The last set will use the original SkPicture.
483 pic = fPicture;
484 } else {
485 pic = &fPictureClones[i];
486 }
487 const int start = i * chunkSize;
488 const int end = SkMin32(start + chunkSize, fTileRects.count());
489 fCloneData[i] = SkNEW_ARGS(CloneData,
490 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
491 }
492}
493
494bool MultiCorePictureRenderer::render(const SkString *path) {
495 bool success = true;
496 if (path != NULL) {
497 for (int i = 0; i < fNumThreads-1; i++) {
498 fCloneData[i]->setPathAndSuccess(path, &success);
499 }
500 }
501
502 fCountdown.reset(fNumThreads);
503 for (int i = 0; i < fNumThreads; i++) {
504 fThreadPool.add(fCloneData[i]);
505 }
506 fCountdown.wait();
507
508 return success;
509}
510
511void MultiCorePictureRenderer::end() {
512 for (int i = 0; i < fNumThreads - 1; i++) {
513 SkDELETE(fCloneData[i]);
514 fCloneData[i] = NULL;
515 }
516
517 fCanvasPool.unrefAll();
518
519 this->INHERITED::end();
520}
521
522MultiCorePictureRenderer::~MultiCorePictureRenderer() {
523 // Each individual CloneData was deleted in end.
524 SkDELETE_ARRAY(fCloneData);
525 SkDELETE_ARRAY(fPictureClones);
526}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000527
scroggo@google.com0a049b82012-11-02 22:01:26 +0000528SkString MultiCorePictureRenderer::getConfigNameInternal() {
529 SkString name = this->INHERITED::getConfigNameInternal();
530 name.appendf("_multi_%i_threads", fNumThreads);
531 return name;
532}
533
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000534///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000535
536void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000537 fReplayer.reset(this->createPicture());
538 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
539 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000540 fPicture->draw(recorder);
541}
542
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000543bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000544 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000545 // Since this class does not actually render, return false.
546 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000547}
548
scroggo@google.com0a049b82012-11-02 22:01:26 +0000549SkString PlaybackCreationRenderer::getConfigNameInternal() {
550 return SkString("playback_creation");
551}
552
junov@chromium.org9313ca42012-11-02 18:11:49 +0000553///////////////////////////////////////////////////////////////////////////////////////////////
554// SkPicture variants for each BBoxHierarchy type
555
556class RTreePicture : public SkPicture {
557public:
558 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
559 static const int kRTreeMinChildren = 6;
560 static const int kRTreeMaxChildren = 11;
561 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
562 SkIntToScalar(fHeight));
563 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
564 aspectRatio);
565 }
566};
567
junov@chromium.org7b537062012-11-06 18:58:43 +0000568class TileGridPicture : public SkPicture {
569public:
570 TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
571 fTileWidth = tileWidth;
572 fTileHeight = tileHeight;
573 fXTileCount = xTileCount;
574 fYTileCount = yTileCount;
575 }
576
577 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
578 return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
579 }
580private:
581 int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
582};
583
junov@chromium.org9313ca42012-11-02 18:11:49 +0000584SkPicture* PictureRenderer::createPicture() {
585 switch (fBBoxHierarchyType) {
586 case kNone_BBoxHierarchyType:
587 return SkNEW(SkPicture);
588 case kRTree_BBoxHierarchyType:
589 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000590 case kTileGrid_BBoxHierarchyType:
591 {
592 int xTileCount = fPicture->width() / fGridWidth +
593 ((fPicture->width() % fGridWidth) ? 1 : 0);
594 int yTileCount = fPicture->height() / fGridHeight +
595 ((fPicture->height() % fGridHeight) ? 1 : 0);
596 return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
597 yTileCount));
598 }
junov@chromium.org9313ca42012-11-02 18:11:49 +0000599 }
600 SkASSERT(0); // invalid bbhType
601 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000602}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000603
604} // namespace sk_tools