blob: cea7b5bff5231725cbcd69bd08215f6169397312 [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"
keyar@chromium.orgea826952012-08-23 15:24:13 +000021#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000022#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000023#include "SkTemplates.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000024#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000025#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000026#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000027
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000028namespace sk_tools {
29
30enum {
31 kDefaultTileWidth = 256,
32 kDefaultTileHeight = 256
33};
34
keyar@chromium.org9d696c02012-08-07 17:11:33 +000035void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000036 SkASSERT(NULL == fPicture);
37 SkASSERT(NULL == fCanvas.get());
38 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000039 return;
40 }
41
42 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000043 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000044 return;
45 }
46
47 fPicture = pict;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000048 fCanvas.reset(this->setupCanvas());
49}
50
51SkCanvas* PictureRenderer::setupCanvas() {
52 return this->setupCanvas(fPicture->width(), fPicture->height());
53}
54
55SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000056 switch(fDeviceType) {
57 case kBitmap_DeviceType: {
58 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000059 sk_tools::setup_bitmap(&bitmap, width, height);
60 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000061 break;
62 }
63#if SK_SUPPORT_GPU
64 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000065 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000066 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000067 width, height)));
68 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000069 break;
70 }
71#endif
72 default:
73 SkASSERT(0);
74 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000075
76 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000077}
78
79void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000080 this->resetState();
keyar@chromium.org9d696c02012-08-07 17:11:33 +000081 fPicture = NULL;
82 fCanvas.reset(NULL);
83}
84
keyar@chromium.org77a55222012-08-20 15:03:47 +000085void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +000086#if SK_SUPPORT_GPU
87 if (this->isUsingGpuDevice()) {
88 SkGLContext* glContext = fGrContextFactory.getGLContext(
89 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +000090
91 SkASSERT(glContext != NULL);
92 if (NULL == glContext) {
93 return;
94 }
95
scroggo@google.com9a412522012-09-07 15:21:18 +000096 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +000097 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +000098 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +000099#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000100}
101
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000102bool PictureRenderer::write(const SkString& path) const {
103 SkASSERT(fCanvas.get() != NULL);
104 SkASSERT(fPicture != NULL);
105 if (NULL == fCanvas.get() || NULL == fPicture) {
106 return false;
107 }
108
109 SkBitmap bitmap;
110 sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height());
111
112 fCanvas->readPixels(&bitmap, 0, 0);
113 sk_tools::force_all_opaque(bitmap);
114
115 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
116}
117
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000118///////////////////////////////////////////////////////////////////////////////////////////////
119
scroggo@google.com9a412522012-09-07 15:21:18 +0000120void RecordPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
121 SkPicture replayer;
122 SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height());
123 fPicture->draw(recorder);
124 replayer.endRecording();
125}
126
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000127///////////////////////////////////////////////////////////////////////////////////////////////
128
scroggo@google.com9a412522012-09-07 15:21:18 +0000129void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000130 SkASSERT(fCanvas.get() != NULL);
131 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000132 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000133 return;
134 }
135
136 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000137 SkGPipeWriter writer;
138 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000139 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000140 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000141 fCanvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000142}
143
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000144///////////////////////////////////////////////////////////////////////////////////////////////
145
scroggo@google.com9a412522012-09-07 15:21:18 +0000146void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000147 SkASSERT(fCanvas.get() != NULL);
148 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000149 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000150 return;
151 }
152
153 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000154 fCanvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000155}
156
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000157///////////////////////////////////////////////////////////////////////////////////////////////
158
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000159TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000160 : fMultiThreaded(false)
161 , fUsePipe(false)
162 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000163 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000164 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000165 , fTileHeightPercentage(0.0)
rileya@google.coma04dc022012-09-10 19:01:38 +0000166 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000167
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000168void TiledPictureRenderer::init(SkPicture* pict) {
169 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000170 SkASSERT(0 == fTileRects.count());
171 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000172 return;
173 }
174
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000175 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
176 // used by bench_pictures.
177 fPicture = pict;
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000178
179 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000180 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000181 }
182 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000183 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000184 }
185
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000186 if (fTileMinPowerOf2Width > 0) {
187 this->setupPowerOf2Tiles();
188 } else {
189 this->setupTiles();
190 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000191}
192
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000193void TiledPictureRenderer::end() {
194 this->deleteTiles();
195 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000196}
197
198TiledPictureRenderer::~TiledPictureRenderer() {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000199 this->deleteTiles();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000200}
201
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000202void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000203 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
204 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
205 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
206 SkIntToScalar(tile_y_start),
207 SkIntToScalar(fTileWidth),
208 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000209 }
210 }
211}
212
213// The goal of the powers of two tiles is to minimize the amount of wasted tile
214// space in the width-wise direction and then minimize the number of tiles. The
215// constraints are that every tile must have a pixel width that is a power of
216// two and also be of some minimal width (that is also a power of two).
217//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000218// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000219// multiple of the minimal width. The binary representation of this rounded
220// value gives us the tiles we need: a bit of value one means we need a tile of
221// that size.
222void TiledPictureRenderer::setupPowerOf2Tiles() {
223 int rounded_value = fPicture->width();
224 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
225 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
226 + fTileMinPowerOf2Width;
227 }
228
robertphillips@google.com94acc702012-09-06 18:43:21 +0000229 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000230 int largest_possible_tile_size = 1 << num_bits;
231
232 // The tile height is constant for a particular picture.
233 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
234 int tile_x_start = 0;
235 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000236 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
237 // to draw each tile.
238 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000239
240 while (current_width >= fTileMinPowerOf2Width) {
241 // It is very important this is a bitwise AND.
242 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000243 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
244 SkIntToScalar(tile_y_start),
245 SkIntToScalar(current_width),
246 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000247 tile_x_start += current_width;
248 }
249
250 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000251 }
252 }
253}
254
255void TiledPictureRenderer::deleteTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000256 fTileRects.reset();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000257}
258
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000259///////////////////////////////////////////////////////////////////////////////////////////////
260// Draw using Pipe
261
262struct TileData {
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000263 TileData(SkCanvas* canvas, ThreadSafePipeController* controller);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000264 SkCanvas* fCanvas;
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000265 ThreadSafePipeController* fController;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000266 SkThread fThread;
267};
268
269static void DrawTile(void* data) {
270 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
271 TileData* tileData = static_cast<TileData*>(data);
272 tileData->fController->playback(tileData->fCanvas);
scroggo@google.com9a412522012-09-07 15:21:18 +0000273 tileData->fCanvas->flush();
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000274}
275
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000276TileData::TileData(SkCanvas* canvas, ThreadSafePipeController* controller)
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000277: fCanvas(canvas)
278, fController(controller)
279, fThread(&DrawTile, static_cast<void*>(this)) {}
280
281///////////////////////////////////////////////////////////////////////////////////////////////
282// Draw using Picture
283
284struct CloneData {
285 CloneData(SkCanvas* target, SkPicture* original);
286 SkCanvas* fCanvas;
287 SkPicture* fClone;
288 SkThread fThread;
289};
290
291static void DrawClonedTile(void* data) {
292 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
293 CloneData* cloneData = static_cast<CloneData*>(data);
294 cloneData->fCanvas->drawPicture(*cloneData->fClone);
scroggo@google.com9a412522012-09-07 15:21:18 +0000295 cloneData->fCanvas->flush();
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000296}
297
298CloneData::CloneData(SkCanvas* target, SkPicture* clone)
299: fCanvas(target)
300, fClone(clone)
301, fThread(&DrawClonedTile, static_cast<void*>(this)) {}
302
303///////////////////////////////////////////////////////////////////////////////////////////////
304
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000305void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
306 SkASSERT(fPicture != NULL);
307 if (NULL == fPicture) {
308 return;
309 }
310
311 if (doExtraWorkToDrawToBaseCanvas) {
312 if (NULL == fCanvas.get()) {
313 fCanvas.reset(this->setupCanvas());
314 }
315 }
316
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000317 if (fMultiThreaded) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000318 // FIXME: Turning off multi threading while we transition to using a pool of tiles
319/*
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000320 if (fUsePipe) {
321 // First, draw into a pipe controller
322 SkGPipeWriter writer;
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000323 ThreadSafePipeController controller(fTiles.count());
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000324 SkCanvas* pipeCanvas = writer.startRecording(&controller,
325 SkGPipeWriter::kSimultaneousReaders_Flag);
326 pipeCanvas->drawPicture(*(fPicture));
327 writer.endRecording();
328
329 // Create and start the threads.
scroggo@google.com6bc8cf82012-08-31 18:34:09 +0000330 TileData** tileData = SkNEW_ARRAY(TileData*, fTiles.count());
331 SkAutoTDeleteArray<TileData*> deleteTileData(tileData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000332 for (int i = 0; i < fTiles.count(); i++) {
333 tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller));
334 if (!tileData[i]->fThread.start()) {
335 SkDebugf("could not start thread %i\n", i);
336 }
337 }
338 for (int i = 0; i < fTiles.count(); i++) {
339 tileData[i]->fThread.join();
340 SkDELETE(tileData[i]);
341 }
342 } else {
343 SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count());
344 SkAutoTDeleteArray<SkPicture> autodelete(clones);
345 fPicture->clone(clones, fTiles.count());
scroggo@google.com6bc8cf82012-08-31 18:34:09 +0000346 CloneData** cloneData = SkNEW_ARRAY(CloneData*, fTiles.count());
347 SkAutoTDeleteArray<CloneData*> deleteCloneData(cloneData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000348 for (int i = 0; i < fTiles.count(); i++) {
349 cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i]));
350 if (!cloneData[i]->fThread.start()) {
351 SkDebugf("Could not start picture thread %i\n", i);
352 }
353 }
354 for (int i = 0; i < fTiles.count(); i++) {
355 cloneData[i]->fThread.join();
356 SkDELETE(cloneData[i]);
357 }
358 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000359 */
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000360 } else {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000361 // For single thread, we really only need one canvas total
362 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
363 SkAutoUnref aur(canvas);
364
365 // Clip the tile to an area that is completely in what the SkPicture says is the
366 // drawn-to area. This is mostly important for tiles on the right and bottom edges
367 // as they may go over this area and the picture may have some commands that
368 // draw outside of this area and so should not actually be written.
369 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
370 SkIntToScalar(fPicture->height()));
371 for (int i = 0; i < fTileRects.count(); ++i) {
372 canvas->resetMatrix();
373 canvas->clipRect(clip);
374 // Translate so that we draw the correct portion of the picture
375 canvas->translate(-fTileRects[i].fLeft, -fTileRects[i].fTop);
376 canvas->drawPicture(*(fPicture));
377 canvas->flush();
378 if (doExtraWorkToDrawToBaseCanvas) {
379 SkASSERT(fCanvas.get() != NULL);
380 SkBitmap source = canvas->getDevice()->accessBitmap(false);
381 fCanvas->drawBitmap(source, fTileRects[i].fLeft, fTileRects[i].fTop);
382 fCanvas->flush();
383 }
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000384 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000385 }
386}
387
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000388///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000389
390void PlaybackCreationRenderer::setup() {
391 SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
392 fPicture->draw(recorder);
393}
394
395void PlaybackCreationRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
396 fReplayer.endRecording();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000397}
398
399}