SkRemote: refactoring

- Cache becomes CachingEncoder that wraps another Encoder
  - Encoders provide IDs
  - syntaxy improvements to Client
  - ID isn't really protocol sensitive.
  - I don't think we need Type::kNone.

No diffs.
https://gold.skia.org/search2?issue=1418863002&unt=true&query=source_type%3Dgm&master=false&include=true

BUG=skia:

Review URL: https://codereview.chromium.org/1418863002
This commit is contained in:
mtklein 2015-10-21 10:46:02 -07:00 committed by Commit bot
parent 0d3f061262
commit b6b8f2727f
4 changed files with 170 additions and 264 deletions

View File

@ -1163,10 +1163,11 @@ Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin
Error ViaRemote::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
SkAutoTDelete<SkRemote::Cache> cache(fCache ? SkRemote::Cache::CreateAlwaysCache()
: SkRemote::Cache::CreateNeverCache());
SkRemote::Server server(canvas);
SkRemote::Client client(cache, &server);
SkAutoTDelete<SkRemote::Encoder> cache(fCache
? SkRemote::Encoder::CreateCachingEncoder(&server)
: nullptr);
SkRemote::Client client(cache.get() ? cache.get() : &server);
return src.draw(&client);
});
}

View File

@ -72,210 +72,141 @@ namespace SkRemote {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
class LookupScope {
class CachingEncoder final : public Encoder {
public:
LookupScope(Cache* cache, Encoder* encoder) : fCache(cache), fEncoder(encoder) {}
~LookupScope() { for (ID id : fToUndefine) { fEncoder->undefine(id); } }
void undefineWhenDone(ID id) { fToUndefine.push_back(id); }
template <typename T>
ID lookup(const T& val) {
ID id;
if (!fCache->lookup(val, &id, this)) {
fEncoder->define(id, val);
}
return id;
}
explicit CachingEncoder(Encoder* wrapped) : fWrapped(wrapped) {}
private:
Cache* fCache;
Encoder* fEncoder;
SkSTArray<4, ID> fToUndefine;
};
Cache* Cache::CreateNeverCache() {
struct NeverCache final : public Cache {
NeverCache()
: fNextMatrix (Type::kMatrix)
, fNextMisc (Type::kMisc)
, fNextPath (Type::kPath)
, fNextStroke (Type::kStroke)
, fNextShader (Type::kShader)
, fNextXfermode(Type::kXfermode)
{}
void cleanup(Encoder*) override {}
static bool Helper(ID* next, ID* id, LookupScope* ls) {
*id = ++(*next);
ls->undefineWhenDone(*id);
return false;
}
bool lookup(const SkMatrix&, ID* id, LookupScope* ls) override {
return Helper(&fNextMatrix, id, ls);
}
bool lookup(const Misc&, ID* id, LookupScope* ls) override {
return Helper(&fNextMisc, id, ls);
}
bool lookup(const SkPath&, ID* id, LookupScope* ls) override {
return Helper(&fNextPath, id, ls);
}
bool lookup(const Stroke&, ID* id, LookupScope* ls) override {
return Helper(&fNextStroke, id, ls);
}
bool lookup(const SkShader* shader, ID* id, LookupScope* ls) override {
if (!shader) {
*id = ID(Type::kShader);
return true; // Null IDs are always defined.
}
return Helper(&fNextShader, id, ls);
}
bool lookup(const SkXfermode* xfermode, ID* id, LookupScope* ls) override {
if (!xfermode) {
*id = ID(Type::kXfermode);
return true; // Null IDs are always defined.
}
return Helper(&fNextXfermode, id, ls);
}
ID fNextMatrix,
fNextMisc,
fNextPath,
fNextStroke,
fNextShader,
fNextXfermode;
};
return new NeverCache;
}
// These can't be declared locally inside AlwaysCache because of the templating. :(
namespace {
template <typename T, typename Map>
static bool always_cache_lookup(const T& val, Map* map, ID* next, ID* id) {
if (const ID* found = map->find(val)) {
*id = *found;
return true;
}
*id = ++(*next);
map->set(val, *id);
return false;
}
struct Undefiner {
struct Undef {
Encoder* fEncoder;
template <typename T>
void operator()(const T&, ID* id) const { fEncoder->undefine(*id); }
};
// Maps const T* -> ID, and refs the key. nullptr always maps to ID(kType).
~CachingEncoder() override {
Undef undef{fWrapped};
fMatrix .foreach(undef);
fMisc .foreach(undef);
fPath .foreach(undef);
fStroke .foreach(undef);
fShader .foreach(undef);
fXfermode.foreach(undef);
}
template <typename Map, typename T>
ID define(Map* map, const T& v) {
if (const ID* id = map->find(v)) {
return *id;
}
ID id = fWrapped->define(v);
map->set(v, id);
return id;
}
ID define(const SkMatrix& v) override { return this->define(&fMatrix, v); }
ID define(const Misc& v) override { return this->define(&fMisc, v); }
ID define(const SkPath& v) override { return this->define(&fPath, v); }
ID define(const Stroke& v) override { return this->define(&fStroke, v); }
ID define(SkShader* v) override { return this->define(&fShader, v); }
ID define(SkXfermode* v) override { return this->define(&fXfermode, v); }
void undefine(ID) override {}
void save() override { fWrapped-> save(); }
void restore() override { fWrapped->restore(); }
void setMatrix(ID matrix) override { fWrapped->setMatrix(matrix); }
void clipPath(ID path, SkRegion::Op op, bool aa) override {
fWrapped->clipPath(path, op, aa);
}
void fillPath(ID path, ID misc, ID shader, ID xfermode) override {
fWrapped->fillPath(path, misc, shader, xfermode);
}
void strokePath(ID path, ID misc, ID shader, ID xfermode, ID stroke) override {
fWrapped->strokePath(path, misc, shader, xfermode, stroke);
}
// Maps const T* -> ID, and refs the key.
template <typename T, Type kType>
class RefKeyMap {
public:
RefKeyMap() {}
~RefKeyMap() { fMap.foreach([](const T* key, ID*) { key->unref(); }); }
~RefKeyMap() { fMap.foreach([](const T* key, ID*) { SkSafeUnref(key); }); }
void set(const T* key, const ID& id) {
SkASSERT(key && id.type() == kType);
fMap.set(SkRef(key), id);
void set(const T* key, ID id) {
SkASSERT(id.type() == kType);
fMap.set(SkSafeRef(key), id);
}
void remove(const T* key) {
SkASSERT(key);
fMap.remove(key);
key->unref();
SkSafeUnref(key);
}
const ID* find(const T* key) const {
static const ID nullID(kType);
return key ? fMap.find(key) : &nullID;
return fMap.find(key);
}
template <typename Fn>
void foreach(const Fn& fn) { fMap.foreach(fn); }
void foreach(const Fn& fn) {
fMap.foreach(fn);
}
private:
SkTHashMap<const T*, ID> fMap;
};
} // namespace
Cache* Cache::CreateAlwaysCache() {
struct AlwaysCache final : public Cache {
AlwaysCache()
: fNextMatrix (Type::kMatrix)
, fNextMisc (Type::kMisc)
, fNextPath (Type::kPath)
, fNextStroke (Type::kStroke)
, fNextShader (Type::kShader)
, fNextXfermode(Type::kXfermode)
{}
SkTHashMap<SkMatrix, ID> fMatrix;
SkTHashMap<Misc, ID, MiscHash> fMisc;
SkTHashMap<SkPath, ID> fPath;
SkTHashMap<Stroke, ID> fStroke;
RefKeyMap<SkShader, Type::kShader> fShader;
RefKeyMap<SkXfermode, Type::kXfermode> fXfermode;
void cleanup(Encoder* encoder) override {
Undefiner undef{encoder};
fMatrix .foreach(undef);
fMisc .foreach(undef);
fPath .foreach(undef);
fStroke .foreach(undef);
fShader .foreach(undef);
fXfermode.foreach(undef);
}
Encoder* fWrapped;
};
bool lookup(const SkMatrix& matrix, ID* id, LookupScope*) override {
return always_cache_lookup(matrix, &fMatrix, &fNextMatrix, id);
}
bool lookup(const Misc& misc, ID* id, LookupScope*) override {
return always_cache_lookup(misc, &fMisc, &fNextMisc, id);
}
bool lookup(const SkPath& path, ID* id, LookupScope*) override {
return always_cache_lookup(path, &fPath, &fNextPath, id);
}
bool lookup(const Stroke& stroke, ID* id, LookupScope*) override {
return always_cache_lookup(stroke, &fStroke, &fNextStroke, id);
}
bool lookup(const SkShader* shader, ID* id, LookupScope*) override {
return always_cache_lookup(shader, &fShader, &fNextShader, id);
}
bool lookup(const SkXfermode* xfermode, ID* id, LookupScope*) override {
return always_cache_lookup(xfermode, &fXfermode, &fNextXfermode, id);
}
SkTHashMap<SkMatrix, ID> fMatrix;
SkTHashMap<Misc, ID, MiscHash> fMisc;
SkTHashMap<SkPath, ID> fPath;
SkTHashMap<Stroke, ID> fStroke;
RefKeyMap<SkShader, Type::kShader> fShader;
RefKeyMap<SkXfermode, Type::kXfermode> fXfermode;
ID fNextMatrix,
fNextMisc,
fNextPath,
fNextStroke,
fNextShader,
fNextXfermode;
};
return new AlwaysCache;
}
Encoder* Encoder::CreateCachingEncoder(Encoder* wrapped) { return new CachingEncoder(wrapped); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
Client::Client(Cache* cache, Encoder* encoder)
// Calls Encoder::define() when created, Encoder::undefine() when destroyed.
class Client::AutoID : ::SkNoncopyable {
public:
template <typename T>
explicit AutoID(Encoder* encoder, const T& val)
: fEncoder(encoder)
, fID(encoder->define(val)) {}
~AutoID() { if (fEncoder) fEncoder->undefine(fID); }
AutoID(AutoID&& o) : fEncoder(o.fEncoder), fID(o.fID) {
o.fEncoder = nullptr;
}
AutoID& operator=(AutoID&&) = delete;
operator ID () const { return fID; }
private:
Encoder* fEncoder;
const ID fID;
};
template <typename T>
Client::AutoID Client::id(const T& val) { return AutoID(fEncoder, val); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
Client::Client(Encoder* encoder)
: SkCanvas(1,1)
, fCache(cache)
, fEncoder(encoder)
{}
Client::~Client() {
fCache->cleanup(fEncoder);
}
void Client::willSave() { fEncoder->save(); }
void Client::didRestore() { fEncoder->restore(); }
void Client::didConcat (const SkMatrix&) { this->didSetMatrix(this->getTotalMatrix()); }
void Client::didSetMatrix(const SkMatrix& matrix) {
LookupScope ls(fCache, fEncoder);
fEncoder->setMatrix(ls.lookup(matrix));
fEncoder->setMatrix(this->id(matrix));
}
void Client::onDrawOval(const SkRect& oval, const SkPaint& paint) {
@ -306,17 +237,16 @@ namespace SkRemote {
}
void Client::onDrawPath(const SkPath& path, const SkPaint& paint) {
LookupScope ls(fCache, fEncoder);
ID p = ls.lookup(path),
m = ls.lookup(Misc::CreateFrom(paint)),
s = ls.lookup(paint.getShader()),
x = ls.lookup(paint.getXfermode());
auto p = this->id(path),
m = this->id(Misc::CreateFrom(paint)),
s = this->id(paint.getShader()),
x = this->id(paint.getXfermode());
if (paint.getStyle() == SkPaint::kFill_Style) {
fEncoder->fillPath(p, m, s, x);
} else {
// TODO: handle kStrokeAndFill_Style
fEncoder->strokePath(p, m, s, x, ls.lookup(Stroke::CreateFrom(paint)));
fEncoder->strokePath(p, m, s, x, this->id(Stroke::CreateFrom(paint)));
}
}
@ -368,20 +298,26 @@ namespace SkRemote {
}
void Client::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
LookupScope ls(fCache, fEncoder);
fEncoder->clipPath(ls.lookup(path), op, edgeStyle == kSoft_ClipEdgeStyle);
fEncoder->clipPath(this->id(path), op, edgeStyle == kSoft_ClipEdgeStyle);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
Server::Server(SkCanvas* canvas) : fCanvas(canvas) {}
void Server::define(ID id, const SkMatrix& v) { fMatrix .set(id, v); }
void Server::define(ID id, const Misc& v) { fMisc .set(id, v); }
void Server::define(ID id, const SkPath& v) { fPath .set(id, v); }
void Server::define(ID id, const Stroke& v) { fStroke .set(id, v); }
void Server::define(ID id, SkShader* v) { fShader .set(id, v); }
void Server::define(ID id, SkXfermode* v) { fXfermode.set(id, v); }
template <typename Map, typename T>
ID Server::define(Type type, Map* map, const T& val) {
ID id(type, fNextID++);
map->set(id, val);
return id;
}
ID Server::define(const SkMatrix& v) { return this->define(Type::kMatrix, &fMatrix, v); }
ID Server::define(const Misc& v) { return this->define(Type::kMisc, &fMisc, v); }
ID Server::define(const SkPath& v) { return this->define(Type::kPath, &fPath, v); }
ID Server::define(const Stroke& v) { return this->define(Type::kStroke, &fStroke, v); }
ID Server::define(SkShader* v) { return this->define(Type::kShader, &fShader, v); }
ID Server::define(SkXfermode* v) { return this->define(Type::kXfermode, &fXfermode, v); }
void Server::undefine(ID id) {
switch(id.type()) {
@ -391,8 +327,6 @@ namespace SkRemote {
case Type::kStroke: return fStroke .remove(id);
case Type::kShader: return fShader .remove(id);
case Type::kXfermode: return fXfermode.remove(id);
case Type::kNone: SkASSERT(false);
};
}

View File

@ -18,7 +18,26 @@
// TODO: document
namespace SkRemote {
// TODO: document
// General purpose identifier. Holds a Type and a 56-bit value.
class ID {
public:
ID() {}
ID(Type type, uint64_t val) {
fVal = (uint64_t)type << 56 | val;
SkASSERT(this->type() == type && this->val() == val);
}
Type type() const { return (Type)(fVal >> 56); }
uint64_t val() const { return fVal & ~((uint64_t)0xFF << 56); }
bool operator==(ID o) const { return fVal == o.fVal; }
private:
uint64_t fVal;
};
// Fields from SkPaint used by stroke, fill, and text draws.
struct Misc {
SkColor fColor;
SkFilterQuality fFilterQuality;
@ -28,7 +47,7 @@ namespace SkRemote {
void applyTo(SkPaint*) const;
};
// TODO: document
// Fields from SkPaint used by stroke draws only.
struct Stroke {
SkScalar fWidth, fMiter;
SkPaint::Cap fCap;
@ -42,12 +61,14 @@ namespace SkRemote {
struct Encoder {
virtual ~Encoder() {}
virtual void define(ID, const SkMatrix&) = 0;
virtual void define(ID, const Misc&) = 0;
virtual void define(ID, const SkPath&) = 0;
virtual void define(ID, const Stroke&) = 0;
virtual void define(ID, SkShader*) = 0;
virtual void define(ID, SkXfermode*) = 0;
static Encoder* CreateCachingEncoder(Encoder*);
virtual ID define(const SkMatrix&) = 0;
virtual ID define(const Misc&) = 0;
virtual ID define(const SkPath&) = 0;
virtual ID define(const Stroke&) = 0;
virtual ID define(SkShader*) = 0;
virtual ID define(SkXfermode*) = 0;
virtual void undefine(ID) = 0;
@ -64,41 +85,17 @@ namespace SkRemote {
virtual void strokePath(ID path, ID misc, ID shader, ID xfermode, ID stroke) = 0;
};
class LookupScope;
// The Cache interface encapsulates the caching logic of the Client.
//
// Each lookup() method must always fill ID* with a valid value,
// but ID may be cached. If so, the lookup() method returns true;
// if not the lookup() method returns false and the Client must
// then define() this ID -> Thing mapping before using the ID.
//
// The Caches may also add IDs to the LookupScope's list of IDs to
// undefine() on destruction. This lets the Cache purge IDs.
struct Cache {
virtual ~Cache() {}
static Cache* CreateNeverCache(); // Never caches anything.
static Cache* CreateAlwaysCache(); // Caches by value (not deep pointer equality).
// TODO: static Cache* CreateDeepCache(); // Caches by deep value.
virtual bool lookup(const SkMatrix&, ID*, LookupScope*) = 0;
virtual bool lookup(const Misc&, ID*, LookupScope*) = 0;
virtual bool lookup(const SkPath&, ID*, LookupScope*) = 0;
virtual bool lookup(const Stroke&, ID*, LookupScope*) = 0;
virtual bool lookup(const SkShader*, ID*, LookupScope*) = 0;
virtual bool lookup(const SkXfermode*, ID*, LookupScope*) = 0;
virtual void cleanup(Encoder*) = 0;
};
// TODO: document
// An SkCanvas that translates to Encoder calls.
class Client final : public SkCanvas {
public:
Client(Cache*, Encoder*);
~Client();
explicit Client(Encoder*);
private:
class AutoID;
template <typename T>
AutoID id(const T&);
void willSave() override;
void didRestore() override;
@ -123,22 +120,21 @@ namespace SkRemote {
void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar,
const SkPaint&) override;
Cache* fCache;
Encoder* fEncoder;
};
// TODO: document
// An Encoder that translates back to SkCanvas calls.
class Server final : public Encoder {
public:
explicit Server(SkCanvas*);
private:
void define(ID, const SkMatrix&) override;
void define(ID, const Misc&) override;
void define(ID, const SkPath&) override;
void define(ID, const Stroke&) override;
void define(ID, SkShader*) override;
void define(ID, SkXfermode*) override;
ID define(const SkMatrix&) override;
ID define(const Misc&) override;
ID define(const SkPath&) override;
ID define(const Stroke&) override;
ID define(SkShader*) override;
ID define(SkXfermode*) override;
void undefine(ID) override;
@ -185,25 +181,22 @@ namespace SkRemote {
template <typename T, Type kType>
class ReffedIDMap {
public:
ReffedIDMap() {
// A null ID always maps to nullptr.
fMap.set(ID(kType), nullptr);
}
ReffedIDMap() {}
~ReffedIDMap() {
// A well-behaved client always cleans up its definitions.
SkASSERT(fMap.count() == 1);
SkASSERT(fMap.count() == 0);
}
void set(const ID& id, T* val) {
SkASSERT(id.type() == kType && val);
fMap.set(id, SkRef(val));
SkASSERT(id.type() == kType);
fMap.set(id, SkSafeRef(val));
}
void remove(const ID& id) {
SkASSERT(id.type() == kType);
T** val = fMap.find(id);
SkASSERT(val && *val);
(*val)->unref();
SkASSERT(val);
SkSafeUnref(*val);
fMap.remove(id);
}
@ -218,6 +211,9 @@ namespace SkRemote {
SkTHashMap<ID, T*> fMap;
};
template <typename Map, typename T>
ID define(Type, Map*, const T&);
IDMap<SkMatrix, Type::kMatrix> fMatrix;
IDMap<Misc , Type::kMisc > fMisc;
IDMap<SkPath , Type::kPath > fPath;
@ -226,6 +222,7 @@ namespace SkRemote {
ReffedIDMap<SkXfermode, Type::kXfermode> fXfermode;
SkCanvas* fCanvas;
uint64_t fNextID = 0;
};
} // namespace SkRemote

View File

@ -15,8 +15,6 @@ namespace SkRemote {
// It is safe to append to this enum without breaking protocol compatibility.
// Resorting, deleting, or inserting anywhere but the end will break compatibility.
enum class Type : uint8_t {
kNone,
kMatrix,
kMisc,
kPath,
@ -25,30 +23,6 @@ namespace SkRemote {
kXfermode,
};
class ID {
public:
explicit ID(Type type = Type::kNone) : fVal((uint64_t)type << 56) {}
ID(Type type, uint64_t val) {
fVal = (uint64_t)type << 56 | val;
SkASSERT(this->type() == type && this->val() == val);
}
Type type() const { return (Type)(fVal >> 56); }
uint64_t val() const { return fVal & ~((uint64_t)0xFF << 56); }
bool operator==(ID o) const { return fVal == o.fVal; }
ID operator++() {
++fVal;
SkASSERT(this->val() != 0); // Overflow is particularly bad as it'd change our Type.
return *this;
}
private:
// High 8 bits hold a Type. Low 56 bits are unique within that Type.
// Any change to this format will break protocol compatibility.
uint64_t fVal;
};
} // namespace SkRemote
#endif//SkRemote_protocol_DEFINED