blob: 85ce3e9d945333ff4e2d7936df44787ea6a493c6 [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"
robertphillips@google.com770963f2014-04-18 18:04:41 +000011#include "SkPictureRecorder.h"
robertphillips@google.comad7d4812013-04-12 15:13:35 +000012
13#define WARN(msg) \
14 SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
15
robertphillips@google.comad7d4812013-04-12 15:13:35 +000016// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +000017// heavy-weight operation since we are drawing the picture into a debug canvas
robertphillips@google.comad7d4812013-04-12 15:13:35 +000018// to extract the commands.
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +000019static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -070020 SkDebugCanvas debugCanvas(SkScalarCeilToInt(input.cullRect().width()),
21 SkScalarCeilToInt(input.cullRect().height()));
robertphillipsc5ba71d2014-09-04 08:42:50 -070022 input.playback(&debugCanvas);
robertphillips@google.comad7d4812013-04-12 15:13:35 +000023
24 if (pattern.count() != debugCanvas.getSize()) {
25 return false;
26 }
27
28 for (int i = 0; i < pattern.count(); ++i) {
29 if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
30 return false;
31 }
32 }
33
34 return true;
35}
36
37// construct the pattern removed by the SkPictureRecord::remove_save_layer1
38// optimization, i.e.:
39// SAVE_LAYER
40// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
41// RESTORE
42//
43// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
44// takes a different path if this is false)
45// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
46// takes a different path if this is false)
47// colorsMatch - control if the saveLayer and dbmr2r paint colors
48// match (the optimization will fail if they do not)
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +000049static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
50 SkTDArray<DrawType>* postOptPattern,
51 const SkBitmap& checkerBoard,
52 bool saveLayerHasPaint,
53 bool dbmr2rHasPaint,
54 bool colorsMatch) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +000055 // Create the pattern that should trigger the optimization
56 preOptPattern->setCount(5);
57 (*preOptPattern)[0] = SAVE;
58 (*preOptPattern)[1] = SAVE_LAYER;
59 (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
60 (*preOptPattern)[3] = RESTORE;
61 (*preOptPattern)[4] = RESTORE;
62
63 if (colorsMatch) {
64 // Create the pattern that should appear after the optimization
65 postOptPattern->setCount(5);
66 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
67 (*postOptPattern)[1] = SAVE;
68 (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
69 (*postOptPattern)[3] = RESTORE;
70 (*postOptPattern)[4] = RESTORE;
71 } else {
72 // Create the pattern that appears if the optimization doesn't fire
73 postOptPattern->setCount(7);
74 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
75 (*postOptPattern)[1] = SAVE;
76 (*postOptPattern)[2] = SAVE_LAYER;
77 (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
78 (*postOptPattern)[4] = RESTORE;
79 (*postOptPattern)[5] = RESTORE;
80 (*postOptPattern)[6] = RESTORE;
81 }
82
robertphillips@google.com84b18c72014-04-13 19:09:42 +000083 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +000084
mtkleinc92e5502014-08-21 13:07:27 -070085 SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
robertphillips@google.comad7d4812013-04-12 15:13:35 +000086 // have to disable the optimizations while generating the picture
robertphillips@google.com84b18c72014-04-13 19:09:42 +000087 recorder.internalOnly_EnableOpts(false);
robertphillips@google.comad7d4812013-04-12 15:13:35 +000088
89 SkPaint saveLayerPaint;
90 saveLayerPaint.setColor(0xCC000000);
91
92 // saveLayer's 'bounds' parameter must be NULL for this optimization
93 if (saveLayerHasPaint) {
94 canvas->saveLayer(NULL, &saveLayerPaint);
95 } else {
96 canvas->saveLayer(NULL, NULL);
97 }
98
99 SkRect rect = { 10, 10, 90, 90 };
100
101 // The dbmr2r's paint must be opaque
102 SkPaint dbmr2rPaint;
103 if (colorsMatch) {
104 dbmr2rPaint.setColor(0xFF000000);
105 } else {
106 dbmr2rPaint.setColor(0xFFFF0000);
107 }
108
109 if (dbmr2rHasPaint) {
110 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
111 } else {
112 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
113 }
114 canvas->restore();
115
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000116 return recorder.endRecording();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000117}
118
119// straight-ahead version that is seen in the skps
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000120static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
121 SkTDArray<DrawType>* postOptPattern,
122 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000123 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
124 true, // saveLayer has a paint
125 true, // dbmr2r has a paint
126 true); // and the colors match
127}
128
129// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000130static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
131 SkTDArray<DrawType>* postOptPattern,
132 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000133 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
134 false, // saveLayer doesn't have a paint!
135 true, // dbmr2r has a paint
136 true); // color matching not really applicable
137}
138
139// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000140static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
141 SkTDArray<DrawType>* postOptPattern,
142 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000143 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
144 true, // saveLayer has a paint
145 false, // dbmr2r doesn't have a paint!
146 true); // color matching not really applicable
147}
148
149// version in which the optimization fails b.c. the colors don't match
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000150static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
151 SkTDArray<DrawType>* postOptPattern,
152 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000153 return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
154 true, // saveLayer has a paint
155 true, // dbmr2r has a paint
156 false); // and the colors don't match!
157}
158
159// construct the pattern removed by the SkPictureRecord::remove_save_layer2
160// optimization, i.e.:
161// SAVE_LAYER (with NULL == bounds)
162// SAVE
163// CLIP_RECT
164// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
165// RESTORE
166// RESTORE
167//
168// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
169// takes a different path if this is false)
170// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
171// takes a different path if this is false)
172// colorsMatch - control if the saveLayer and dbmr2r paint colors
173// match (the optimization will fail if they do not)
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000174static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern,
175 SkTDArray<DrawType>* postOptPattern,
176 const SkBitmap& checkerBoard,
177 bool saveLayerHasPaint,
178 bool dbmr2rHasPaint,
179 bool colorsMatch) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000180 // Create the pattern that should trigger the optimization
181 preOptPattern->setCount(8);
182 (*preOptPattern)[0] = SAVE;
183 (*preOptPattern)[1] = SAVE_LAYER;
184 (*preOptPattern)[2] = SAVE;
185 (*preOptPattern)[3] = CLIP_RECT;
186 (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
187 (*preOptPattern)[5] = RESTORE;
188 (*preOptPattern)[6] = RESTORE;
189 (*preOptPattern)[7] = RESTORE;
190
191 if (colorsMatch) {
192 // Create the pattern that should appear after the optimization
193 postOptPattern->setCount(8);
194 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
195 (*postOptPattern)[1] = SAVE;
196 (*postOptPattern)[2] = SAVE;
197 (*postOptPattern)[3] = CLIP_RECT;
198 (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
199 (*postOptPattern)[5] = RESTORE;
200 (*postOptPattern)[6] = RESTORE;
201 (*postOptPattern)[7] = RESTORE;
202 } else {
203 // Create the pattern that appears if the optimization doesn't fire
204 postOptPattern->setCount(10);
205 (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
206 (*postOptPattern)[1] = SAVE;
207 (*postOptPattern)[2] = SAVE_LAYER;
208 (*postOptPattern)[3] = SAVE;
209 (*postOptPattern)[4] = CLIP_RECT;
210 (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
211 (*postOptPattern)[6] = RESTORE;
212 (*postOptPattern)[7] = RESTORE;
213 (*postOptPattern)[8] = RESTORE;
214 (*postOptPattern)[9] = RESTORE;
215 }
216
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000217 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000218
mtkleinc92e5502014-08-21 13:07:27 -0700219 SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000220 // have to disable the optimizations while generating the picture
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000221 recorder.internalOnly_EnableOpts(false);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000222
223 SkPaint saveLayerPaint;
224 saveLayerPaint.setColor(0xCC000000);
225
226 // saveLayer's 'bounds' parameter must be NULL for this optimization
227 if (saveLayerHasPaint) {
228 canvas->saveLayer(NULL, &saveLayerPaint);
229 } else {
230 canvas->saveLayer(NULL, NULL);
231 }
232
233 canvas->save();
234
235 SkRect rect = { 10, 10, 90, 90 };
236 canvas->clipRect(rect);
237
238 // The dbmr2r's paint must be opaque
239 SkPaint dbmr2rPaint;
240 if (colorsMatch) {
241 dbmr2rPaint.setColor(0xFF000000);
242 } else {
243 dbmr2rPaint.setColor(0xFFFF0000);
244 }
245
246 if (dbmr2rHasPaint) {
247 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
248 } else {
249 canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
250 }
251 canvas->restore();
252 canvas->restore();
253
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000254 return recorder.endRecording();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000255}
256
257// straight-ahead version that is seen in the skps
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000258static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
259 SkTDArray<DrawType>* postOptPattern,
260 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000261 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
262 true, // saveLayer has a paint
263 true, // dbmr2r has a paint
264 true); // and the colors match
265}
266
267// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000268static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
269 SkTDArray<DrawType>* postOptPattern,
270 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000271 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
272 false, // saveLayer doesn't have a paint!
273 true, // dbmr2r has a paint
274 true); // color matching not really applicable
275}
276
277// alternate version that should still succeed
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000278static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
279 SkTDArray<DrawType>* postOptPattern,
280 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000281 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
282 true, // saveLayer has a paint
283 false, // dbmr2r doesn't have a paint!
284 true); // color matching not really applicable
285}
286
287// version in which the optimization fails b.c. the colors don't match
commit-bot@chromium.org6c4e71a2013-11-20 21:32:10 +0000288static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
289 SkTDArray<DrawType>* postOptPattern,
290 const SkBitmap& checkerBoard) {
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000291 return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
292 true, // saveLayer has a paint
293 true, // dbmr2r has a paint
294 false); // and the colors don't match!
295}
296
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000297// As our .skp optimizations get folded into the captured skps our code will
298// no longer be locally exercised. This GM manually constructs the patterns
299// our optimizations will remove to test them. It acts as both a GM and a unit
300// test
301class OptimizationsGM : public skiagm::GM {
302public:
303 OptimizationsGM() {
304 this->makeCheckerboard();
305 }
306
307 static const int kWidth = 800;
308 static const int kHeight = 800;
309
310protected:
commit-bot@chromium.org96ab95f2014-01-09 16:45:38 +0000311 uint32_t onGetFlags() const SK_OVERRIDE {
312 // One optimization changes the color drawn slightly in a 565 target.
313 // We've decided it's innocuous, so we disable this GM when targeting 565.
314 // Revisit this if we get finer-grained control: it'd be nice to keep drawing directly.
315 // For more, see skia:1994.
316 return skiagm::GM::kSkip565_Flag;
317 }
318
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000319 SkString onShortName() {
320 return SkString("optimizations");
321 }
322
323 SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
324
325 typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
326 SkTDArray<DrawType> *postOptPattern,
327 const SkBitmap& checkerBoard);
328
329 virtual void onDraw(SkCanvas* canvas) {
330
331 PFCreateOpt gOpts[] = {
332 create_save_layer_opt_1_v1,
333 create_save_layer_opt_1_v2,
334 create_save_layer_opt_1_v3,
335 create_save_layer_opt_1_v4,
336 create_save_layer_opt_2_v1,
337 create_save_layer_opt_2_v2,
338 create_save_layer_opt_2_v3,
339 create_save_layer_opt_2_v4,
340 };
341
342 SkTDArray<DrawType> prePattern, postPattern;
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700343 SkScalar xPos = 0, yPos = 0;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000344
345 for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
346 SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
347
348 if (!(check_pattern(*pre, prePattern))) {
349 WARN("Pre optimization pattern mismatch");
350 SkASSERT(0);
351 }
352
353 canvas->save();
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700354 canvas->translate(xPos, yPos);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700355 pre->playback(canvas);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700356 xPos += pre->cullRect().width();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000357 canvas->restore();
358
359 // re-render the 'pre' picture and thus 'apply' the optimization
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000360 SkPictureRecorder recorder;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000361
mtkleinc92e5502014-08-21 13:07:27 -0700362 SkCanvas* recordCanvas =
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700363 recorder.DEPRECATED_beginRecording(pre->cullRect().width(),
364 pre->cullRect().height(),
365 NULL, 0);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000366
robertphillipsc5ba71d2014-09-04 08:42:50 -0700367 pre->playback(recordCanvas);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000368
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000369 SkAutoTUnref<SkPicture> post(recorder.endRecording());
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000370
371 if (!(check_pattern(*post, postPattern))) {
372 WARN("Post optimization pattern mismatch");
373 SkASSERT(0);
374 }
375
376 canvas->save();
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700377 canvas->translate(xPos, yPos);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700378 post->playback(canvas);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700379 xPos += post->cullRect().width();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000380 canvas->restore();
381
382 if (xPos >= kWidth) {
383 // start a new line
384 xPos = 0;
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700385 yPos += post->cullRect().height();
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000386 }
387
388 // TODO: we could also render the pre and post pictures to bitmaps
389 // and manually compare them in this method
390 }
391 }
392
393private:
394 void makeCheckerboard() {
395 static const unsigned int kCheckerboardWidth = 16;
396 static const unsigned int kCheckerboardHeight = 16;
397
reed@google.comeb9a46c2014-01-25 16:46:20 +0000398 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000399 for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
400 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
401 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
402 *scanline++ = 0xFFFFFFFF;
403 *scanline++ = 0xFF000000;
404 }
405 scanline = fCheckerboard.getAddr32(0, y + 1);
406 for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
407 *scanline++ = 0xFF000000;
408 *scanline++ = 0xFFFFFFFF;
409 }
410 }
411 }
412
413 SkBitmap fCheckerboard;
414
415 typedef skiagm::GM INHERITED;
416};
417
418//////////////////////////////////////////////////////////////////////////////
419
420DEF_GM( return new OptimizationsGM; )