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:
mtklein 2015-11-19 08:53:27 -08:00 committed by Commit bot
parent 4222e19aea
commit 24e7db8b2e
3 changed files with 76 additions and 107 deletions

View File

@ -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

View File

@ -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

View File

@ -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);