| 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 "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 } | |
| OLD | NEW |