Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: src/record/SkRecordOpts.cpp

Issue 331573004: Add EXPERIMENTAL_beginRecording() for SkRecord-based recording. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: put back Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/record/SkRecordOpts.h ('k') | src/record/SkRecordPattern.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "SkRecordOpts.h"
9
10 #include "SkRecordPattern.h"
11 #include "SkRecords.h"
12 #include "SkTDArray.h"
13
14 using namespace SkRecords;
15
16 void SkRecordOptimize(SkRecord* record) {
17 // TODO(mtklein): fuse independent optimizations to reduce number of passes?
18 SkRecordNoopCulls(record);
19 SkRecordNoopSaveRestores(record);
20 // TODO(mtklein): figure out why we draw differently and reenable
21 //SkRecordNoopSaveLayerDrawRestores(record);
22
23 SkRecordAnnotateCullingPairs(record);
24 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo undDrawPosTextH.
25 SkRecordBoundDrawPosTextH(record);
26 }
27
28 // Most of the optimizations in this file are pattern-based. These are all defi ned as structs with:
29 // - a Pattern typedef
30 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
31 // which returns true if it made changes and false if not.
32
33 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
34 // It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
35 // record, and [begin,end) span of the commands that matched.
36 template <typename Pass>
37 static bool apply(Pass* pass, SkRecord* record) {
38 typename Pass::Pattern pattern;
39 bool changed = false;
40 unsigned begin, end = 0;
41
42 while (pattern.search(record, &begin, &end)) {
43 changed |= pass->onMatch(record, &pattern, begin, end);
44 }
45 return changed;
46 }
47
48 struct CullNooper {
49 typedef Pattern3<Is<PushCull>, Star<Is<NoOp> >, Is<PopCull> > Pattern;
50
51 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
52 record->replace<NoOp>(begin); // PushCull
53 record->replace<NoOp>(end-1); // PopCull
54 return true;
55 }
56 };
57
58 void SkRecordNoopCulls(SkRecord* record) {
59 CullNooper pass;
60 while (apply(&pass, record));
61 }
62
63 // Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into a ctual NoOps.
64 struct SaveOnlyDrawsRestoreNooper {
65 typedef Pattern3<Is<Save>,
66 Star<Or<Is<NoOp>, IsDraw> >,
67 Is<Restore> >
68 Pattern;
69
70 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
71 record->replace<NoOp>(begin); // Save
72 record->replace<NoOp>(end-1); // Restore
73 return true;
74 }
75 };
76 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
77 struct SaveNoDrawsRestoreNooper {
78 // Star matches greedily, so we also have to exclude Save and Restore.
79 typedef Pattern3<Is<Save>,
80 Star<Not<Or3<Is<Save>,
81 Is<Restore>,
82 IsDraw> > >,
83 Is<Restore> >
84 Pattern;
85
86 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
87 // If restore doesn't revert both matrix and clip, this isn't safe to no op away.
88 if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
89 return false;
90 }
91
92 // The entire span between Save and Restore (inclusively) does nothing.
93 for (unsigned i = begin; i < end; i++) {
94 record->replace<NoOp>(i);
95 }
96 return true;
97 }
98 };
99 void SkRecordNoopSaveRestores(SkRecord* record) {
100 SaveOnlyDrawsRestoreNooper onlyDraws;
101 SaveNoDrawsRestoreNooper noDraws;
102
103 // Run until they stop changing things.
104 while (apply(&onlyDraws, record) || apply(&noDraws, record));
105 }
106
107 // For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
108 // draw, and no-op the SaveLayer and Restore.
109 struct SaveLayerDrawRestoreNooper {
110 typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
111
112 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
113 SaveLayer* saveLayer = pattern->first<SaveLayer>();
114 if (saveLayer->bounds != NULL) {
115 // SaveLayer with bounds is too tricky for us.
116 return false;
117 }
118
119 SkPaint* layerPaint = saveLayer->paint;
120 if (NULL == layerPaint) {
121 // There wasn't really any point to this SaveLayer at all.
122 return KillSaveLayerAndRestore(record, begin);
123 }
124
125 SkPaint* drawPaint = pattern->second<SkPaint>();
126 if (drawPaint == NULL) {
127 // We can just give the draw the SaveLayer's paint.
128 // TODO(mtklein): figure out how to do this clearly
129 return false;
130 }
131
132 const uint32_t layerColor = layerPaint->getColor();
133 const uint32_t drawColor = drawPaint->getColor();
134 if (!IsOnlyAlpha(layerColor) || !IsOpaque(drawColor) ||
135 HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) {
136 // Too fancy for us. Actually, as long as layerColor is just an alp ha
137 // we can blend it into drawColor's alpha; drawColor doesn't strictl y have to be opaque.
138 return false;
139 }
140
141 drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor)));
142 return KillSaveLayerAndRestore(record, begin);
143 }
144
145 static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerInde x) {
146 record->replace<NoOp>(saveLayerIndex); // SaveLayer
147 record->replace<NoOp>(saveLayerIndex+2); // Restore
148 return true;
149 }
150
151 static bool HasAnyEffect(const SkPaint& paint) {
152 return paint.getPathEffect() ||
153 paint.getShader() ||
154 paint.getXfermode() ||
155 paint.getMaskFilter() ||
156 paint.getColorFilter() ||
157 paint.getRasterizer() ||
158 paint.getLooper() ||
159 paint.getImageFilter();
160 }
161
162 static bool IsOpaque(SkColor color) {
163 return SkColorGetA(color) == SK_AlphaOPAQUE;
164 }
165 static bool IsOnlyAlpha(SkColor color) {
166 return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT);
167 }
168 };
169 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
170 SaveLayerDrawRestoreNooper pass;
171 apply(&pass, record);
172 }
173
174
175 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
176 struct StrengthReducer {
177 typedef Pattern1<Is<DrawPosText> > Pattern;
178
179 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
180 SkASSERT(end == begin + 1);
181 DrawPosText* draw = pattern->first<DrawPosText>();
182
183 const unsigned points = draw->paint.countText(draw->text, draw->byteLeng th);
184 if (points == 0) {
185 return false; // No point (ha!).
186 }
187
188 const SkScalar firstY = draw->pos[0].fY;
189 for (unsigned i = 1; i < points; i++) {
190 if (draw->pos[i].fY != firstY) {
191 return false; // Needs full power of DrawPosText.
192 }
193 }
194 // All ys are the same. We can replace DrawPosText with DrawPosTextH.
195
196 // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
197 // We're going to squint and look at that as 2*points SkScalars, [x,y,x, y,x,y,x,y, ...].
198 // Then we'll rearrange things so all the xs are in order up front, clob bering the ys.
199 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNo tSafe);
200 SkScalar* scalars = &draw->pos[0].fX;
201 for (unsigned i = 0; i < 2*points; i += 2) {
202 scalars[i/2] = scalars[i];
203 }
204
205 // Extend lifetime of draw to the end of the loop so we can copy its pai nt.
206 Adopted<DrawPosText> adopted(draw);
207 SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
208 DrawPosTextH,
209 (draw->paint, draw->text, draw->byteLength, scalars , firstY));
210 return true;
211 }
212 };
213 void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
214 StrengthReducer pass;
215 apply(&pass, record);
216 }
217
218 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat ive upper and lower
219 // bounds to use with SkCanvas::quickRejectY.
220 struct TextBounder {
221 typedef Pattern1<Is<DrawPosTextH> > Pattern;
222
223 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
224 SkASSERT(end == begin + 1);
225 DrawPosTextH* draw = pattern->first<DrawPosTextH>();
226
227 // If we're drawing vertical text, none of the checks we're about to do make any sense.
228 // We'll need to call SkPaint::computeFastBounds() later, so bail if tha t's not possible.
229 if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
230 return false;
231 }
232
233 // Rather than checking the top and bottom font metrics, we guess. Actu ally looking up the
234 // top and bottom metrics is slow, and this overapproximation should be good enough.
235 const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
236 SkDEBUGCODE(SkPaint::FontMetrics metrics;)
237 SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
238 SkASSERT(-buffer <= metrics.fTop);
239 SkASSERT(+buffer >= metrics.fBottom);
240
241 // Let the paint adjust the text bounds. We don't care about left and r ight here, so we use
242 // 0 and 1 respectively just so the bounds rectangle isn't empty.
243 SkRect bounds;
244 bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
245 SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
246
247 Adopted<DrawPosTextH> adopted(draw);
248 SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted ),
249 BoundedDrawPosTextH,
250 (&adopted, adjusted.fTop, adjusted.fBottom));
251 return true;
252 }
253 };
254 void SkRecordBoundDrawPosTextH(SkRecord* record) {
255 TextBounder pass;
256 apply(&pass, record);
257 }
258
259 // Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCu ll when the canvas
260 // can quickReject the cull rect.
261 // There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
262 class CullAnnotator {
263 public:
264 // Do nothing to most ops.
265 template <typename T> void operator()(T*) {}
266
267 void operator()(PushCull* push) {
268 Pair pair = { fIndex, push };
269 fPushStack.push(pair);
270 }
271
272 void operator()(PopCull* pop) {
273 Pair push = fPushStack.top();
274 fPushStack.pop();
275
276 SkASSERT(fIndex > push.index);
277 unsigned skip = fIndex - push.index;
278
279 Adopted<PushCull> adopted(push.command);
280 SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopte d),
281 PairedPushCull, (&adopted, skip));
282 }
283
284 void apply(SkRecord* record) {
285 for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
286 fRecord->mutate<void>(fIndex, *this);
287 }
288 }
289
290 private:
291 struct Pair {
292 unsigned index;
293 PushCull* command;
294 };
295
296 SkTDArray<Pair> fPushStack;
297 SkRecord* fRecord;
298 unsigned fIndex;
299 };
300 void SkRecordAnnotateCullingPairs(SkRecord* record) {
301 CullAnnotator pass;
302 pass.apply(record);
303 }
OLDNEW
« no previous file with comments | « src/record/SkRecordOpts.h ('k') | src/record/SkRecordPattern.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698