blob: 1e6ddfa1d6457cb1cb5924568c2760e5bfac9eff [file] [log] [blame]
robertphillips@google.comad7d4812013-04-12 15:13:35 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm.h"
9#include "SkDebugCanvas.h"
10#include "SkPictureFlat.h"
11
12#define WARN(msg) \
13 SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
14
robertphillips@google.comad7d4812013-04-12 15:13:35 +000015// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +000016// heavy-weight operation since we are drawing the picture into a debug canvas
robertphillips@google.comad7d4812013-04-12 15:13:35 +000017// to extract the commands.
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +000018static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +000019 SkDebugCanvas debugCanvas(input.width(), input.height());
20 debugCanvas.setBounds(input.width(), input.height());
21 input.draw(&debugCanvas);
22
23 if (pattern.count() != debugCanvas.getSize()) {
24 return false;
25 }
26
27 for (int i = 0; i < pattern.count(); ++i) {
28 if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
29 return false;
30 }
31 }
32
33 return true;
34}
35
36// construct the pattern removed by the SkPictureRecord::remove_save_layer1
37// optimization, i.e.:
38// SAVE_LAYER
39// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
40// RESTORE
41//
42// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
43// takes a different path if this is false)
44// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
45// takes a different path if this is false)
46// colorsMatch - control if the saveLayer and dbmr2r paint colors
47// match (the optimization will fail if they do not)
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +000048static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
49 SkTDArray<DrawType>* postOptPattern,
50 const SkBitmap& checkerBoard,
51 bool saveLayerHasPaint,
52 bool dbmr2rHasPaint,
53 bool colorsMatch) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +000054 // Create the pattern that should trigger the optimization
55 preOptPattern->setCount(5);
56 (*preOptPattern)[0] = SAVE;
57 (*preOptPattern)[1] = SAVE_LAYER;
58 (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
59 (*preOptPattern)[3] = RESTORE;
60 (*preOptPattern)[4] = RESTORE;
61
62 if (colorsMatch) {
63 // Create the pattern that should appear after the optimization
64 postOptPattern->setCount(5);
65 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
66 (*postOptPattern)[1] = SAVE;
67 (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
68 (*postOptPattern)[3] = RESTORE;
69 (*postOptPattern)[4] = RESTORE;
70 } else {
71 // Create the pattern that appears if the optimization doesn't fire
72 postOptPattern->setCount(7);
73 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
74 (*postOptPattern)[1] = SAVE;
75 (*postOptPattern)[2] = SAVE_LAYER;
76 (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
77 (*postOptPattern)[4] = RESTORE;
78 (*postOptPattern)[5] = RESTORE;
79 (*postOptPattern)[6] = RESTORE;
80 }
81
robertphillips@google.com84b18c72014-04-13 19:09:42 +000082 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +000083
robertphillips@google.com84b18c72014-04-13 19:09:42 +000084 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.comad7d4812013-04-12 15:13:35 +000085 // have to disable the optimizations while generating the picture
robertphillips@google.com84b18c72014-04-13 19:09:42 +000086 recorder.internalOnly_EnableOpts(false);
robertphillips@google.comad7d4812013-04-12 15:13:35 +000087
88 SkPaint saveLayerPaint;
89 saveLayerPaint.setColor(0xCC000000);
90
91 // saveLayer's 'bounds' parameter must be NULL for this optimization
92 if (saveLayerHasPaint) {
93 canvas->saveLayer(NULL, &saveLayerPaint);
94 } else {
95 canvas->saveLayer(NULL, NULL);
96 }
97
98 SkRect rect = { 10, 10, 90, 90 };
99
100 // The dbmr2r's paint must be opaque
101 SkPaint dbmr2rPaint;
102 if (colorsMatch) {
103 dbmr2rPaint.setColor(0xFF000000);
104 } else {
105 dbmr2rPaint.setColor(0xFFFF0000);
106 }
107
108 if (dbmr2rHasPaint) {
109 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
110 } else {
111 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
112 }
113 canvas->restore();
114
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000115 return recorder.endRecording();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000116}
117
118// straight-ahead version that is seen in the skps
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000119static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
120 SkTDArray<DrawType>* postOptPattern,
121 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000122 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
123 true, // saveLayer has a paint
124 true, // dbmr2r has a paint
125 true); // and the colors match
126}
127
128// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000129static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
130 SkTDArray<DrawType>* postOptPattern,
131 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000132 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
133 false, // saveLayer doesn't have a paint!
134 true, // dbmr2r has a paint
135 true); // color matching not really applicable
136}
137
138// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000139static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
140 SkTDArray<DrawType>* postOptPattern,
141 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000142 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
143 true, // saveLayer has a paint
144 false, // dbmr2r doesn't have a paint!
145 true); // color matching not really applicable
146}
147
148// version in which the optimization fails b.c. the colors don't match
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000149static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
150 SkTDArray<DrawType>* postOptPattern,
151 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000152 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
153 true, // saveLayer has a paint
154 true, // dbmr2r has a paint
155 false); // and the colors don't match!
156}
157
158// construct the pattern removed by the SkPictureRecord::remove_save_layer2
159// optimization, i.e.:
160// SAVE_LAYER (with NULL == bounds)
161// SAVE
162// CLIP_RECT
163// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
164// RESTORE
165// RESTORE
166//
167// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
168// takes a different path if this is false)
169// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
170// takes a different path if this is false)
171// colorsMatch - control if the saveLayer and dbmr2r paint colors
172// match (the optimization will fail if they do not)
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000173static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern,
174 SkTDArray<DrawType>* postOptPattern,
175 const SkBitmap& checkerBoard,
176 bool saveLayerHasPaint,
177 bool dbmr2rHasPaint,
178 bool colorsMatch) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000179 // Create the pattern that should trigger the optimization
180 preOptPattern->setCount(8);
181 (*preOptPattern)[0] = SAVE;
182 (*preOptPattern)[1] = SAVE_LAYER;
183 (*preOptPattern)[2] = SAVE;
184 (*preOptPattern)[3] = CLIP_RECT;
185 (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
186 (*preOptPattern)[5] = RESTORE;
187 (*preOptPattern)[6] = RESTORE;
188 (*preOptPattern)[7] = RESTORE;
189
190 if (colorsMatch) {
191 // Create the pattern that should appear after the optimization
192 postOptPattern->setCount(8);
193 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
194 (*postOptPattern)[1] = SAVE;
195 (*postOptPattern)[2] = SAVE;
196 (*postOptPattern)[3] = CLIP_RECT;
197 (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
198 (*postOptPattern)[5] = RESTORE;
199 (*postOptPattern)[6] = RESTORE;
200 (*postOptPattern)[7] = RESTORE;
201 } else {
202 // Create the pattern that appears if the optimization doesn't fire
203 postOptPattern->setCount(10);
204 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
205 (*postOptPattern)[1] = SAVE;
206 (*postOptPattern)[2] = SAVE_LAYER;
207 (*postOptPattern)[3] = SAVE;
208 (*postOptPattern)[4] = CLIP_RECT;
209 (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
210 (*postOptPattern)[6] = RESTORE;
211 (*postOptPattern)[7] = RESTORE;
212 (*postOptPattern)[8] = RESTORE;
213 (*postOptPattern)[9] = RESTORE;
214 }
215
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000216 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000217
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000218 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000219 // have to disable the optimizations while generating the picture
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000220 recorder.internalOnly_EnableOpts(false);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000221
222 SkPaint saveLayerPaint;
223 saveLayerPaint.setColor(0xCC000000);
224
225 // saveLayer's 'bounds' parameter must be NULL for this optimization
226 if (saveLayerHasPaint) {
227 canvas->saveLayer(NULL, &saveLayerPaint);
228 } else {
229 canvas->saveLayer(NULL, NULL);
230 }
231
232 canvas->save();
233
234 SkRect rect = { 10, 10, 90, 90 };
235 canvas->clipRect(rect);
236
237 // The dbmr2r's paint must be opaque
238 SkPaint dbmr2rPaint;
239 if (colorsMatch) {
240 dbmr2rPaint.setColor(0xFF000000);
241 } else {
242 dbmr2rPaint.setColor(0xFFFF0000);
243 }
244
245 if (dbmr2rHasPaint) {
246 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
247 } else {
248 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
249 }
250 canvas->restore();
251 canvas->restore();
252
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000253 return recorder.endRecording();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000254}
255
256// straight-ahead version that is seen in the skps
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000257static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
258 SkTDArray<DrawType>* postOptPattern,
259 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000260 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
261 true, // saveLayer has a paint
262 true, // dbmr2r has a paint
263 true); // and the colors match
264}
265
266// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000267static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
268 SkTDArray<DrawType>* postOptPattern,
269 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000270 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
271 false, // saveLayer doesn't have a paint!
272 true, // dbmr2r has a paint
273 true); // color matching not really applicable
274}
275
276// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000277static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
278 SkTDArray<DrawType>* postOptPattern,
279 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000280 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
281 true, // saveLayer has a paint
282 false, // dbmr2r doesn't have a paint!
283 true); // color matching not really applicable
284}
285
286// version in which the optimization fails b.c. the colors don't match
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000287static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
288 SkTDArray<DrawType>* postOptPattern,
289 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000290 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
291 true, // saveLayer has a paint
292 true, // dbmr2r has a paint
293 false); // and the colors don't match!
294}
295
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000296// As our .skp optimizations get folded into the captured skps our code will
297// no longer be locally exercised. This GM manually constructs the patterns
298// our optimizations will remove to test them. It acts as both a GM and a unit
299// test
300class OptimizationsGM : public skiagm::GM {
301public:
302 OptimizationsGM() {
303 this->makeCheckerboard();
304 }
305
306 static const int kWidth = 800;
307 static const int kHeight = 800;
308
309protected:
commit-bot@chromium.org96ab95f2014-01-09 16:45:38 +0000310 uint32_t onGetFlags() const SK_OVERRIDE {
311 // One optimization changes the color drawn slightly in a 565 target.
312 // We've decided it's innocuous, so we disable this GM when targeting 565.
313 // Revisit this if we get finer-grained control: it'd be nice to keep drawing directly.
314 // For more, see skia:1994.
315 return skiagm::GM::kSkip565_Flag;
316 }
317
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000318 SkString onShortName() {
319 return SkString("optimizations");
320 }
321
322 SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
323
324 typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
325 SkTDArray<DrawType> *postOptPattern,
326 const SkBitmap& checkerBoard);
327
328 virtual void onDraw(SkCanvas* canvas) {
329
330 PFCreateOpt gOpts[] = {
331 create_save_layer_opt_1_v1,
332 create_save_layer_opt_1_v2,
333 create_save_layer_opt_1_v3,
334 create_save_layer_opt_1_v4,
335 create_save_layer_opt_2_v1,
336 create_save_layer_opt_2_v2,
337 create_save_layer_opt_2_v3,
338 create_save_layer_opt_2_v4,
339 };
340
341 SkTDArray<DrawType> prePattern, postPattern;
342 int xPos = 0, yPos = 0;
343
344 for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
345 SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
346
347 if (!(check_pattern(*pre, prePattern))) {
348 WARN("Pre optimization pattern mismatch");
349 SkASSERT(0);
350 }
351
352 canvas->save();
353 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
354 pre->draw(canvas);
355 xPos += pre->width();
356 canvas->restore();
357
358 // re-render the 'pre' picture and thus 'apply' the optimization
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000359 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000360
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000361 SkCanvas* recordCanvas = recorder.beginRecording(pre->width(), pre->height());
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000362
363 pre->draw(recordCanvas);
364
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000365 SkAutoTUnref<SkPicture> post(recorder.endRecording());
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000366
367 if (!(check_pattern(*post, postPattern))) {
368 WARN("Post optimization pattern mismatch");
369 SkASSERT(0);
370 }
371
372 canvas->save();
373 canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
374 post->draw(canvas);
375 xPos += post->width();
376 canvas->restore();
377
378 if (xPos >= kWidth) {
379 // start a new line
380 xPos = 0;
381 yPos += post->height();
382 }
383
384 // TODO: we could also render the pre and post pictures to bitmaps
385 // and manually compare them in this method
386 }
387 }
388
389private:
390 void makeCheckerboard() {
391 static const unsigned int kCheckerboardWidth = 16;
392 static const unsigned int kCheckerboardHeight = 16;
393
reed@google.comeb9a46c2014-01-25 16:46:20 +0000394 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000395 for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
396 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
397 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
398 *scanline++ = 0xFFFFFFFF;
399 *scanline++ = 0xFF000000;
400 }
401 scanline = fCheckerboard.getAddr32(0, y + 1);
402 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
403 *scanline++ = 0xFF000000;
404 *scanline++ = 0xFFFFFFFF;
405 }
406 }
407 }
408
409 SkBitmap fCheckerboard;
410
411 typedef skiagm::GM INHERITED;
412};
413
414//////////////////////////////////////////////////////////////////////////////
415
416DEF_GM( return new OptimizationsGM; )