blob: d407b0eea81aa6164d400e9ce0e79272c27e4103 [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"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000025#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000026#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000027#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000028
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000029namespace sk_tools {
30
31enum {
32 kDefaultTileWidth = 256,
33 kDefaultTileHeight = 256
34};
35
keyar@chromium.org9d696c02012-08-07 17:11:33 +000036void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000037 SkASSERT(NULL == fPicture);
38 SkASSERT(NULL == fCanvas.get());
39 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000040 return;
41 }
42
43 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000044 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000045 return;
46 }
47
48 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000049 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000050 fCanvas.reset(this->setupCanvas());
51}
52
53SkCanvas* PictureRenderer::setupCanvas() {
54 return this->setupCanvas(fPicture->width(), fPicture->height());
55}
56
57SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000058 switch(fDeviceType) {
59 case kBitmap_DeviceType: {
60 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000061 sk_tools::setup_bitmap(&bitmap, width, height);
62 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000063 break;
64 }
65#if SK_SUPPORT_GPU
66 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000067 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000068 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000069 width, height)));
70 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000071 break;
72 }
73#endif
74 default:
75 SkASSERT(0);
76 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000077
78 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000079}
80
81void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000082 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +000083 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +000084 fPicture = NULL;
85 fCanvas.reset(NULL);
86}
87
junov@chromium.org9313ca42012-11-02 18:11:49 +000088/** Converts fPicture to a picture that uses a BBoxHierarchy.
89 * PictureRenderer subclasses that are used to test picture playback
90 * should call this method during init.
91 */
92void PictureRenderer::buildBBoxHierarchy() {
93 SkASSERT(NULL != fPicture);
94 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
95 SkPicture* newPicture = this->createPicture();
96 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
97 this->recordFlags());
98 fPicture->draw(recorder);
99 newPicture->endRecording();
100 fPicture->unref();
101 fPicture = newPicture;
102 }
103}
104
keyar@chromium.org77a55222012-08-20 15:03:47 +0000105void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000106#if SK_SUPPORT_GPU
107 if (this->isUsingGpuDevice()) {
108 SkGLContext* glContext = fGrContextFactory.getGLContext(
109 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000110
111 SkASSERT(glContext != NULL);
112 if (NULL == glContext) {
113 return;
114 }
115
scroggo@google.com9a412522012-09-07 15:21:18 +0000116 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000117 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000118 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000119#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000120}
121
junov@chromium.org9313ca42012-11-02 18:11:49 +0000122uint32_t PictureRenderer::recordFlags() {
123 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
124 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
125}
126
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000127/**
128 * Write the canvas to the specified path.
129 * @param canvas Must be non-null. Canvas to be written to a file.
130 * @param path Path for the file to be written. Should have no extension; write() will append
131 * an appropriate one. Passed in by value so it can be modified.
132 * @return bool True if the Canvas is written to a file.
133 */
134static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000135 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000136 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000137 return false;
138 }
139
140 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000141 SkISize size = canvas->getDeviceSize();
142 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000143
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000144 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000145 sk_tools::force_all_opaque(bitmap);
146
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000147 // Since path is passed in by value, it is okay to modify it.
148 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000149 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
150}
151
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000152/**
153 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
154 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
155 */
156static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
157 if (NULL == path) {
158 return true;
159 }
160 SkString pathWithNumber(*path);
161 pathWithNumber.appendf("%i", number);
162 return write(canvas, pathWithNumber);
163}
164
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000165///////////////////////////////////////////////////////////////////////////////////////////////
166
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000167bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000168 SkAutoTUnref<SkPicture> replayer(this->createPicture());
169 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
170 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000171 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000172 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000173 // Since this class does not actually render, return false.
174 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000175}
176
scroggo@google.com0a049b82012-11-02 22:01:26 +0000177SkString RecordPictureRenderer::getConfigNameInternal() {
178 return SkString("record");
179}
180
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000181///////////////////////////////////////////////////////////////////////////////////////////////
182
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000183bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000184 SkASSERT(fCanvas.get() != NULL);
185 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000186 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000187 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000188 }
189
190 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000191 SkGPipeWriter writer;
192 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000193 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000194 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000195 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000196 if (NULL != path) {
197 return write(fCanvas, *path);
198 }
199 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000200}
201
scroggo@google.com0a049b82012-11-02 22:01:26 +0000202SkString PipePictureRenderer::getConfigNameInternal() {
203 return SkString("pipe");
204}
205
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000206///////////////////////////////////////////////////////////////////////////////////////////////
207
junov@chromium.org9313ca42012-11-02 18:11:49 +0000208void SimplePictureRenderer::init(SkPicture* picture) {
209 INHERITED::init(picture);
210 this->buildBBoxHierarchy();
211}
212
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000213bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000214 SkASSERT(fCanvas.get() != NULL);
215 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000216 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000217 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000218 }
219
220 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000221 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000222 if (NULL != path) {
223 return write(fCanvas, *path);
224 }
225 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000226}
227
scroggo@google.com0a049b82012-11-02 22:01:26 +0000228SkString SimplePictureRenderer::getConfigNameInternal() {
229 return SkString("simple");
230}
231
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000232///////////////////////////////////////////////////////////////////////////////////////////////
233
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000234TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000235 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000236 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000237 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000238 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000239 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000240
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000241void TiledPictureRenderer::init(SkPicture* pict) {
242 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000243 SkASSERT(0 == fTileRects.count());
244 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000245 return;
246 }
247
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000248 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
249 // used by bench_pictures.
250 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000251 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000252 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000253
254 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000255 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000256 }
257 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000258 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000259 }
260
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000261 if (fTileMinPowerOf2Width > 0) {
262 this->setupPowerOf2Tiles();
263 } else {
264 this->setupTiles();
265 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000266}
267
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000268void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000269 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000270 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000271}
272
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000273void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000274 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
275 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
276 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
277 SkIntToScalar(tile_y_start),
278 SkIntToScalar(fTileWidth),
279 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000280 }
281 }
282}
283
284// The goal of the powers of two tiles is to minimize the amount of wasted tile
285// space in the width-wise direction and then minimize the number of tiles. The
286// constraints are that every tile must have a pixel width that is a power of
287// two and also be of some minimal width (that is also a power of two).
288//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000289// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000290// multiple of the minimal width. The binary representation of this rounded
291// value gives us the tiles we need: a bit of value one means we need a tile of
292// that size.
293void TiledPictureRenderer::setupPowerOf2Tiles() {
294 int rounded_value = fPicture->width();
295 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
296 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
297 + fTileMinPowerOf2Width;
298 }
299
robertphillips@google.com94acc702012-09-06 18:43:21 +0000300 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000301 int largest_possible_tile_size = 1 << num_bits;
302
303 // The tile height is constant for a particular picture.
304 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
305 int tile_x_start = 0;
306 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000307 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
308 // to draw each tile.
309 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000310
311 while (current_width >= fTileMinPowerOf2Width) {
312 // It is very important this is a bitwise AND.
313 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000314 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
315 SkIntToScalar(tile_y_start),
316 SkIntToScalar(current_width),
317 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000318 tile_x_start += current_width;
319 }
320
321 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000322 }
323 }
324}
325
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000326/**
327 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
328 * canvas represents the rectangle's portion of the overall picture.
329 * Saves and restores so that the initial clip and matrix return to their state before this function
330 * is called.
331 */
332template<class T>
333static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
334 int saveCount = canvas->save();
335 // Translate so that we draw the correct portion of the picture
336 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
337 playback->draw(canvas);
338 canvas->restoreToCount(saveCount);
339 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000340}
341
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000342///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000343
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000344bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000345 SkASSERT(fPicture != NULL);
346 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000347 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000348 }
349
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000350 // Reuse one canvas for all tiles.
351 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
352 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000353
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000354 bool success = true;
355 for (int i = 0; i < fTileRects.count(); ++i) {
356 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
357 if (NULL != path) {
358 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000359 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000360 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000361 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000362}
363
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000364SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
365 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
366 SkASSERT(fPicture != NULL);
367 // Clip the tile to an area that is completely in what the SkPicture says is the
368 // drawn-to area. This is mostly important for tiles on the right and bottom edges
369 // as they may go over this area and the picture may have some commands that
370 // draw outside of this area and so should not actually be written.
371 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
372 SkIntToScalar(fPicture->height()));
373 canvas->clipRect(clip);
374 return canvas;
375}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000376
377SkString TiledPictureRenderer::getConfigNameInternal() {
378 SkString name;
379 if (fTileMinPowerOf2Width > 0) {
380 name.append("pow2tile_");
381 name.appendf("%i", fTileMinPowerOf2Width);
382 } else {
383 name.append("tile_");
384 if (fTileWidthPercentage > 0) {
385 name.appendf("%.f%%", fTileWidthPercentage);
386 } else {
387 name.appendf("%i", fTileWidth);
388 }
389 }
390 name.append("x");
391 if (fTileHeightPercentage > 0) {
392 name.appendf("%.f%%", fTileHeightPercentage);
393 } else {
394 name.appendf("%i", fTileHeight);
395 }
396 return name;
397}
398
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000399///////////////////////////////////////////////////////////////////////////////////////////////
400
401// Holds all of the information needed to draw a set of tiles.
402class CloneData : public SkRunnable {
403
404public:
405 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
406 SkRunnable* done)
407 : fClone(clone)
408 , fCanvas(canvas)
409 , fPath(NULL)
410 , fRects(rects)
411 , fStart(start)
412 , fEnd(end)
413 , fSuccess(NULL)
414 , fDone(done) {
415 SkASSERT(fDone != NULL);
416 }
417
418 virtual void run() SK_OVERRIDE {
419 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
420 for (int i = fStart; i < fEnd; i++) {
421 DrawTileToCanvas(fCanvas, fRects[i], fClone);
422 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
423 && fSuccess != NULL) {
424 *fSuccess = false;
425 // If one tile fails to write to a file, do not continue drawing the rest.
426 break;
427 }
428 }
429 fDone->run();
430 }
431
432 void setPathAndSuccess(const SkString* path, bool* success) {
433 fPath = path;
434 fSuccess = success;
435 }
436
437private:
438 // All pointers unowned.
439 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
440 // is threadsafe.
441 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
442 const SkString* fPath; // If non-null, path to write the result to as a PNG.
443 SkTDArray<SkRect>& fRects; // All tiles of the picture.
444 const int fStart; // Range of tiles drawn by this thread.
445 const int fEnd;
446 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
447 // and only set to false upon failure to write to a PNG.
448 SkRunnable* fDone;
449};
450
451MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
452: fNumThreads(threadCount)
453, fThreadPool(threadCount)
454, fCountdown(threadCount) {
455 // Only need to create fNumThreads - 1 clones, since one thread will use the base
456 // picture.
457 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
458 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
459}
460
461void MultiCorePictureRenderer::init(SkPicture *pict) {
462 // Set fPicture and the tiles.
463 this->INHERITED::init(pict);
464 for (int i = 0; i < fNumThreads; ++i) {
465 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
466 }
467 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
468 fPicture->clone(fPictureClones, fNumThreads - 1);
469 // Populate each thread with the appropriate data.
470 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
471 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
472
473 for (int i = 0; i < fNumThreads; i++) {
474 SkPicture* pic;
475 if (i == fNumThreads-1) {
476 // The last set will use the original SkPicture.
477 pic = fPicture;
478 } else {
479 pic = &fPictureClones[i];
480 }
481 const int start = i * chunkSize;
482 const int end = SkMin32(start + chunkSize, fTileRects.count());
483 fCloneData[i] = SkNEW_ARGS(CloneData,
484 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
485 }
486}
487
488bool MultiCorePictureRenderer::render(const SkString *path) {
489 bool success = true;
490 if (path != NULL) {
491 for (int i = 0; i < fNumThreads-1; i++) {
492 fCloneData[i]->setPathAndSuccess(path, &success);
493 }
494 }
495
496 fCountdown.reset(fNumThreads);
497 for (int i = 0; i < fNumThreads; i++) {
498 fThreadPool.add(fCloneData[i]);
499 }
500 fCountdown.wait();
501
502 return success;
503}
504
505void MultiCorePictureRenderer::end() {
506 for (int i = 0; i < fNumThreads - 1; i++) {
507 SkDELETE(fCloneData[i]);
508 fCloneData[i] = NULL;
509 }
510
511 fCanvasPool.unrefAll();
512
513 this->INHERITED::end();
514}
515
516MultiCorePictureRenderer::~MultiCorePictureRenderer() {
517 // Each individual CloneData was deleted in end.
518 SkDELETE_ARRAY(fCloneData);
519 SkDELETE_ARRAY(fPictureClones);
520}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000521
scroggo@google.com0a049b82012-11-02 22:01:26 +0000522SkString MultiCorePictureRenderer::getConfigNameInternal() {
523 SkString name = this->INHERITED::getConfigNameInternal();
524 name.appendf("_multi_%i_threads", fNumThreads);
525 return name;
526}
527
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000528///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000529
530void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000531 fReplayer.reset(this->createPicture());
532 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
533 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000534 fPicture->draw(recorder);
535}
536
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000537bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000538 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000539 // Since this class does not actually render, return false.
540 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000541}
542
scroggo@google.com0a049b82012-11-02 22:01:26 +0000543SkString PlaybackCreationRenderer::getConfigNameInternal() {
544 return SkString("playback_creation");
545}
546
junov@chromium.org9313ca42012-11-02 18:11:49 +0000547///////////////////////////////////////////////////////////////////////////////////////////////
548// SkPicture variants for each BBoxHierarchy type
549
550class RTreePicture : public SkPicture {
551public:
552 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
553 static const int kRTreeMinChildren = 6;
554 static const int kRTreeMaxChildren = 11;
555 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
556 SkIntToScalar(fHeight));
557 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
558 aspectRatio);
559 }
560};
561
562SkPicture* PictureRenderer::createPicture() {
563 switch (fBBoxHierarchyType) {
564 case kNone_BBoxHierarchyType:
565 return SkNEW(SkPicture);
566 case kRTree_BBoxHierarchyType:
567 return SkNEW(RTreePicture);
568 }
569 SkASSERT(0); // invalid bbhType
570 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000571}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000572
573} // namespace sk_tools