Modernize SkRecordPattern.h
- Fold Or, Or3, Or4 into one flexible Or. - Fold Pattern1...Pattern7 into one flexible Pattern. - Rename Star Greedy Still fighting with a flexible get<N>() method instead of first, second, third, etc. BUG=skia: Review URL: https://codereview.chromium.org/1465443002
This commit is contained in:
parent
4222e19aea
commit
24e7db8b2e
@ -27,33 +27,33 @@ void SkRecordOptimize(SkRecord* record) {
|
||||
}
|
||||
|
||||
// Most of the optimizations in this file are pattern-based. These are all defined as structs with:
|
||||
// - a Pattern typedef
|
||||
// - a bool onMatch(SkRceord*, Pattern*, int begin, int end) method,
|
||||
// - a Match typedef
|
||||
// - a bool onMatch(SkRceord*, Match*, int begin, int end) method,
|
||||
// which returns true if it made changes and false if not.
|
||||
|
||||
// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
|
||||
// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
|
||||
// It looks for spans which match Pass::Match, and when found calls onMatch() with that pattern,
|
||||
// record, and [begin,end) span of the commands that matched.
|
||||
template <typename Pass>
|
||||
static bool apply(Pass* pass, SkRecord* record) {
|
||||
typename Pass::Pattern pattern;
|
||||
typename Pass::Match match;
|
||||
bool changed = false;
|
||||
int begin, end = 0;
|
||||
|
||||
while (pattern.search(record, &begin, &end)) {
|
||||
changed |= pass->onMatch(record, &pattern, begin, end);
|
||||
while (match.search(record, &begin, &end)) {
|
||||
changed |= pass->onMatch(record, &match, begin, end);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
|
||||
struct SaveOnlyDrawsRestoreNooper {
|
||||
typedef Pattern3<Is<Save>,
|
||||
Star<Or<Is<NoOp>, IsDraw> >,
|
||||
Is<Restore> >
|
||||
Pattern;
|
||||
typedef Pattern<Is<Save>,
|
||||
Greedy<Or<Is<NoOp>, IsDraw>>,
|
||||
Is<Restore>>
|
||||
Match;
|
||||
|
||||
bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
|
||||
bool onMatch(SkRecord* record, Match*, int begin, int end) {
|
||||
record->replace<NoOp>(begin); // Save
|
||||
record->replace<NoOp>(end-1); // Restore
|
||||
return true;
|
||||
@ -119,17 +119,17 @@ static bool fold_opacity_layer_color_to_paint(const SkPaint& layerPaint,
|
||||
|
||||
// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
|
||||
struct SaveNoDrawsRestoreNooper {
|
||||
// Star matches greedily, so we also have to exclude Save and Restore.
|
||||
// Greedy matches greedily, so we also have to exclude Save and Restore.
|
||||
// Nested SaveLayers need to be excluded, or we'll match their Restore!
|
||||
typedef Pattern3<Is<Save>,
|
||||
Star<Not<Or4<Is<Save>,
|
||||
typedef Pattern<Is<Save>,
|
||||
Greedy<Not<Or<Is<Save>,
|
||||
Is<SaveLayer>,
|
||||
Is<Restore>,
|
||||
IsDraw> > >,
|
||||
Is<Restore> >
|
||||
Pattern;
|
||||
IsDraw>>>,
|
||||
Is<Restore>>
|
||||
Match;
|
||||
|
||||
bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
|
||||
bool onMatch(SkRecord* record, Match*, int begin, int end) {
|
||||
// The entire span between Save and Restore (inclusively) does nothing.
|
||||
for (int i = begin; i < end; i++) {
|
||||
record->replace<NoOp>(i);
|
||||
@ -148,17 +148,17 @@ void SkRecordNoopSaveRestores(SkRecord* record) {
|
||||
// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
|
||||
// draw, and no-op the SaveLayer and Restore.
|
||||
struct SaveLayerDrawRestoreNooper {
|
||||
typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
|
||||
typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
|
||||
|
||||
bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
|
||||
bool onMatch(SkRecord* record, Match* match, int begin, int end) {
|
||||
// A SaveLayer's bounds field is just a hint, so we should be free to ignore it.
|
||||
SkPaint* layerPaint = pattern->first<SaveLayer>()->paint;
|
||||
SkPaint* layerPaint = match->first<SaveLayer>()->paint;
|
||||
if (nullptr == layerPaint) {
|
||||
// There wasn't really any point to this SaveLayer at all.
|
||||
return KillSaveLayerAndRestore(record, begin);
|
||||
}
|
||||
|
||||
SkPaint* drawPaint = pattern->second<SkPaint>();
|
||||
SkPaint* drawPaint = match->second<SkPaint>();
|
||||
if (drawPaint == nullptr) {
|
||||
// We can just give the draw the SaveLayer's paint.
|
||||
// TODO(mtklein): figure out how to do this clearly
|
||||
@ -194,11 +194,11 @@ void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
|
||||
Restore
|
||||
*/
|
||||
struct SvgOpacityAndFilterLayerMergePass {
|
||||
typedef Pattern7<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>,
|
||||
Is<Restore>, Is<Restore>, Is<Restore> > Pattern;
|
||||
typedef Pattern<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>,
|
||||
Is<Restore>, Is<Restore>, Is<Restore>> Match;
|
||||
|
||||
bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
|
||||
SkPaint* opacityPaint = pattern->first<SaveLayer>()->paint;
|
||||
bool onMatch(SkRecord* record, Match* match, int begin, int end) {
|
||||
SkPaint* opacityPaint = match->first<SaveLayer>()->paint;
|
||||
if (nullptr == opacityPaint) {
|
||||
// There wasn't really any point to this SaveLayer at all.
|
||||
return KillSaveLayerAndRestore(record, begin);
|
||||
@ -206,7 +206,7 @@ struct SvgOpacityAndFilterLayerMergePass {
|
||||
|
||||
// This layer typically contains a filter, but this should work for layers with for other
|
||||
// purposes too.
|
||||
SkPaint* filterLayerPaint = pattern->fourth<SaveLayer>()->paint;
|
||||
SkPaint* filterLayerPaint = match->fourth<SaveLayer>()->paint;
|
||||
if (filterLayerPaint == nullptr) {
|
||||
// We can just give the inner SaveLayer the paint of the outer SaveLayer.
|
||||
// TODO(mtklein): figure out how to do this clearly
|
||||
|
@ -80,44 +80,48 @@ struct Not {
|
||||
bool operator()(T* ptr) { return !Matcher()(ptr); }
|
||||
};
|
||||
|
||||
// Matches if either of A or B does. Stores nothing.
|
||||
template <typename A, typename B>
|
||||
// Matches if any of First or Rest... does. Stores nothing.
|
||||
template <typename First, typename... Rest>
|
||||
struct Or {
|
||||
template <typename T>
|
||||
bool operator()(T* ptr) { return A()(ptr) || B()(ptr); }
|
||||
bool operator()(T* ptr) { return First()(ptr) || Or<Rest...>()(ptr); }
|
||||
};
|
||||
template <typename First>
|
||||
struct Or<First> {
|
||||
template <typename T>
|
||||
bool operator()(T* ptr) { return First()(ptr); }
|
||||
};
|
||||
|
||||
// Matches if any of A, B or C does. Stores nothing.
|
||||
template <typename A, typename B, typename C>
|
||||
struct Or3 : Or<A, Or<B, C> > {};
|
||||
|
||||
// Matches if any of A, B, C or D does. Stores nothing.
|
||||
template <typename A, typename B, typename C, typename D>
|
||||
struct Or4 : Or<A, Or<B, Or<C, D> > > {};
|
||||
|
||||
// Star is a special matcher that greedily matches Matcher 0 or more times. Stores nothing.
|
||||
// Greedy is a special matcher that greedily matches Matcher 0 or more times. Stores nothing.
|
||||
template <typename Matcher>
|
||||
struct Star {
|
||||
struct Greedy {
|
||||
template <typename T>
|
||||
bool operator()(T* ptr) { return Matcher()(ptr); }
|
||||
};
|
||||
|
||||
// Cons builds a list of Matchers.
|
||||
// It first matches Matcher (something from above), then Pattern (another Cons or Nil).
|
||||
// Pattern matches each of its matchers in order.
|
||||
//
|
||||
// This is the main entry point to pattern matching, and so provides a couple of extra API bits:
|
||||
// - search scans through the record to look for matches;
|
||||
// - first, second, and third return the data stored by their respective matchers in the pattern.
|
||||
//
|
||||
// These Cons build lists analogously to Lisp's "cons". See Pattern# for the "list" equivalent.
|
||||
template <typename Matcher, typename Pattern>
|
||||
class Cons {
|
||||
// - first, second, third, ... return the data stored by their respective matchers in the pattern.
|
||||
|
||||
template <typename... Matchers> class Pattern;
|
||||
|
||||
template <> class Pattern<> {
|
||||
public:
|
||||
// If this pattern matches the SkRecord starting at i,
|
||||
// Bottoms out recursion. Just return whatever i the front decided on.
|
||||
int match(SkRecord*, int i) { return i; }
|
||||
};
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
class Pattern<First, Rest...> {
|
||||
public:
|
||||
// If this pattern matches the SkRecord starting from i,
|
||||
// return the index just past the end of the pattern, otherwise return 0.
|
||||
SK_ALWAYS_INLINE int match(SkRecord* record, int i) {
|
||||
i = this->matchHead(&fHead, record, i);
|
||||
return i == 0 ? 0 : fTail.match(record, i);
|
||||
i = this->matchFirst(&fFirst, record, i);
|
||||
return i > 0 ? fRest.match(record, i) : 0;
|
||||
}
|
||||
|
||||
// Starting from *end, walk through the SkRecord to find the first span matching this pattern.
|
||||
@ -132,31 +136,29 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Once either match or search has succeeded, access the stored data of the first, second,
|
||||
// or third matcher in this pattern. Add as needed for longer patterns.
|
||||
// T is checked statically at compile time; no casting is involved. It's just an API wart.
|
||||
template <typename T> T* first() { return fHead.get(); }
|
||||
template <typename T> T* second() { return fTail.fHead.get(); }
|
||||
template <typename T> T* third() { return fTail.fTail.fHead.get(); }
|
||||
template <typename T> T* fourth() { return fTail.fTail.fTail.fHead.get(); }
|
||||
// TODO: some sort of smart get<i>()
|
||||
template <typename T> T* first() { return fFirst.get(); }
|
||||
template <typename T> T* second() { return fRest.template first<T>(); }
|
||||
template <typename T> T* third() { return fRest.template second<T>(); }
|
||||
template <typename T> T* fourth() { return fRest.template third<T>(); }
|
||||
|
||||
private:
|
||||
// If head isn't a Star, try to match at i once.
|
||||
// If first isn't a Greedy, try to match at i once.
|
||||
template <typename T>
|
||||
int matchHead(T*, SkRecord* record, int i) {
|
||||
int matchFirst(T* first, SkRecord* record, int i) {
|
||||
if (i < record->count()) {
|
||||
if (record->mutate<bool>(i, fHead)) {
|
||||
if (record->mutate<bool>(i, *first)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If head is a Star, walk i until it doesn't match.
|
||||
// If first is a Greedy, walk i until it doesn't match.
|
||||
template <typename T>
|
||||
int matchHead(Star<T>*, SkRecord* record, int i) {
|
||||
int matchFirst(Greedy<T>* first, SkRecord* record, int i) {
|
||||
while (i < record->count()) {
|
||||
if (!record->mutate<bool>(i, fHead)) {
|
||||
if (!record->mutate<bool>(i, *first)) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
@ -164,43 +166,10 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
Matcher fHead;
|
||||
Pattern fTail;
|
||||
|
||||
// All Cons are friends with each other. This lets first, second, and third work.
|
||||
template <typename, typename> friend class Cons;
|
||||
First fFirst;
|
||||
Pattern<Rest...> fRest;
|
||||
};
|
||||
|
||||
// Nil is the end of every pattern Cons chain.
|
||||
struct Nil {
|
||||
// Bottoms out recursion down the fTail chain. Just return whatever i the front decided on.
|
||||
int match(SkRecord*, int i) { return i; }
|
||||
};
|
||||
|
||||
// These Pattern# types are syntax sugar over Cons and Nil, just to help eliminate some of the
|
||||
// template noise. Use these if you can. Feel free to add more for longer patterns.
|
||||
// All types A, B, C, ... are Matchers.
|
||||
template <typename A>
|
||||
struct Pattern1 : Cons<A, Nil> {};
|
||||
|
||||
template <typename A, typename B>
|
||||
struct Pattern2 : Cons<A, Pattern1<B> > {};
|
||||
|
||||
template <typename A, typename B, typename C>
|
||||
struct Pattern3 : Cons<A, Pattern2<B, C> > {};
|
||||
|
||||
template <typename A, typename B, typename C, typename D>
|
||||
struct Pattern4 : Cons<A, Pattern3<B, C, D> > {};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E>
|
||||
struct Pattern5 : Cons<A, Pattern4<B, C, D, E> > {};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E, typename F>
|
||||
struct Pattern6 : Cons<A, Pattern5<B, C, D, E, F> > {};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E, typename F, typename G>
|
||||
struct Pattern7 : Cons<A, Pattern6<B, C, D, E, F, G> > {};
|
||||
|
||||
} // namespace SkRecords
|
||||
|
||||
#endif//SkRecordPattern_DEFINED
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include "SkRecords.h"
|
||||
|
||||
using namespace SkRecords;
|
||||
typedef Pattern3<Is<Save>,
|
||||
Is<ClipRect>,
|
||||
Is<Restore> >
|
||||
typedef Pattern<Is<Save>,
|
||||
Is<ClipRect>,
|
||||
Is<Restore>>
|
||||
SaveClipRectRestore;
|
||||
|
||||
DEF_TEST(RecordPattern_Simple, r) {
|
||||
@ -77,8 +77,8 @@ DEF_TEST(RecordPattern_DontMatchSubsequences, r) {
|
||||
REPORTER_ASSERT(r, !pattern.match(&record, 0));
|
||||
}
|
||||
|
||||
DEF_TEST(RecordPattern_Star, r) {
|
||||
Pattern3<Is<Save>, Star<Is<ClipRect> >, Is<Restore> > pattern;
|
||||
DEF_TEST(RecordPattern_Greedy, r) {
|
||||
Pattern<Is<Save>, Greedy<Is<ClipRect>>, Is<Restore>> pattern;
|
||||
|
||||
SkRecord record;
|
||||
SkRecorder recorder(&record, 1920, 1200);
|
||||
@ -98,11 +98,11 @@ DEF_TEST(RecordPattern_Star, r) {
|
||||
}
|
||||
|
||||
DEF_TEST(RecordPattern_Complex, r) {
|
||||
Pattern3<Is<Save>,
|
||||
Star<Not<Or3<Is<Save>,
|
||||
Pattern<Is<Save>,
|
||||
Greedy<Not<Or<Is<Save>,
|
||||
Is<Restore>,
|
||||
IsDraw> > >,
|
||||
Is<Restore> > pattern;
|
||||
IsDraw>>>,
|
||||
Is<Restore>> pattern;
|
||||
|
||||
SkRecord record;
|
||||
SkRecorder recorder(&record, 1920, 1200);
|
||||
@ -142,7 +142,7 @@ DEF_TEST(RecordPattern_Complex, r) {
|
||||
}
|
||||
|
||||
DEF_TEST(RecordPattern_SaveLayerIsNotADraw, r) {
|
||||
Pattern1<IsDraw> pattern;
|
||||
Pattern<IsDraw> pattern;
|
||||
|
||||
SkRecord record;
|
||||
SkRecorder recorder(&record, 1920, 1200);
|
||||
|
Loading…
Reference in New Issue
Block a user