blob: 23223da5165b623df3d26e1be2cc361dc4972295 [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
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000167SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
168 // defer the canvas setup until the render step
169 return NULL;
170}
171
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000172bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000173 SkAutoTUnref<SkPicture> replayer(this->createPicture());
174 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
175 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000176 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000177 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000178 // Since this class does not actually render, return false.
179 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000180}
181
scroggo@google.com0a049b82012-11-02 22:01:26 +0000182SkString RecordPictureRenderer::getConfigNameInternal() {
183 return SkString("record");
184}
185
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000186///////////////////////////////////////////////////////////////////////////////////////////////
187
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000188bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000189 SkASSERT(fCanvas.get() != NULL);
190 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000191 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000192 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000193 }
194
195 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000196 SkGPipeWriter writer;
197 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000198 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000199 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000200 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000201 if (NULL != path) {
202 return write(fCanvas, *path);
203 }
204 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000205}
206
scroggo@google.com0a049b82012-11-02 22:01:26 +0000207SkString PipePictureRenderer::getConfigNameInternal() {
208 return SkString("pipe");
209}
210
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000211///////////////////////////////////////////////////////////////////////////////////////////////
212
junov@chromium.org9313ca42012-11-02 18:11:49 +0000213void SimplePictureRenderer::init(SkPicture* picture) {
214 INHERITED::init(picture);
215 this->buildBBoxHierarchy();
216}
217
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000218bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000219 SkASSERT(fCanvas.get() != NULL);
220 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000221 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000222 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000223 }
224
225 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000226 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000227 if (NULL != path) {
228 return write(fCanvas, *path);
229 }
230 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000231}
232
scroggo@google.com0a049b82012-11-02 22:01:26 +0000233SkString SimplePictureRenderer::getConfigNameInternal() {
234 return SkString("simple");
235}
236
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000237///////////////////////////////////////////////////////////////////////////////////////////////
238
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000239TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000240 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000241 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000242 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000243 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000244 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000245
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000246void TiledPictureRenderer::init(SkPicture* pict) {
247 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000248 SkASSERT(0 == fTileRects.count());
249 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000250 return;
251 }
252
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000253 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
254 // used by bench_pictures.
255 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000256 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000257 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000258
259 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000260 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000261 }
262 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000263 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000264 }
265
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000266 if (fTileMinPowerOf2Width > 0) {
267 this->setupPowerOf2Tiles();
268 } else {
269 this->setupTiles();
270 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000271}
272
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000273void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000274 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000275 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000276}
277
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000278void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000279 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
280 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
281 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
282 SkIntToScalar(tile_y_start),
283 SkIntToScalar(fTileWidth),
284 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000285 }
286 }
287}
288
289// The goal of the powers of two tiles is to minimize the amount of wasted tile
290// space in the width-wise direction and then minimize the number of tiles. The
291// constraints are that every tile must have a pixel width that is a power of
292// two and also be of some minimal width (that is also a power of two).
293//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000294// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000295// multiple of the minimal width. The binary representation of this rounded
296// value gives us the tiles we need: a bit of value one means we need a tile of
297// that size.
298void TiledPictureRenderer::setupPowerOf2Tiles() {
299 int rounded_value = fPicture->width();
300 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
301 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
302 + fTileMinPowerOf2Width;
303 }
304
robertphillips@google.com94acc702012-09-06 18:43:21 +0000305 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000306 int largest_possible_tile_size = 1 << num_bits;
307
308 // The tile height is constant for a particular picture.
309 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
310 int tile_x_start = 0;
311 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000312 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
313 // to draw each tile.
314 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000315
316 while (current_width >= fTileMinPowerOf2Width) {
317 // It is very important this is a bitwise AND.
318 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000319 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
320 SkIntToScalar(tile_y_start),
321 SkIntToScalar(current_width),
322 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000323 tile_x_start += current_width;
324 }
325
326 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000327 }
328 }
329}
330
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000331/**
332 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
333 * canvas represents the rectangle's portion of the overall picture.
334 * Saves and restores so that the initial clip and matrix return to their state before this function
335 * is called.
336 */
337template<class T>
338static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
339 int saveCount = canvas->save();
340 // Translate so that we draw the correct portion of the picture
341 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
342 playback->draw(canvas);
343 canvas->restoreToCount(saveCount);
344 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000345}
346
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000347///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000348
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000349bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000350 SkASSERT(fPicture != NULL);
351 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000352 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000353 }
354
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000355 // Reuse one canvas for all tiles.
356 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
357 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000358
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000359 bool success = true;
360 for (int i = 0; i < fTileRects.count(); ++i) {
361 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
362 if (NULL != path) {
363 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000364 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000365 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000366 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000367}
368
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000369SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
370 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
371 SkASSERT(fPicture != NULL);
372 // Clip the tile to an area that is completely in what the SkPicture says is the
373 // drawn-to area. This is mostly important for tiles on the right and bottom edges
374 // as they may go over this area and the picture may have some commands that
375 // draw outside of this area and so should not actually be written.
376 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
377 SkIntToScalar(fPicture->height()));
378 canvas->clipRect(clip);
379 return canvas;
380}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000381
382SkString TiledPictureRenderer::getConfigNameInternal() {
383 SkString name;
384 if (fTileMinPowerOf2Width > 0) {
385 name.append("pow2tile_");
386 name.appendf("%i", fTileMinPowerOf2Width);
387 } else {
388 name.append("tile_");
389 if (fTileWidthPercentage > 0) {
390 name.appendf("%.f%%", fTileWidthPercentage);
391 } else {
392 name.appendf("%i", fTileWidth);
393 }
394 }
395 name.append("x");
396 if (fTileHeightPercentage > 0) {
397 name.appendf("%.f%%", fTileHeightPercentage);
398 } else {
399 name.appendf("%i", fTileHeight);
400 }
401 return name;
402}
403
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000404///////////////////////////////////////////////////////////////////////////////////////////////
405
406// Holds all of the information needed to draw a set of tiles.
407class CloneData : public SkRunnable {
408
409public:
410 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
411 SkRunnable* done)
412 : fClone(clone)
413 , fCanvas(canvas)
414 , fPath(NULL)
415 , fRects(rects)
416 , fStart(start)
417 , fEnd(end)
418 , fSuccess(NULL)
419 , fDone(done) {
420 SkASSERT(fDone != NULL);
421 }
422
423 virtual void run() SK_OVERRIDE {
424 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
425 for (int i = fStart; i < fEnd; i++) {
426 DrawTileToCanvas(fCanvas, fRects[i], fClone);
427 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
428 && fSuccess != NULL) {
429 *fSuccess = false;
430 // If one tile fails to write to a file, do not continue drawing the rest.
431 break;
432 }
433 }
434 fDone->run();
435 }
436
437 void setPathAndSuccess(const SkString* path, bool* success) {
438 fPath = path;
439 fSuccess = success;
440 }
441
442private:
443 // All pointers unowned.
444 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
445 // is threadsafe.
446 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
447 const SkString* fPath; // If non-null, path to write the result to as a PNG.
448 SkTDArray<SkRect>& fRects; // All tiles of the picture.
449 const int fStart; // Range of tiles drawn by this thread.
450 const int fEnd;
451 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
452 // and only set to false upon failure to write to a PNG.
453 SkRunnable* fDone;
454};
455
456MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
457: fNumThreads(threadCount)
458, fThreadPool(threadCount)
459, fCountdown(threadCount) {
460 // Only need to create fNumThreads - 1 clones, since one thread will use the base
461 // picture.
462 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
463 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
464}
465
466void MultiCorePictureRenderer::init(SkPicture *pict) {
467 // Set fPicture and the tiles.
468 this->INHERITED::init(pict);
469 for (int i = 0; i < fNumThreads; ++i) {
470 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
471 }
472 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
473 fPicture->clone(fPictureClones, fNumThreads - 1);
474 // Populate each thread with the appropriate data.
475 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
476 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
477
478 for (int i = 0; i < fNumThreads; i++) {
479 SkPicture* pic;
480 if (i == fNumThreads-1) {
481 // The last set will use the original SkPicture.
482 pic = fPicture;
483 } else {
484 pic = &fPictureClones[i];
485 }
486 const int start = i * chunkSize;
487 const int end = SkMin32(start + chunkSize, fTileRects.count());
488 fCloneData[i] = SkNEW_ARGS(CloneData,
489 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
490 }
491}
492
493bool MultiCorePictureRenderer::render(const SkString *path) {
494 bool success = true;
495 if (path != NULL) {
496 for (int i = 0; i < fNumThreads-1; i++) {
497 fCloneData[i]->setPathAndSuccess(path, &success);
498 }
499 }
500
501 fCountdown.reset(fNumThreads);
502 for (int i = 0; i < fNumThreads; i++) {
503 fThreadPool.add(fCloneData[i]);
504 }
505 fCountdown.wait();
506
507 return success;
508}
509
510void MultiCorePictureRenderer::end() {
511 for (int i = 0; i < fNumThreads - 1; i++) {
512 SkDELETE(fCloneData[i]);
513 fCloneData[i] = NULL;
514 }
515
516 fCanvasPool.unrefAll();
517
518 this->INHERITED::end();
519}
520
521MultiCorePictureRenderer::~MultiCorePictureRenderer() {
522 // Each individual CloneData was deleted in end.
523 SkDELETE_ARRAY(fCloneData);
524 SkDELETE_ARRAY(fPictureClones);
525}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000526
scroggo@google.com0a049b82012-11-02 22:01:26 +0000527SkString MultiCorePictureRenderer::getConfigNameInternal() {
528 SkString name = this->INHERITED::getConfigNameInternal();
529 name.appendf("_multi_%i_threads", fNumThreads);
530 return name;
531}
532
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000533///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000534
535void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000536 fReplayer.reset(this->createPicture());
537 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
538 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000539 fPicture->draw(recorder);
540}
541
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000542bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000543 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000544 // Since this class does not actually render, return false.
545 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000546}
547
scroggo@google.com0a049b82012-11-02 22:01:26 +0000548SkString PlaybackCreationRenderer::getConfigNameInternal() {
549 return SkString("playback_creation");
550}
551
junov@chromium.org9313ca42012-11-02 18:11:49 +0000552///////////////////////////////////////////////////////////////////////////////////////////////
553// SkPicture variants for each BBoxHierarchy type
554
555class RTreePicture : public SkPicture {
556public:
557 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
558 static const int kRTreeMinChildren = 6;
559 static const int kRTreeMaxChildren = 11;
560 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
561 SkIntToScalar(fHeight));
562 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
563 aspectRatio);
564 }
565};
566
567SkPicture* PictureRenderer::createPicture() {
568 switch (fBBoxHierarchyType) {
569 case kNone_BBoxHierarchyType:
570 return SkNEW(SkPicture);
571 case kRTree_BBoxHierarchyType:
572 return SkNEW(RTreePicture);
573 }
574 SkASSERT(0); // invalid bbhType
575 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000576}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000577
578} // namespace sk_tools