OLD | NEW |
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 "SkRecords.h" | 10 #include "SkRecords.h" |
11 #include "SkTDArray.h" | 11 #include "SkTDArray.h" |
12 | 12 |
13 void SkRecordOptimize(SkRecord* record) { | 13 void SkRecordOptimize(SkRecord* record) { |
| 14 // TODO(mtklein): fuse independent optimizations to reduce number of passes? |
| 15 SkRecordNoopSaveRestores(record); |
14 SkRecordAnnotateCullingPairs(record); | 16 SkRecordAnnotateCullingPairs(record); |
15 } | 17 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo
undDrawPosTextH. |
| 18 SkRecordBoundDrawPosTextH(record); |
| 19 } |
| 20 |
| 21 // Streamline replacing one command with another. |
| 22 #define REPLACE(record, index, T, ...) \ |
| 23 SkNEW_PLACEMENT_ARGS(record->replace<SkRecords::T>(index), SkRecords::T, (__
VA_ARGS__)) |
16 | 24 |
17 namespace { | 25 namespace { |
18 | 26 |
19 struct Annotator { | 27 // Convenience base class to share some common implementation code. |
20 unsigned index; | 28 class Common : SkNoncopyable { |
21 SkTDArray<SkRecords::PushCull*> pushStack; | 29 public: |
22 | 30 explicit Common(SkRecord* record) : fRecord(record), fIndex(0) {} |
23 // Do nothing to most record types. | 31 |
24 template <typename T> void operator()(T*) {} | 32 unsigned index() const { return fIndex; } |
25 }; | 33 void next() { ++fIndex; } |
26 | 34 |
27 template <> void Annotator::operator()(SkRecords::PushCull* push) { | 35 protected: |
28 // Store the push's index for now. We'll calculate the offset using this in
the paired pop. | 36 SkRecord* fRecord; |
29 push->popOffset = index; | 37 unsigned fIndex; |
30 pushStack.push(push); | 38 }; |
31 } | 39 |
32 | 40 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual
no-ops. |
33 template <> void Annotator::operator()(SkRecords::PopCull* pop) { | 41 // TODO(mtklein): state machine diagram |
34 SkRecords::PushCull* push = pushStack.top(); | 42 class SaveRestoreNooper : public Common { |
35 pushStack.pop(); | 43 public: |
36 | 44 explicit SaveRestoreNooper(SkRecord* record) |
37 SkASSERT(index > push->popOffset); // push->popOffset holds the ind
ex of the push. | 45 : Common(record), fSave(kInactive), fChanged(false) {} |
38 push->popOffset = index - push->popOffset; // Now it's the offset between p
ush and pop. | 46 |
| 47 // Most drawing commands reset to inactive state without nooping anything. |
| 48 template <typename T> |
| 49 void operator()(T*) { fSave = kInactive; } |
| 50 |
| 51 bool changed() const { return fChanged; } |
| 52 |
| 53 private: |
| 54 static const unsigned kInactive = ~0; |
| 55 unsigned fSave; |
| 56 bool fChanged; |
| 57 }; |
| 58 |
| 59 // If the command doesn't draw anything, that doesn't reset the state back to in
active. |
| 60 // TODO(mtklein): do this with some sort of template-based trait mechanism inste
ad of macros |
| 61 #define IGNORE(T) template <> void SaveRestoreNooper::operator()(SkRecords::T*)
{} |
| 62 IGNORE(NoOp) |
| 63 IGNORE(Concat) |
| 64 IGNORE(SetMatrix) |
| 65 IGNORE(ClipRect) |
| 66 IGNORE(ClipRRect) |
| 67 IGNORE(ClipPath) |
| 68 IGNORE(ClipRegion) |
| 69 IGNORE(PairedPushCull) |
| 70 IGNORE(PushCull) |
| 71 IGNORE(PopCull) |
| 72 #undef CLIP |
| 73 |
| 74 template <> |
| 75 void SaveRestoreNooper::operator()(SkRecords::Save* r) { |
| 76 fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInacti
ve; |
| 77 } |
| 78 |
| 79 template <> |
| 80 void SaveRestoreNooper::operator()(SkRecords::Restore* r) { |
| 81 if (fSave != kInactive) { |
| 82 // Remove everything between the save and restore, inclusive on both sid
es. |
| 83 fChanged = true; |
| 84 SkRecord::Destroyer destroyer; |
| 85 for (unsigned i = fSave; i <= this->index(); i++) { |
| 86 fRecord->mutate(i, destroyer); |
| 87 REPLACE(fRecord, i, NoOp); |
| 88 } |
| 89 fSave = kInactive; |
| 90 } |
| 91 } |
| 92 |
| 93 |
| 94 // Tries to replace PushCull with PairedPushCull, which lets us skip to the pair
ed PopCull |
| 95 // when the canvas can quickReject the cull rect. |
| 96 class CullAnnotator : public Common { |
| 97 public: |
| 98 explicit CullAnnotator(SkRecord* record) : Common(record) {} |
| 99 |
| 100 // Do nothing to most ops. |
| 101 template <typename T> |
| 102 void operator()(T*) {} |
| 103 |
| 104 private: |
| 105 struct Pair { |
| 106 unsigned index; |
| 107 SkRecords::PushCull* command; |
| 108 }; |
| 109 |
| 110 SkTDArray<Pair> fPushStack; |
| 111 }; |
| 112 |
| 113 template <> |
| 114 void CullAnnotator::operator()(SkRecords::PushCull* push) { |
| 115 Pair pair = { this->index(), push }; |
| 116 fPushStack.push(pair); |
| 117 } |
| 118 |
| 119 template <> |
| 120 void CullAnnotator::operator()(SkRecords::PopCull* pop) { |
| 121 Pair push = fPushStack.top(); |
| 122 fPushStack.pop(); |
| 123 |
| 124 SkASSERT(this->index() > push.index); |
| 125 unsigned skip = this->index() - push.index; |
| 126 |
| 127 // PairedPushCull adopts push.command. |
| 128 REPLACE(fRecord, push.index, PairedPushCull, push.command, skip); |
| 129 } |
| 130 |
| 131 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. |
| 132 class StrengthReducer : public Common { |
| 133 public: |
| 134 explicit StrengthReducer(SkRecord* record) : Common(record) {} |
| 135 |
| 136 // Do nothing to most ops. |
| 137 template <typename T> |
| 138 void operator()(T*) {} |
| 139 }; |
| 140 |
| 141 template <> |
| 142 void StrengthReducer::operator()(SkRecords::DrawPosText* r) { |
| 143 const unsigned points = r->paint.countText(r->text, r->byteLength); |
| 144 if (points == 0) { |
| 145 // No point (ha!). |
| 146 return; |
| 147 } |
| 148 |
| 149 const SkScalar firstY = r->pos[0].fY; |
| 150 for (unsigned i = 1; i < points; i++) { |
| 151 if (r->pos[i].fY != firstY) { |
| 152 // Needs the full strength of DrawPosText. |
| 153 return; |
| 154 } |
| 155 } |
| 156 // All ys are the same. We can replace DrawPosText with DrawPosTextH. |
| 157 |
| 158 // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ]. |
| 159 // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,
y,x,y, ...]. |
| 160 // Then we'll rearrange things so all the xs are in order up front, clobberi
ng the ys. |
| 161 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSaf
e); |
| 162 SkScalar* scalars = &r->pos[0].fX; |
| 163 for (unsigned i = 0; i < 2*points; i += 2) { |
| 164 scalars[i/2] = scalars[i]; |
| 165 } |
| 166 |
| 167 SkRecord::Destroyer destroyer; |
| 168 fRecord->mutate(this->index(), destroyer); |
| 169 REPLACE(fRecord, this->index(), |
| 170 DrawPosTextH, (char*)r->text, r->byteLength, scalars, firstY, r->pai
nt); |
| 171 } |
| 172 |
| 173 |
| 174 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat
ive upper and lower |
| 175 // bounds to use with SkCanvas::quickRejectY. |
| 176 class TextBounder : public Common { |
| 177 public: |
| 178 explicit TextBounder(SkRecord* record) : Common(record) {} |
| 179 |
| 180 // Do nothing to most ops. |
| 181 template <typename T> |
| 182 void operator()(T*) {} |
| 183 }; |
| 184 |
| 185 template <> |
| 186 void TextBounder::operator()(SkRecords::DrawPosTextH* r) { |
| 187 // If we're drawing vertical text, none of the checks we're about to do make
any sense. |
| 188 // We'll need to call SkPaint::computeFastBounds() later, so bail if that's
not possible. |
| 189 if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { |
| 190 return; |
| 191 } |
| 192 |
| 193 // Rather than checking the top and bottom font metrics, we guess. Actually
looking up the |
| 194 // top and bottom metrics is slow, and this overapproximation should be good
enough. |
| 195 const SkScalar buffer = r->paint.getTextSize() * 1.5f; |
| 196 SkDEBUGCODE(SkPaint::FontMetrics metrics;) |
| 197 SkDEBUGCODE(r->paint.getFontMetrics(&metrics);) |
| 198 SkASSERT(-buffer <= metrics.fTop); |
| 199 SkASSERT(+buffer >= metrics.fBottom); |
| 200 |
| 201 // Let the paint adjust the text bounds. We don't care about left and right
here, so we use |
| 202 // 0 and 1 respectively just so the bounds rectangle isn't empty. |
| 203 SkRect bounds; |
| 204 bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer); |
| 205 SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); |
| 206 |
| 207 // BoundedDrawPosTextH adopts r. |
| 208 REPLACE(fRecord, this->index(), BoundedDrawPosTextH, r, adjusted.fTop, adjus
ted.fBottom); |
| 209 } |
| 210 |
| 211 template <typename Pass> |
| 212 static void run_pass(Pass& pass, SkRecord* record) { |
| 213 for (; pass.index() < record->count(); pass.next()) { |
| 214 record->mutate(pass.index(), pass); |
| 215 } |
39 } | 216 } |
40 | 217 |
41 } // namespace | 218 } // namespace |
42 | 219 |
| 220 |
| 221 void SkRecordNoopSaveRestores(SkRecord* record) { |
| 222 // Run SaveRestoreNooper until it doesn't make any more changes. |
| 223 bool changed; |
| 224 do { |
| 225 SaveRestoreNooper nooper(record); |
| 226 run_pass(nooper, record); |
| 227 changed = nooper.changed(); |
| 228 } while (changed); |
| 229 } |
| 230 |
43 void SkRecordAnnotateCullingPairs(SkRecord* record) { | 231 void SkRecordAnnotateCullingPairs(SkRecord* record) { |
44 Annotator annotator; | 232 CullAnnotator annotator(record); |
45 | 233 run_pass(annotator, record); |
46 for (annotator.index = 0; annotator.index < record->count(); annotator.index
++) { | 234 } |
47 record->mutate(annotator.index, annotator); | 235 |
48 } | 236 void SkRecordReduceDrawPosTextStrength(SkRecord* record) { |
49 } | 237 StrengthReducer reducer(record); |
| 238 run_pass(reducer, record); |
| 239 } |
| 240 |
| 241 void SkRecordBoundDrawPosTextH(SkRecord* record) { |
| 242 TextBounder bounder(record); |
| 243 run_pass(bounder, record); |
| 244 } |
| 245 |
| 246 #undef REPLACE |
OLD | NEW |