• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 "src/core/SkRecordOpts.h"
9 
10 #include "include/private/base/SkTDArray.h"
11 #include "src/core/SkCanvasPriv.h"
12 #include "src/core/SkRecordPattern.h"
13 #include "src/core/SkRecords.h"
14 
15 using namespace SkRecords;
16 
17 // Most of the optimizations in this file are pattern-based.  These are all defined as structs with:
18 //   - a Match typedef
19 //   - a bool onMatch(SkRceord*, Match*, int begin, int end) method,
20 //     which returns true if it made changes and false if not.
21 
22 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
23 // It looks for spans which match Pass::Match, and when found calls onMatch() with that pattern,
24 // record, and [begin,end) span of the commands that matched.
25 template <typename Pass>
apply(Pass * pass,SkRecord * record)26 static bool apply(Pass* pass, SkRecord* record) {
27     typename Pass::Match match;
28     bool changed = false;
29     int begin, end = 0;
30 
31     while (match.search(record, &begin, &end)) {
32         changed |= pass->onMatch(record, &match, begin, end);
33     }
34     return changed;
35 }
36 
37 ///////////////////////////////////////////////////////////////////////////////////////////////////
38 
multiple_set_matrices(SkRecord * record)39 static void multiple_set_matrices(SkRecord* record) {
40     struct {
41         typedef Pattern<Is<SetMatrix>,
42                         Greedy<Is<NoOp>>,
43                         Is<SetMatrix> >
44             Match;
45 
46         bool onMatch(SkRecord* record, Match* pattern, int begin, int end) {
47             record->replace<NoOp>(begin);  // first SetMatrix
48             return true;
49         }
50     } pass;
51     while (apply(&pass, record));
52 }
53 
54 ///////////////////////////////////////////////////////////////////////////////////////////////////
55 
56 #if 0   // experimental, but needs knowledge of previous matrix to operate correctly
57 static void apply_matrix_to_draw_params(SkRecord* record) {
58     struct {
59         typedef Pattern<Is<SetMatrix>,
60                         Greedy<Is<NoOp>>,
61                         Is<SetMatrix> >
62             Pattern;
63 
64         bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
65             record->replace<NoOp>(begin);  // first SetMatrix
66             return true;
67         }
68     } pass;
69     // No need to loop, as we never "open up" opportunities for more of this type of optimization.
70     apply(&pass, record);
71 }
72 #endif
73 
74 ///////////////////////////////////////////////////////////////////////////////////////////////////
75 
76 // Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
77 struct SaveOnlyDrawsRestoreNooper {
78     typedef Pattern<Is<Save>,
79                     Greedy<Or<Is<NoOp>, IsDraw>>,
80                     Is<Restore>>
81         Match;
82 
onMatchSaveOnlyDrawsRestoreNooper83     bool onMatch(SkRecord* record, Match*, int begin, int end) {
84         record->replace<NoOp>(begin);  // Save
85         record->replace<NoOp>(end-1);  // Restore
86         return true;
87     }
88 };
89 
fold_opacity_layer_color_to_paint(const SkPaint * layerPaint,bool isSaveLayer,SkPaint * paint)90 static bool fold_opacity_layer_color_to_paint(const SkPaint* layerPaint,
91                                               bool isSaveLayer,
92                                               SkPaint* paint) {
93     // We assume layerPaint is always from a saveLayer.  If isSaveLayer is
94     // true, we assume paint is too.
95 
96     // The alpha folding can proceed if the filter layer paint does not have properties which cause
97     // the resulting filter layer to be "blended" in complex ways to the parent layer.
98     // TODO: most likely only some xfer modes are the hard constraints
99     if (!paint->isSrcOver()) {
100         return false;
101     }
102 
103     if (!isSaveLayer && paint->getImageFilter()) {
104         // For normal draws, the paint color is used as one input for the color for the draw. Image
105         // filter will operate on the result, and thus we can not change the input.
106         // For layer saves, the image filter is applied to the layer contents. The layer is then
107         // modulated with the paint color, so it's fine to proceed with the fold for saveLayer
108         // paints with image filters.
109         return false;
110     }
111 
112     if (paint->getColorFilter()) {
113         // Filter input depends on the paint color.
114 
115         // Here we could filter the color if we knew the draw is going to be uniform color.  This
116         // should be detectable as drawPath/drawRect/.. without a shader being uniform, while
117         // drawBitmap/drawSprite or a shader being non-uniform. However, current matchers don't
118         // give the type out easily, so just do not optimize that at the moment.
119         return false;
120     }
121 
122     if (layerPaint) {
123         const uint32_t layerColor = layerPaint->getColor();
124         // The layer paint color must have only alpha component.
125         if (SK_ColorTRANSPARENT != SkColorSetA(layerColor, SK_AlphaTRANSPARENT)) {
126             return false;
127         }
128 
129         // The layer paint can not have any effects.
130         if (layerPaint->getPathEffect()  ||
131             layerPaint->getShader()      ||
132             !layerPaint->isSrcOver()     ||
133             layerPaint->getMaskFilter()  ||
134             layerPaint->getColorFilter() ||
135             layerPaint->getImageFilter()) {
136             return false;
137         }
138         paint->setAlpha(SkMulDiv255Round(paint->getAlpha(), SkColorGetA(layerColor)));
139     }
140 
141     return true;
142 }
143 
144 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
145 struct SaveNoDrawsRestoreNooper {
146     // Greedy matches greedily, so we also have to exclude Save and Restore.
147     // Nested SaveLayers need to be excluded, or we'll match their Restore!
148     typedef Pattern<Is<Save>,
149                     Greedy<Not<Or<Is<Save>,
150                                   Is<SaveLayer>,
151                                   Is<Restore>,
152                                   IsDraw>>>,
153                     Is<Restore>>
154         Match;
155 
onMatchSaveNoDrawsRestoreNooper156     bool onMatch(SkRecord* record, Match*, int begin, int end) {
157         // The entire span between Save and Restore (inclusively) does nothing.
158         for (int i = begin; i < end; i++) {
159             record->replace<NoOp>(i);
160         }
161         return true;
162     }
163 };
SkRecordNoopSaveRestores(SkRecord * record)164 void SkRecordNoopSaveRestores(SkRecord* record) {
165     SaveOnlyDrawsRestoreNooper onlyDraws;
166     SaveNoDrawsRestoreNooper noDraws;
167 
168     // Run until they stop changing things.
169     while (apply(&onlyDraws, record) || apply(&noDraws, record));
170 }
171 
172 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
effectively_srcover(const SkPaint * paint)173 static bool effectively_srcover(const SkPaint* paint) {
174     if (!paint || paint->isSrcOver()) {
175         return true;
176     }
177     // src-mode with opaque and no effects (which might change opaqueness) is ok too.
178     return !paint->getShader() && !paint->getColorFilter() && !paint->getImageFilter() &&
179            0xFF == paint->getAlpha() && paint->asBlendMode() == SkBlendMode::kSrc;
180 }
181 
182 // For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
183 // draw, and no-op the SaveLayer and Restore.
184 struct SaveLayerDrawRestoreNooper {
185     typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
186 
onMatchSaveLayerDrawRestoreNooper187     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
188         if (match->first<SaveLayer>()->backdrop) {
189             // can't throw away the layer if we have a backdrop
190             return false;
191         }
192 
193         // A SaveLayer's bounds field is just a hint, so we should be free to ignore it.
194         SkPaint* layerPaint = match->first<SaveLayer>()->paint;
195         SkPaint* drawPaint = match->second<SkPaint>();
196 
197         if (nullptr == layerPaint && effectively_srcover(drawPaint)) {
198             // There wasn't really any point to this SaveLayer at all.
199             return KillSaveLayerAndRestore(record, begin);
200         }
201 
202         if (drawPaint == nullptr) {
203             // We can just give the draw the SaveLayer's paint.
204             // TODO(mtklein): figure out how to do this clearly
205             return false;
206         }
207 
208         if (!fold_opacity_layer_color_to_paint(layerPaint, false /*isSaveLayer*/, drawPaint)) {
209             return false;
210         }
211 
212         return KillSaveLayerAndRestore(record, begin);
213     }
214 
KillSaveLayerAndRestoreSaveLayerDrawRestoreNooper215     static bool KillSaveLayerAndRestore(SkRecord* record, int saveLayerIndex) {
216         record->replace<NoOp>(saveLayerIndex);    // SaveLayer
217         record->replace<NoOp>(saveLayerIndex+2);  // Restore
218         return true;
219     }
220 };
SkRecordNoopSaveLayerDrawRestores(SkRecord * record)221 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
222     SaveLayerDrawRestoreNooper pass;
223     apply(&pass, record);
224 }
225 #endif
226 
227 /* For SVG generated:
228   SaveLayer (non-opaque, typically for CSS opacity)
229     Save
230       ClipRect
231       SaveLayer (typically for SVG filter)
232       Restore
233     Restore
234   Restore
235 */
236 struct SvgOpacityAndFilterLayerMergePass {
237     typedef Pattern<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>,
238                     Is<Restore>, Is<Restore>, Is<Restore>> Match;
239 
onMatchSvgOpacityAndFilterLayerMergePass240     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
241         if (match->first<SaveLayer>()->backdrop) {
242             // can't throw away the layer if we have a backdrop
243             return false;
244         }
245 
246         SkPaint* opacityPaint = match->first<SaveLayer>()->paint;
247         if (nullptr == opacityPaint) {
248             // There wasn't really any point to this SaveLayer at all.
249             return KillSaveLayerAndRestore(record, begin);
250         }
251 
252         // This layer typically contains a filter, but this should work for layers with for other
253         // purposes too.
254         SkPaint* filterLayerPaint = match->fourth<SaveLayer>()->paint;
255         if (filterLayerPaint == nullptr) {
256             // We can just give the inner SaveLayer the paint of the outer SaveLayer.
257             // TODO(mtklein): figure out how to do this clearly
258             return false;
259         }
260 
261         if (!fold_opacity_layer_color_to_paint(opacityPaint, true /*isSaveLayer*/,
262                                                filterLayerPaint)) {
263             return false;
264         }
265 
266         return KillSaveLayerAndRestore(record, begin);
267     }
268 
KillSaveLayerAndRestoreSvgOpacityAndFilterLayerMergePass269     static bool KillSaveLayerAndRestore(SkRecord* record, int saveLayerIndex) {
270         record->replace<NoOp>(saveLayerIndex);     // SaveLayer
271         record->replace<NoOp>(saveLayerIndex + 6); // Restore
272         return true;
273     }
274 };
275 
SkRecordMergeSvgOpacityAndFilterLayers(SkRecord * record)276 void SkRecordMergeSvgOpacityAndFilterLayers(SkRecord* record) {
277     SvgOpacityAndFilterLayerMergePass pass;
278     apply(&pass, record);
279 }
280 
281 ///////////////////////////////////////////////////////////////////////////////////////////////////
282 
SkRecordOptimize(SkRecord * record)283 void SkRecordOptimize(SkRecord* record) {
284     // This might be useful  as a first pass in the future if we want to weed
285     // out junk for other optimization passes.  Right now, nothing needs it,
286     // and the bounding box hierarchy will do the work of skipping no-op
287     // Save-NoDraw-Restore sequences better than we can here.
288     // As there is a known problem with this peephole and drawAnnotation, disable this.
289     // If we want to enable this we must first fix this bug:
290     //     https://bugs.chromium.org/p/skia/issues/detail?id=5548
291 //    SkRecordNoopSaveRestores(record);
292 
293     // Turn off this optimization completely for Android framework
294     // because it makes the following Android CTS test fail:
295     // android.uirendering.cts.testclasses.LayerTests#testSaveLayerClippedWithAlpha
296 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
297     SkRecordNoopSaveLayerDrawRestores(record);
298 #endif
299     SkRecordMergeSvgOpacityAndFilterLayers(record);
300 
301     record->defrag();
302 }
303 
SkRecordOptimize2(SkRecord * record)304 void SkRecordOptimize2(SkRecord* record) {
305     multiple_set_matrices(record);
306     SkRecordNoopSaveRestores(record);
307     // See why we turn this off in SkRecordOptimize above.
308 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
309     SkRecordNoopSaveLayerDrawRestores(record);
310 #endif
311     SkRecordMergeSvgOpacityAndFilterLayers(record);
312 
313     record->defrag();
314 }
315