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

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

Issue 263063002: Add pattern matchers for SkRecord (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: init pointers Created 6 years, 7 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 | « gyp/tests.gypi ('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
1 /* 1 /*
2 * Copyright 2014 Google Inc. 2 * Copyright 2014 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkRecordOpts.h" 8 #include "SkRecordOpts.h"
9 9
10 #include "SkRecordTraits.h" 10 #include "SkRecordPattern.h"
11 #include "SkRecords.h" 11 #include "SkRecords.h"
12 #include "SkTDArray.h" 12 #include "SkTDArray.h"
13 13
14 using namespace SkRecords;
15
14 void SkRecordOptimize(SkRecord* record) { 16 void SkRecordOptimize(SkRecord* record) {
15 // TODO(mtklein): fuse independent optimizations to reduce number of passes? 17 // TODO(mtklein): fuse independent optimizations to reduce number of passes?
16 SkRecordNoopSaveRestores(record); 18 SkRecordNoopSaveRestores(record);
17 SkRecordAnnotateCullingPairs(record); 19 SkRecordAnnotateCullingPairs(record);
18 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo undDrawPosTextH. 20 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo undDrawPosTextH.
19 SkRecordBoundDrawPosTextH(record); 21 SkRecordBoundDrawPosTextH(record);
20 } 22 }
21 23
22 namespace { 24 // Most of the optimizations in this file are pattern-based. These are all defi ned as structs with:
25 // - a Pattern typedef
26 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
27 // which returns true if it made changes and false if not.
23 28
24 // Convenience base class to share some common implementation code. 29 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
25 class Common : SkNoncopyable { 30 // It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
26 public: 31 // record, and [begin,end) span of the commands that matched.
27 explicit Common(SkRecord* record) : fRecord(record), fIndex(0) {} 32 template <typename Pass>
33 static bool apply(Pass* pass, SkRecord* record) {
34 typename Pass::Pattern pattern;
35 bool changed = false;
36 unsigned begin, end = 0;
28 37
29 unsigned index() const { return fIndex; } 38 while (pattern.search(record, &begin, &end)) {
30 void next() { ++fIndex; } 39 changed |= pass->onMatch(record, &pattern, begin, end);
31 40 }
32 protected: 41 return changed;
33 SkRecord* fRecord; 42 }
34 unsigned fIndex;
35 };
36 43
37 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops. 44 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
38 // TODO(mtklein): state machine diagram 45 struct SaveRestoreNooper {
39 class SaveRestoreNooper : public Common { 46 // Star matches greedily, so we also have to exclude Save and Restore.
40 public: 47 typedef Pattern3<Is<Save>,
41 explicit SaveRestoreNooper(SkRecord* record) 48 Star<Not<Or3<Is<Save>,
42 : Common(record), fSave(kInactive), fChanged(false) {} 49 Is<Restore>,
50 IsDraw> > >,
51 Is<Restore> >
52 Pattern;
43 53
44 // Drawing commands reset state to inactive without nooping. 54 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
45 template <typename T> 55 // If restore doesn't revert both matrix and clip, this isn't safe to no op away.
46 SK_WHEN(SkRecords::IsDraw<T>, void) operator()(T*) { fSave = kInactive; } 56 if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
57 return false;
58 }
47 59
48 // Most non-drawing commands can be ignored. 60 // The entire span between Save and Restore (inclusively) does nothing.
49 template <typename T> 61 for (unsigned i = begin; i < end; i++) {
50 SK_WHEN(!SkRecords::IsDraw<T>, void) operator()(T*) {} 62 record->replace<NoOp>(i);
51 63 }
52 void operator()(SkRecords::Save* r) { 64 return true;
53 fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kIn active;
54 } 65 }
55
56 void operator()(SkRecords::Restore* r) {
57 if (fSave != kInactive) {
58 // Remove everything between the save and restore, inclusive on both sides.
59 fChanged = true;
60 for (unsigned i = fSave; i <= this->index(); i++) {
61 fRecord->replace<SkRecords::NoOp>(i);
62 }
63 fSave = kInactive;
64 }
65 }
66
67 bool changed() const { return fChanged; }
68
69 private:
70 static const unsigned kInactive = ~0;
71 unsigned fSave;
72 bool fChanged;
73 }; 66 };
74 67 void SkRecordNoopSaveRestores(SkRecord* record) {
75 // Tries to replace PushCull with PairedPushCull, which lets us skip to the pair ed PopCull 68 SaveRestoreNooper pass;
76 // when the canvas can quickReject the cull rect. 69 while (apply(&pass, record)); // Run until it stops changing things.
77 class CullAnnotator : public Common { 70 }
78 public:
79 explicit CullAnnotator(SkRecord* record) : Common(record) {}
80
81 // Do nothing to most ops.
82 template <typename T> void operator()(T*) {}
83
84 void operator()(SkRecords::PushCull* push) {
85 Pair pair = { this->index(), push };
86 fPushStack.push(pair);
87 }
88
89 void operator()(SkRecords::PopCull* pop) {
90 Pair push = fPushStack.top();
91 fPushStack.pop();
92
93 SkASSERT(this->index() > push.index);
94 unsigned skip = this->index() - push.index;
95
96 SkRecords::Adopted<SkRecords::PushCull> adopted(push.command);
97 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.in dex, adopted),
98 SkRecords::PairedPushCull, (&adopted, skip));
99 }
100
101 private:
102 struct Pair {
103 unsigned index;
104 SkRecords::PushCull* command;
105 };
106
107 SkTDArray<Pair> fPushStack;
108 };
109 71
110 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. 72 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
111 class StrengthReducer : public Common { 73 struct StrengthReducer {
112 public: 74 typedef Pattern1<Is<DrawPosText> > Pattern;
113 explicit StrengthReducer(SkRecord* record) : Common(record) {}
114 75
115 // Do nothing to most ops. 76 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
116 template <typename T> void operator()(T*) {} 77 SkASSERT(end == begin + 1);
78 DrawPosText* draw = pattern->first<DrawPosText>();
117 79
118 void operator()(SkRecords::DrawPosText* r) { 80 const unsigned points = draw->paint.countText(draw->text, draw->byteLeng th);
119 const unsigned points = r->paint.countText(r->text, r->byteLength);
120 if (points == 0) { 81 if (points == 0) {
121 // No point (ha!). 82 return false; // No point (ha!).
122 return;
123 } 83 }
124 84
125 const SkScalar firstY = r->pos[0].fY; 85 const SkScalar firstY = draw->pos[0].fY;
126 for (unsigned i = 1; i < points; i++) { 86 for (unsigned i = 1; i < points; i++) {
127 if (r->pos[i].fY != firstY) { 87 if (draw->pos[i].fY != firstY) {
128 // Needs the full strength of DrawPosText. 88 return false; // Needs full power of DrawPosText.
129 return;
130 } 89 }
131 } 90 }
132 // All ys are the same. We can replace DrawPosText with DrawPosTextH. 91 // All ys are the same. We can replace DrawPosText with DrawPosTextH.
133 92
134 // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ]. 93 // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
135 // We're going to squint and look at that as 2*points SkScalars, [x,y,x, y,x,y,x,y, ...]. 94 // We're going to squint and look at that as 2*points SkScalars, [x,y,x, y,x,y,x,y, ...].
136 // Then we'll rearrange things so all the xs are in order up front, clob bering the ys. 95 // Then we'll rearrange things so all the xs are in order up front, clob bering the ys.
137 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNo tSafe); 96 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNo tSafe);
138 SkScalar* scalars = &r->pos[0].fX; 97 SkScalar* scalars = &draw->pos[0].fX;
139 for (unsigned i = 0; i < 2*points; i += 2) { 98 for (unsigned i = 0; i < 2*points; i += 2) {
140 scalars[i/2] = scalars[i]; 99 scalars[i/2] = scalars[i];
141 } 100 }
142 101
143 // Extend lifetime of r to the end of the method so we can copy its part s. 102 // Extend lifetime of draw to the end of the loop so we can copy its pai nt.
144 SkRecords::Adopted<SkRecords::DrawPosText> adopted(r); 103 Adopted<DrawPosText> adopted(draw);
145 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->ind ex(), adopted), 104 SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
146 SkRecords::DrawPosTextH, 105 DrawPosTextH,
147 (r->text, r->byteLength, scalars, firstY, r->paint) ); 106 (draw->text, draw->byteLength, scalars, firstY, dra w->paint));
107 return true;
148 } 108 }
149 }; 109 };
110 void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
111 StrengthReducer pass;
112 apply(&pass, record);
113 }
150 114
151 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat ive upper and lower 115 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat ive upper and lower
152 // bounds to use with SkCanvas::quickRejectY. 116 // bounds to use with SkCanvas::quickRejectY.
153 class TextBounder : public Common { 117 struct TextBounder {
154 public: 118 typedef Pattern1<Is<DrawPosTextH> > Pattern;
155 explicit TextBounder(SkRecord* record) : Common(record) {}
156 119
157 // Do nothing to most ops. 120 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en d) {
158 template <typename T> void operator()(T*) {} 121 SkASSERT(end == begin + 1);
122 DrawPosTextH* draw = pattern->first<DrawPosTextH>();
159 123
160 void operator()(SkRecords::DrawPosTextH* r) {
161 // If we're drawing vertical text, none of the checks we're about to do make any sense. 124 // If we're drawing vertical text, none of the checks we're about to do make any sense.
162 // We'll need to call SkPaint::computeFastBounds() later, so bail if tha t's not possible. 125 // We'll need to call SkPaint::computeFastBounds() later, so bail if tha t's not possible.
163 if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { 126 if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
164 return; 127 return false;
165 } 128 }
166 129
167 // Rather than checking the top and bottom font metrics, we guess. Actu ally looking up the 130 // Rather than checking the top and bottom font metrics, we guess. Actu ally looking up the
168 // top and bottom metrics is slow, and this overapproximation should be good enough. 131 // top and bottom metrics is slow, and this overapproximation should be good enough.
169 const SkScalar buffer = r->paint.getTextSize() * 1.5f; 132 const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
170 SkDEBUGCODE(SkPaint::FontMetrics metrics;) 133 SkDEBUGCODE(SkPaint::FontMetrics metrics;)
171 SkDEBUGCODE(r->paint.getFontMetrics(&metrics);) 134 SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
172 SkASSERT(-buffer <= metrics.fTop); 135 SkASSERT(-buffer <= metrics.fTop);
173 SkASSERT(+buffer >= metrics.fBottom); 136 SkASSERT(+buffer >= metrics.fBottom);
174 137
175 // Let the paint adjust the text bounds. We don't care about left and r ight here, so we use 138 // Let the paint adjust the text bounds. We don't care about left and r ight here, so we use
176 // 0 and 1 respectively just so the bounds rectangle isn't empty. 139 // 0 and 1 respectively just so the bounds rectangle isn't empty.
177 SkRect bounds; 140 SkRect bounds;
178 bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer); 141 bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
179 SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); 142 SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
180 143
181 SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r); 144 Adopted<DrawPosTextH> adopted(draw);
182 SkNEW_PLACEMENT_ARGS( 145 SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted ),
183 fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->index(), adopted), 146 BoundedDrawPosTextH,
184 SkRecords::BoundedDrawPosTextH, 147 (&adopted, adjusted.fTop, adjusted.fBottom));
185 (&adopted, adjusted.fTop, adjusted.fBottom)); 148 return true;
186 } 149 }
187 }; 150 };
188 151 void SkRecordBoundDrawPosTextH(SkRecord* record) {
189 152 TextBounder pass;
190 template <typename Pass> 153 apply(&pass, record);
191 static void run_pass(Pass& pass, SkRecord* record) {
192 for (; pass.index() < record->count(); pass.next()) {
193 record->mutate(pass.index(), pass);
194 }
195 } 154 }
196 155
197 } // namespace 156 // Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCu ll when the canvas
157 // can quickReject the cull rect.
158 // There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
159 class CullAnnotator {
160 public:
161 // Do nothing to most ops.
162 template <typename T> void operator()(T*) {}
198 163
164 void operator()(PushCull* push) {
165 Pair pair = { fIndex, push };
166 fPushStack.push(pair);
167 }
199 168
200 void SkRecordNoopSaveRestores(SkRecord* record) { 169 void operator()(PopCull* pop) {
201 // Run SaveRestoreNooper until it doesn't make any more changes. 170 Pair push = fPushStack.top();
202 bool changed; 171 fPushStack.pop();
203 do { 172
204 SaveRestoreNooper nooper(record); 173 SkASSERT(fIndex > push.index);
205 run_pass(nooper, record); 174 unsigned skip = fIndex - push.index;
206 changed = nooper.changed(); 175
207 } while (changed); 176 Adopted<PushCull> adopted(push.command);
177 SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopte d),
178 PairedPushCull, (&adopted, skip));
179 }
180
181 void apply(SkRecord* record) {
182 for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
183 fRecord->mutate(fIndex, *this);
184 }
185 }
186
187 private:
188 struct Pair {
189 unsigned index;
190 PushCull* command;
191 };
192
193 SkTDArray<Pair> fPushStack;
194 SkRecord* fRecord;
195 unsigned fIndex;
196 };
197 void SkRecordAnnotateCullingPairs(SkRecord* record) {
198 CullAnnotator pass;
199 pass.apply(record);
208 } 200 }
209
210 void SkRecordAnnotateCullingPairs(SkRecord* record) {
211 CullAnnotator annotator(record);
212 run_pass(annotator, record);
213 }
214
215 void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
216 StrengthReducer reducer(record);
217 run_pass(reducer, record);
218 }
219
220 void SkRecordBoundDrawPosTextH(SkRecord* record) {
221 TextBounder bounder(record);
222 run_pass(bounder, record);
223 }
OLDNEW
« no previous file with comments | « gyp/tests.gypi ('k') | src/record/SkRecordPattern.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698