• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include "SkPictureRecorder.h"
12 
13 #define WARN(msg)                                           \
14     SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
15 
16 // Do the commands in 'input' match the supplied pattern? Note: this is a pretty
17 // heavy-weight operation since we are drawing the picture into a debug canvas
18 // to extract the commands.
check_pattern(SkPicture & input,const SkTDArray<DrawType> & pattern)19 static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
20     SkDebugCanvas debugCanvas(SkScalarCeilToInt(input.cullRect().width()),
21                               SkScalarCeilToInt(input.cullRect().height()));
22     input.playback(&debugCanvas);
23 
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)
create_save_layer_opt_1(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard,bool saveLayerHasPaint,bool dbmr2rHasPaint,bool colorsMatch)49 static 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)  {
55     // 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 
83     SkPictureRecorder recorder;
84 
85     SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
86     // have to disable the optimizations while generating the picture
87     recorder.internalOnly_EnableOpts(false);
88 
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 
116     return recorder.endRecording();
117 }
118 
119 // straight-ahead version that is seen in the skps
create_save_layer_opt_1_v1(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)120 static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
121                                              SkTDArray<DrawType>* postOptPattern,
122                                              const SkBitmap& checkerBoard) {
123     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
create_save_layer_opt_1_v2(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)130 static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
131                                              SkTDArray<DrawType>* postOptPattern,
132                                              const SkBitmap& checkerBoard) {
133     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
create_save_layer_opt_1_v3(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)140 static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
141                                              SkTDArray<DrawType>* postOptPattern,
142                                              const SkBitmap& checkerBoard) {
143     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
create_save_layer_opt_1_v4(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)150 static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
151                                              SkTDArray<DrawType>* postOptPattern,
152                                              const SkBitmap& checkerBoard) {
153     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)
create_save_layer_opt_2(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard,bool saveLayerHasPaint,bool dbmr2rHasPaint,bool colorsMatch)174 static 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)  {
180     // 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 
217     SkPictureRecorder recorder;
218 
219     SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
220     // have to disable the optimizations while generating the picture
221     recorder.internalOnly_EnableOpts(false);
222 
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 
254     return recorder.endRecording();
255 }
256 
257 // straight-ahead version that is seen in the skps
create_save_layer_opt_2_v1(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)258 static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
259                                              SkTDArray<DrawType>* postOptPattern,
260                                              const SkBitmap& checkerBoard) {
261     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
create_save_layer_opt_2_v2(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)268 static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
269                                              SkTDArray<DrawType>* postOptPattern,
270                                              const SkBitmap& checkerBoard) {
271     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
create_save_layer_opt_2_v3(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)278 static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
279                                              SkTDArray<DrawType>* postOptPattern,
280                                              const SkBitmap& checkerBoard) {
281     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
create_save_layer_opt_2_v4(SkTDArray<DrawType> * preOptPattern,SkTDArray<DrawType> * postOptPattern,const SkBitmap & checkerBoard)288 static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
289                                              SkTDArray<DrawType>* postOptPattern,
290                                              const SkBitmap& checkerBoard) {
291     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 
297 // 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
301 class OptimizationsGM : public skiagm::GM {
302 public:
OptimizationsGM()303     OptimizationsGM() {
304         this->makeCheckerboard();
305     }
306 
307     static const int kWidth = 800;
308     static const int kHeight = 800;
309 
310 protected:
onGetFlags() const311     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 
onShortName()319     SkString onShortName() {
320         return SkString("optimizations");
321     }
322 
onISize()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 
onDraw(SkCanvas * canvas)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;
343         SkScalar xPos = 0, yPos = 0;
344 
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();
354                 canvas->translate(xPos, yPos);
355                 pre->playback(canvas);
356                 xPos += pre->cullRect().width();
357             canvas->restore();
358 
359             // re-render the 'pre' picture and thus 'apply' the optimization
360             SkPictureRecorder recorder;
361 
362             SkCanvas* recordCanvas =
363                 recorder.DEPRECATED_beginRecording(pre->cullRect().width(),
364                                                    pre->cullRect().height(),
365                                                    NULL, 0);
366 
367             pre->playback(recordCanvas);
368 
369             SkAutoTUnref<SkPicture> post(recorder.endRecording());
370 
371             if (!(check_pattern(*post, postPattern))) {
372                 WARN("Post optimization pattern mismatch");
373                 SkASSERT(0);
374             }
375 
376             canvas->save();
377                 canvas->translate(xPos, yPos);
378                 post->playback(canvas);
379                 xPos += post->cullRect().width();
380             canvas->restore();
381 
382             if (xPos >= kWidth) {
383                 // start a new line
384                 xPos = 0;
385                 yPos += post->cullRect().height();
386             }
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 
393 private:
makeCheckerboard()394     void makeCheckerboard() {
395         static const unsigned int kCheckerboardWidth = 16;
396         static const unsigned int kCheckerboardHeight = 16;
397 
398         fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
399         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 
420 DEF_GM( return new OptimizationsGM; )
421