fonts: Reland push font remoting.

This relands the following changes:
1) https://skia-review.googlesource.com/c/skia/+/120283
2) https://skia-review.googlesource.com/c/skia/+/125029
3) https://skia-review.googlesource.com/c/skia/+/125140

The original changes had to be reverted due to a memory leak in
SkBaseDevice from SkTextBlobCacheDiffCanvas. This has been addressed by
https://skia-review.googlesource.com/c/skia/+/125160

TBR=herb@google.com

Bug: skia:7515, 831354
Change-Id: I73f4fcb1c397f31bf01553ff48c71ed2d6dd0770
Reviewed-on: https://skia-review.googlesource.com/125326
Commit-Queue: Khusal Sagar <khushalsagar@chromium.org>
Reviewed-by: Khusal Sagar <khushalsagar@chromium.org>
This commit is contained in:
Khushal 2018-05-02 10:29:37 -07:00 committed by Skia Commit-Bot
parent 81afc04b53
commit 38a0843688
9 changed files with 887 additions and 664 deletions

View File

@ -226,6 +226,7 @@ tests_sources = [
"$_tests/SkNxTest.cpp", "$_tests/SkNxTest.cpp",
"$_tests/SkPEGTest.cpp", "$_tests/SkPEGTest.cpp",
"$_tests/SkRasterPipelineTest.cpp", "$_tests/SkRasterPipelineTest.cpp",
"$_tests/SkRemoteGlyphCacheTest.cpp",
"$_tests/SkResourceCacheTest.cpp", "$_tests/SkResourceCacheTest.cpp",
"$_tests/SkSharedMutexTest.cpp", "$_tests/SkSharedMutexTest.cpp",
"$_tests/SkSLErrorTest.cpp", "$_tests/SkSLErrorTest.cpp",

File diff suppressed because it is too large Load Diff

View File

@ -5,192 +5,196 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#ifndef SkRemoteGlyphCache_DEFINED #ifndef SkRemoteGlyphCachePriv_DEFINED
#define SkRemoteGlyphCache_DEFINED #define SkRemoteGlyphCachePriv_DEFINED
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
#include "../private/SkTHash.h"
#include "SkData.h" #include "SkData.h"
#include "SkDescriptor.h"
#include "SkDrawLooper.h" #include "SkDrawLooper.h"
#include "SkGlyphCache.h"
#include "SkMakeUnique.h" #include "SkMakeUnique.h"
#include "SkNoDrawCanvas.h" #include "SkNoDrawCanvas.h"
#include "SkRefCnt.h" #include "SkRefCnt.h"
#include "SkRemoteGlyphCache.h"
#include "SkSerialProcs.h" #include "SkSerialProcs.h"
#include "SkStrikeCache.h"
#include "SkTextBlobRunIterator.h"
#include "SkTHash.h"
#include "SkTypeface.h" #include "SkTypeface.h"
#include "SkTypeface_remote.h"
// The client uses a SkStrikeCacheClientRPC to send and receive data. class Serializer;
using SkStrikeCacheClientRPC = std::function<sk_sp<SkData>(const SkData&)>; class SkDescriptor;
class SkGlyphCache;
struct SkPackedGlyphID;
class SkScalerContextRecDescriptor;
class SkTextBlobRunIterator;
class SkTypefaceProxy;
struct WireTypeface;
class SkScalerContextRecDescriptor { class SkStrikeServer;
public:
SkScalerContextRecDescriptor() {}
explicit SkScalerContextRecDescriptor(const SkScalerContextRec& rec) {
auto desc = reinterpret_cast<SkDescriptor*>(&fDescriptor);
desc->init();
desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
desc->computeChecksum();
SkASSERT(sizeof(fDescriptor) == desc->getLength());
}
explicit SkScalerContextRecDescriptor(const SkDescriptor& desc) struct SkDescriptorMapOperators {
: SkScalerContextRecDescriptor(ExtractRec(desc)) { } size_t operator()(const SkDescriptor* key) const;
bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const;
SkScalerContextRecDescriptor& operator=(const SkScalerContextRecDescriptor& rhs) {
std::memcpy(&fDescriptor, &rhs.fDescriptor, rhs.desc().getLength());
return *this;
}
const SkDescriptor& desc() const {
return *reinterpret_cast<const SkDescriptor*>(&fDescriptor);
}
struct Hash {
uint32_t operator()(SkScalerContextRecDescriptor const& s) const {
return s.desc().getChecksum();
}
};
friend bool operator==(const SkScalerContextRecDescriptor& lhs,
const SkScalerContextRecDescriptor& rhs ) {
return lhs.desc() == rhs.desc();
}
private:
static SkScalerContextRec ExtractRec(const SkDescriptor& desc) {
uint32_t size;
auto recPtr = desc.findEntry(kRec_SkDescriptorTag, &size);
SkScalerContextRec result;
std::memcpy(&result, recPtr, size);
return result;
}
// The system only passes descriptors without effects. That is why it uses a fixed size
// descriptor. storageFor is needed because some of the constructors below are private.
template <typename T>
using storageFor = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
struct {
storageFor<SkDescriptor> dummy1;
storageFor<SkDescriptor::Entry> dummy2;
storageFor<SkScalerContextRec> dummy3;
} fDescriptor;
}; };
class SkStrikeDifferences { template <typename T>
public: using SkDescriptorMap = std::unordered_map<const SkDescriptor*, T, SkDescriptorMapOperators,
SkStrikeDifferences(SkFontID typefaceID, std::unique_ptr<SkDescriptor> desc); SkDescriptorMapOperators>;
void add(uint16_t glyphID, SkIPoint pos);
SkFontID fTypefaceID;
std::unique_ptr<SkDescriptor> fDesc;
std::unique_ptr<SkTHashSet<SkPackedGlyphID>> fGlyphIDs =
skstd::make_unique<SkTHashSet<SkPackedGlyphID>>();
};
class SkStrikeCacheDifferenceSpec { using SkDescriptorSet =
public: std::unordered_set<const SkDescriptor*, SkDescriptorMapOperators, SkDescriptorMapOperators>;
SkStrikeDifferences& findStrikeDifferences(const SkDescriptor& desc, SkFontID typefaceID);
int strikeCount() const { return fDescriptorToDifferencesMap.size(); }
size_t sizeBytes() const;
template <typename PerStrike, typename PerGlyph>
void iterateDifferences(PerStrike perStrike, PerGlyph perGlyph) const;
private: // A SkTextBlobCacheDiffCanvas is used to populate the SkStrikeServer with ops
SkDescriptorMap<SkStrikeDifferences> fDescriptorToDifferencesMap{16}; // which will be serialized and renderered using the SkStrikeClient.
}; class SK_API SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas {
class SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas {
public: public:
SkTextBlobCacheDiffCanvas(int width, int height, SkTextBlobCacheDiffCanvas(int width, int height, const SkMatrix& deviceMatrix,
const SkMatrix& deviceMatrix, const SkSurfaceProps& props, SkStrikeServer* strikeserver);
const SkSurfaceProps& props, ~SkTextBlobCacheDiffCanvas() override;
SkScalerContextFlags flags,
SkStrikeCacheDifferenceSpec* strikeDiffs);
protected: protected:
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; SkCanvas::SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
void onDrawTextBlob( void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override; const SkPaint& paint) override;
private: private:
void processLooper( void processLooper(const SkPoint& position,
const SkPoint& position, const SkTextBlobRunIterator& it,
const SkTextBlobRunIterator& it, const SkPaint& origPaint,
const SkPaint& origPaint, SkDrawLooper* looper);
SkDrawLooper* looper); void processGlyphRun(const SkPoint& position,
const SkTextBlobRunIterator& it,
void processGlyphRun( const SkPaint& runPaint);
const SkPoint& position,
const SkTextBlobRunIterator& it,
const SkPaint& runPaint);
const SkMatrix fDeviceMatrix; const SkMatrix fDeviceMatrix;
const SkSurfaceProps fSurfaceProps; const SkSurfaceProps fSurfaceProps;
const SkScalerContextFlags fScalerContextFlags; SkStrikeServer* const fStrikeServer;
SkStrikeCacheDifferenceSpec* const fStrikeCacheDiff;
}; };
class SkStrikeServer { using SkDiscardableHandleId = uint32_t;
// This class is not thread-safe.
class SK_API SkStrikeServer {
public: public:
SkStrikeServer(); // An interface used by the server to create handles for pinning SkGlyphCache
// entries on the remote client.
class SK_API DiscardableHandleManager {
public:
virtual ~DiscardableHandleManager() {}
// Creates a new *locked* handle and returns a unique ID that can be used to identify
// it on the remote client.
virtual SkDiscardableHandleId createHandle() = 0;
// Returns true if the handle could be successfully locked. The server can
// assume it will remain locked until the next set of serialized entries is
// pulled from the SkStrikeServer.
// If returns false, the cache entry mapped to the handle has been deleted
// on the client. Any subsequent attempts to lock the same handle are not
// allowed.
virtual bool lockHandle(SkDiscardableHandleId) = 0;
// TODO(khushalsagar): Add an API which checks whether a handle is still
// valid without locking, so we can avoid tracking stale handles once they
// have been purged on the remote side.
};
SkStrikeServer(DiscardableHandleManager* discardableHandleManager);
~SkStrikeServer(); ~SkStrikeServer();
// embedding clients call these methods // Serializes the typeface to be remoted using this server.
void serve(const SkData&, std::vector<uint8_t>*); sk_sp<SkData> serializeTypeface(SkTypeface*);
void prepareSerializeProcs(SkSerialProcs* procs); // Serializes the strike data captured using a SkTextBlobCacheDiffCanvas. Any
// handles locked using the DiscardableHandleManager will be assumed to be
// unlocked after this call.
void writeStrikeData(std::vector<uint8_t>* memory);
// mostly called internally by Skia // Methods used internally in skia ------------------------------------------
SkScalerContext* generateScalerContext( class SkGlyphCacheState {
const SkScalerContextRecDescriptor& desc, SkFontID typefaceId); public:
SkGlyphCacheState(std::unique_ptr<SkDescriptor> desc,
SkDiscardableHandleId discardableHandleId);
~SkGlyphCacheState();
void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID);
void writePendingGlyphs(Serializer* serializer);
bool has_pending_glyphs() const { return !fPendingGlyphs.empty(); }
SkDiscardableHandleId discardable_handle_id() const { return fDiscardableHandleId; }
private:
// The set of glyphs cached on the remote client.
SkTHashSet<SkPackedGlyphID> fCachedGlyphs;
// The set of glyphs which has not yet been serialized and sent to the
// remote client.
std::vector<SkPackedGlyphID> fPendingGlyphs;
std::unique_ptr<SkDescriptor> fDesc;
const SkDiscardableHandleId fDiscardableHandleId = -1;
std::unique_ptr<SkScalerContext> fContext;
};
SkGlyphCacheState* getOrCreateCache(SkTypeface*, std::unique_ptr<SkDescriptor>);
private: private:
using DescriptorToContextMap = SkTHashMap<SkScalerContextRecDescriptor, SkDescriptorMap<std::unique_ptr<SkGlyphCacheState>> fRemoteGlyphStateMap;
std::unique_ptr<SkScalerContext>, DiscardableHandleManager* const fDiscardableHandleManager;
SkScalerContextRecDescriptor::Hash>; SkTHashSet<SkFontID> fCachedTypefaces;
sk_sp<SkData> encodeTypeface(SkTypeface* tf); // State cached until the next serialization.
SkDescriptorSet fLockedDescs;
int fOpCount = 0; std::vector<WireTypeface> fTypefacesToSend;
SkTHashMap<SkFontID, sk_sp<SkTypeface>> fTypefaceMap;
DescriptorToContextMap fScalerContextMap;
}; };
class SkStrikeClient { class SK_API SkStrikeClient {
public: public:
SkStrikeClient(SkStrikeCacheClientRPC); // An interface to delete handles that may be pinned by the remote server.
class DiscardableHandleManager : public SkRefCnt {
public:
virtual ~DiscardableHandleManager() {}
// embedding clients call these methods // Returns true if the handle was unlocked and can be safely deleted. Once
void primeStrikeCache(const SkStrikeCacheDifferenceSpec&); // successful, subsequent attempts to delete the same handle are invalid.
void prepareDeserializeProcs(SkDeserialProcs* procs); virtual bool deleteHandle(SkDiscardableHandleId) = 0;
};
// mostly called internally by Skia SkStrikeClient(sk_sp<DiscardableHandleManager>);
void generateFontMetrics( ~SkStrikeClient();
const SkTypefaceProxy&, const SkScalerContextRec&, SkPaint::FontMetrics*);
void generateMetricsAndImage( // Deserializes the typeface previously serialized using the SkStrikeServer. Returns null if the
const SkTypefaceProxy&, const SkScalerContextRec&, SkArenaAlloc*, SkGlyph*); // data is invalid.
bool generatePath( sk_sp<SkTypeface> deserializeTypeface(const void* data, size_t length);
const SkTypefaceProxy&, const SkScalerContextRec&, SkGlyphID glyph, SkPath* path);
SkTypeface* lookupTypeface(SkFontID id); // Deserializes the strike data from a SkStrikeServer. All messages generated
// from a server when serializing the ops must be deserialized before the op
// is rasterized.
// Returns false if the data is invalid.
bool readStrikeData(const volatile void* memory, size_t memorySize);
// TODO: Remove these since we don't support pulling this data on-demand.
void generateFontMetrics(const SkTypefaceProxy& typefaceProxy,
const SkScalerContextRec& rec,
SkPaint::FontMetrics* metrics);
void generateMetricsAndImage(const SkTypefaceProxy& typefaceProxy,
const SkScalerContextRec& rec,
SkArenaAlloc* alloc,
SkGlyph* glyph);
void generatePath(const SkTypefaceProxy& typefaceProxy,
const SkScalerContextRec& rec,
SkGlyphID glyphID,
SkPath* path);
private: private:
sk_sp<SkTypeface> decodeTypeface(const void* buf, size_t len); class DiscardableStrikePinner;
// TODO: Figure out how to manage the entries for the following maps. sk_sp<SkTypeface> addTypeface(const WireTypeface& wire);
SkTHashMap<SkFontID, sk_sp<SkTypefaceProxy>> fMapIdToTypeface;
SkStrikeCacheClientRPC fClientRPC; SkTHashMap<SkFontID, sk_sp<SkTypeface>> fRemoteFontIdToTypeface;
sk_sp<DiscardableHandleManager> fDiscardableHandleManager;
std::vector<uint8_t> fBuffer;
}; };
#endif // SkRemoteGlyphCache_DEFINED #endif // SkRemoteGlyphCachePriv_DEFINED

View File

@ -135,8 +135,8 @@ public:
fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]); fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]);
msg.appendf(" frame %g miter %g format %d join %d cap %d flags %#hx\n", msg.appendf(" frame %g miter %g format %d join %d cap %d flags %#hx\n",
fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags); fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags);
msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n", msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n", fLumBits,
fLumBits, fDeviceGamma, fPaintGamma, fContrast); fDeviceGamma, fPaintGamma, fContrast);
return msg; return msg;
} }

View File

@ -9,6 +9,7 @@
#define SkStrikeCache_DEFINED #define SkStrikeCache_DEFINED
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include "SkDescriptor.h" #include "SkDescriptor.h"
#include "SkSpinlock.h" #include "SkSpinlock.h"
@ -31,24 +32,6 @@ class SkTraceMemoryDump;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
struct SkDescriptorMapOperators {
size_t operator()(const SkDescriptor* key) const {
return key->getChecksum();
}
bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const {
return *lhs == *rhs;
}
};
template <typename T>
using SkDescriptorMap =
std::unordered_map<
const SkDescriptor*,
T,
SkDescriptorMapOperators,
SkDescriptorMapOperators>;
class SkStrikePinner { class SkStrikePinner {
public: public:
virtual ~SkStrikePinner() = default; virtual ~SkStrikePinner() = default;

View File

@ -6,17 +6,15 @@
*/ */
#include "SkTypeface_remote.h" #include "SkTypeface_remote.h"
#include "SkPaint.h"
#include "SkRemoteGlyphCache.h" #include "SkRemoteGlyphCache.h"
SkScalerContextProxy::SkScalerContextProxy( #include "SkPaint.h"
sk_sp<SkTypeface> tf,
const SkScalerContextEffects& effects, SkScalerContextProxy::SkScalerContextProxy(sk_sp<SkTypeface> tf,
const SkDescriptor* desc, const SkScalerContextEffects& effects,
SkStrikeClient* rsc) const SkDescriptor* desc,
: SkScalerContext{std::move(tf), effects, desc} SkStrikeClient* rsc)
, fClient{rsc} {} : SkScalerContext{std::move(tf), effects, desc}, fClient{rsc} {}
unsigned SkScalerContextProxy::generateGlyphCount() { unsigned SkScalerContextProxy::generateGlyphCount() {
SK_ABORT("Should never be called."); SK_ABORT("Should never be called.");
@ -40,7 +38,8 @@ void SkScalerContextProxy::generateImage(const SkGlyph& glyph) {
} }
bool SkScalerContextProxy::generatePath(SkGlyphID glyphID, SkPath* path) { bool SkScalerContextProxy::generatePath(SkGlyphID glyphID, SkPath* path) {
return fClient->generatePath(*this->typefaceProxy(), this->getRec(), glyphID, path); fClient->generatePath(*this->typefaceProxy(), this->getRec(), glyphID, path);
return true;
} }
void SkScalerContextProxy::generateFontMetrics(SkPaint::FontMetrics* metrics) { void SkScalerContextProxy::generateFontMetrics(SkPaint::FontMetrics* metrics) {

View File

@ -21,11 +21,10 @@ class SkTypefaceProxy;
class SkScalerContextProxy : public SkScalerContext { class SkScalerContextProxy : public SkScalerContext {
public: public:
SkScalerContextProxy( SkScalerContextProxy(sk_sp<SkTypeface> tf,
sk_sp<SkTypeface> tf, const SkScalerContextEffects& effects,
const SkScalerContextEffects& effects, const SkDescriptor* desc,
const SkDescriptor* desc, SkStrikeClient* rsc);
SkStrikeClient* rsc);
protected: protected:
unsigned generateGlyphCount() override; unsigned generateGlyphCount() override;
@ -52,16 +51,12 @@ private:
class SkTypefaceProxy : public SkTypeface { class SkTypefaceProxy : public SkTypeface {
public: public:
SkTypefaceProxy( SkTypefaceProxy(SkFontID fontId,
SkFontID fontId, int glyphCount,
int glyphCount, const SkFontStyle& style,
const SkFontStyle& style, bool isFixed,
bool isFixed, SkStrikeClient* rsc)
SkStrikeClient* rsc) : INHERITED{style, false}, fFontId{fontId}, fGlyphCount{glyphCount}, fRsc{rsc} {}
: INHERITED{style, false}
, fFontId{fontId}
, fGlyphCount{glyphCount}
, fRsc{rsc} { }
SkFontID remoteTypefaceID() const {return fFontId;} SkFontID remoteTypefaceID() const {return fFontId;}
int glyphCount() const {return fGlyphCount;} int glyphCount() const {return fGlyphCount;}
static SkTypefaceProxy* DownCast(SkTypeface* typeface) { static SkTypefaceProxy* DownCast(SkTypeface* typeface) {
@ -138,6 +133,8 @@ protected:
private: private:
const SkFontID fFontId; const SkFontID fFontId;
const int fGlyphCount; const int fGlyphCount;
// TODO: Does this need a ref to the strike client? If yes, make it a weak ref.
SkStrikeClient* const fRsc; SkStrikeClient* const fRsc;
typedef SkTypeface INHERITED; typedef SkTypeface INHERITED;

View File

@ -0,0 +1,223 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkGraphics.h"
#include "SkMutex.h"
#include "SkRemoteGlyphCache.h"
#include "SkStrikeCache.h"
#include "SkSurface.h"
#include "SkTextBlob.h"
#include "SkTypeface_remote.h"
#include "Test.h"
class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
public SkStrikeClient::DiscardableHandleManager {
public:
DiscardableManager() = default;
~DiscardableManager() override = default;
// Server implementation.
SkDiscardableHandleId createHandle() override {
// Handles starts as locked.
fLockedHandles.add(++fNextHandleId);
return fNextHandleId;
}
bool lockHandle(SkDiscardableHandleId id) override {
if (id <= fLastDeletedHandleId) return false;
fLockedHandles.add(id);
return true;
}
// Client implementation.
bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; }
void unlockAll() { fLockedHandles.reset(); }
void unlockAndDeleteAll() {
unlockAll();
fLastDeletedHandleId = fNextHandleId;
}
const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; }
SkDiscardableHandleId handleCount() { return fNextHandleId; }
private:
SkDiscardableHandleId fNextHandleId = 0u;
SkDiscardableHandleId fLastDeletedHandleId = 0u;
SkTHashSet<SkDiscardableHandleId> fLockedHandles;
};
sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
SkPaint font;
font.setTypeface(tf);
font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
font.setTextAlign(SkPaint::kLeft_Align);
font.setStyle(SkPaint::kFill_Style);
font.setHinting(SkPaint::kNormal_Hinting);
font.setTextSize(1u);
SkTextBlobBuilder builder;
SkRect bounds = SkRect::MakeWH(10, 10);
const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
SkASSERT(runBuffer.utf8text == nullptr);
SkASSERT(runBuffer.clusters == nullptr);
for (int i = 0; i < glyphCount; i++) {
runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
runBuffer.pos[i] = SkIntToScalar(i);
}
return builder.make();
}
SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) {
auto surface = SkSurface::MakeRasterN32Premul(width, height);
SkPaint paint;
surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint);
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
surface->readPixels(bitmap, 0, 0);
return bitmap;
}
DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
auto server_tf = SkTypeface::MakeDefault();
auto tf_data = server.serializeTypeface(server_tf.get());
auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
REPORTER_ASSERT(reporter, client_tf);
REPORTER_ASSERT(reporter, SkTypefaceProxy::DownCast(client_tf.get())->remoteTypefaceID() ==
server_tf->uniqueID());
}
DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
// Server.
auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
auto serverTfData = server.serializeTypeface(serverTf.get());
int glyphCount = 10;
auto serverBlob = buildTextBlob(serverTf, glyphCount);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
SkPaint paint;
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
std::vector<uint8_t> serverStrikeData;
server.writeStrikeData(&serverStrikeData);
// Client.
auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
REPORTER_ASSERT(reporter,
client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
auto clientBlob = buildTextBlob(clientTf, glyphCount);
SkBitmap expected = RasterBlob(serverBlob, 10, 10);
SkBitmap actual = RasterBlob(clientBlob, 10, 10);
for (int i = 0; i < expected.width(); ++i) {
for (int j = 0; j < expected.height(); ++j) {
REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j));
}
}
}
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
server.serializeTypeface(serverTf.get());
int glyphCount = 10;
auto serverBlob = buildTextBlob(serverTf, glyphCount);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
SkPaint paint;
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
// The strike from the blob should be locked after it has been drawn on the canvas.
REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
// Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
// again.
std::vector<uint8_t> fontData;
server.writeStrikeData(&fontData);
discardableManager->unlockAll();
REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
}
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
server.serializeTypeface(serverTf.get());
int glyphCount = 10;
auto serverBlob = buildTextBlob(serverTf, glyphCount);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
SkPaint paint;
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
// Write the strike data and delete all the handles. Re-analyzing the blob should create new
// handles.
std::vector<uint8_t> fontData;
server.writeStrikeData(&fontData);
discardableManager->unlockAndDeleteAll();
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
printf("HandleCount: %d\n ", discardableManager->handleCount());
REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
}
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
// Server.
auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
auto serverTfData = server.serializeTypeface(serverTf.get());
int glyphCount = 10;
auto serverBlob = buildTextBlob(serverTf, glyphCount);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
SkPaint paint;
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
std::vector<uint8_t> serverStrikeData;
server.writeStrikeData(&serverStrikeData);
// Client.
REPORTER_ASSERT(reporter,
client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
// The cache remains alive until it is pinned in the discardable manager.
SkGraphics::PurgeFontCache();
REPORTER_ASSERT(reporter, !clientTf->unique());
// Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
// clientTf.
discardableManager->unlockAndDeleteAll();
SkGraphics::PurgeFontCache();
REPORTER_ASSERT(reporter, clientTf->unique());
}

View File

@ -16,8 +16,9 @@
#include <thread> #include <thread>
#include <unistd.h> #include <unistd.h>
#include "SkRemoteGlyphCache.h"
#include "SkGraphics.h" #include "SkGraphics.h"
#include "SkRemoteGlyphCache.h"
#include "SkScalerContext.h"
#include "SkSurface.h" #include "SkSurface.h"
static std::string gSkpName; static std::string gSkpName;
@ -25,6 +26,46 @@ static bool gUseGpu = true;
static bool gPurgeFontCaches = true; static bool gPurgeFontCaches = true;
static bool gUseProcess = true; static bool gUseProcess = true;
class ServerDiscardableManager : public SkStrikeServer::DiscardableHandleManager {
public:
ServerDiscardableManager() = default;
~ServerDiscardableManager() override = default;
SkDiscardableHandleId createHandle() override { return ++nextHandleId; }
bool lockHandle(SkDiscardableHandleId handleId) override {
return handleId > lastPurgedHandleId;
}
void purgeAll() { lastPurgedHandleId = nextHandleId; }
private:
SkDiscardableHandleId nextHandleId = 0u;
SkDiscardableHandleId lastPurgedHandleId = 0u;
};
class ClientDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
public:
class ScopedPurgeCache {
public:
ScopedPurgeCache(ClientDiscardableManager* manager) : fManager(manager) {
if (fManager) fManager->allowPurging = true;
}
~ScopedPurgeCache() {
if (fManager) fManager->allowPurging = false;
}
private:
ClientDiscardableManager* fManager;
};
ClientDiscardableManager() = default;
~ClientDiscardableManager() override = default;
bool deleteHandle(SkDiscardableHandleId) override { return allowPurging; }
private:
bool allowPurging = false;
};
static bool write_SkData(int fd, const SkData& data) { static bool write_SkData(int fd, const SkData& data) {
size_t size = data.size(); size_t size = data.size();
ssize_t bytesWritten = ::write(fd, &size, sizeof(size)); ssize_t bytesWritten = ::write(fd, &size, sizeof(size));
@ -43,7 +84,6 @@ static bool write_SkData(int fd, const SkData& data) {
} }
static sk_sp<SkData> read_SkData(int fd) { static sk_sp<SkData> read_SkData(int fd) {
size_t size; size_t size;
ssize_t readSize = ::read(fd, &size, sizeof(size)); ssize_t readSize = ::read(fd, &size, sizeof(size));
if (readSize <= 0) { if (readSize <= 0) {
@ -92,44 +132,56 @@ private:
std::chrono::duration<double> fElapsedSeconds{0.0}; std::chrono::duration<double> fElapsedSeconds{0.0};
}; };
static void build_prime_cache_spec(const SkIRect &bounds, static bool push_font_data(const SkPicture& pic, SkStrikeServer* strikeServer, int writeFd) {
const SkSurfaceProps &props,
const SkPicture &pic,
SkStrikeCacheDifferenceSpec *strikeDifference) {
SkMatrix deviceMatrix = SkMatrix::I(); SkMatrix deviceMatrix = SkMatrix::I();
const SkIRect bounds = pic.cullRect().round();
SkTextBlobCacheDiffCanvas filter( const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
bounds.width(), bounds.height(), deviceMatrix, props, SkTextBlobCacheDiffCanvas filter(bounds.width(), bounds.height(), deviceMatrix, props,
SkScalerContextFlags::kFakeGammaAndBoostContrast, strikeServer);
strikeDifference);
pic.playback(&filter); pic.playback(&filter);
std::vector<uint8_t> fontData;
strikeServer->writeStrikeData(&fontData);
auto data = SkData::MakeWithoutCopy(fontData.data(), fontData.size());
return write_SkData(writeFd, *data);
} }
static void final_draw(std::string outFilename, static void final_draw(std::string outFilename, SkData* picData, SkStrikeClient* client,
SkDeserialProcs* procs, ClientDiscardableManager* discardableManager, int readFd, int writeFd) {
SkData* picData, SkDeserialProcs procs;
SkStrikeClient* client) { auto decode = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
return reinterpret_cast<SkStrikeClient*>(ctx)->deserializeTypeface(data, length);
};
procs.fTypefaceProc = decode;
procs.fTypefaceCtx = client;
auto pic = SkPicture::MakeFromData(picData, procs); auto pic = SkPicture::MakeFromData(picData, &procs);
auto cullRect = pic->cullRect(); auto cullRect = pic->cullRect();
auto r = cullRect.round(); auto r = cullRect.round();
auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height()); auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height());
auto c = s->getCanvas(); auto c = s->getCanvas();
auto picUnderTest = SkPicture::MakeFromData(picData, procs); auto picUnderTest = SkPicture::MakeFromData(picData, &procs);
Timer drawTime; Timer drawTime;
auto randomData = SkData::MakeUninitialized(1u);
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
if (gPurgeFontCaches) { if (gPurgeFontCaches) {
ClientDiscardableManager::ScopedPurgeCache purge(discardableManager);
SkGraphics::PurgeFontCache(); SkGraphics::PurgeFontCache();
SkASSERT(SkGraphics::GetFontCacheUsed() == 0u);
} }
drawTime.start(); drawTime.start();
if (client != nullptr) { if (client != nullptr) {
SkStrikeCacheDifferenceSpec strikeDifference; // Kick the renderer to send us the fonts.
build_prime_cache_spec(r, s->props(), *picUnderTest, &strikeDifference); write_SkData(writeFd, *randomData);
client->primeStrikeCache(strikeDifference); auto fontData = read_SkData(readFd);
if (fontData && !fontData->isEmpty()) {
if (!client->readStrikeData(fontData->data(), fontData->size()))
SK_ABORT("Bad serialization");
}
} }
c->drawPicture(picUnderTest); c->drawPicture(picUnderTest);
drawTime.stop(); drawTime.stop();
@ -150,22 +202,16 @@ static void final_draw(std::string outFilename,
static void gpu(int readFd, int writeFd) { static void gpu(int readFd, int writeFd) {
if (gUseGpu) { if (gUseGpu) {
auto clientRPC = [readFd, writeFd](const SkData& inBuffer) {
write_SkData(writeFd, inBuffer);
return read_SkData(readFd);
};
auto picData = read_SkData(readFd); auto picData = read_SkData(readFd);
if (picData == nullptr) { if (picData == nullptr) {
return; return;
} }
SkStrikeClient client{clientRPC}; sk_sp<ClientDiscardableManager> discardableManager = sk_make_sp<ClientDiscardableManager>();
SkStrikeClient strikeClient(discardableManager);
SkDeserialProcs procs; final_draw("test.png", picData.get(), &strikeClient, discardableManager.get(), readFd,
client.prepareDeserializeProcs(&procs); writeFd);
final_draw("test.png", &procs, picData.get(), &client);
} }
::close(writeFd); ::close(writeFd);
@ -177,7 +223,8 @@ static void gpu(int readFd, int writeFd) {
static int renderer( static int renderer(
const std::string& skpName, int readFd, int writeFd) const std::string& skpName, int readFd, int writeFd)
{ {
SkStrikeServer server{}; ServerDiscardableManager discardableManager;
SkStrikeServer server(&discardableManager);
auto closeAll = [readFd, writeFd]() { auto closeAll = [readFd, writeFd]() {
::close(writeFd); ::close(writeFd);
::close(readFd); ::close(readFd);
@ -186,11 +233,16 @@ static int renderer(
auto skpData = SkData::MakeFromFileName(skpName.c_str()); auto skpData = SkData::MakeFromFileName(skpName.c_str());
std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl; std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl;
SkSerialProcs procs;
sk_sp<SkData> stream; sk_sp<SkData> stream;
if (gUseGpu) { if (gUseGpu) {
auto pic = SkPicture::MakeFromData(skpData.get()); auto pic = SkPicture::MakeFromData(skpData.get());
server.prepareSerializeProcs(&procs); SkSerialProcs procs;
auto encode = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> {
return reinterpret_cast<SkStrikeServer*>(ctx)->serializeTypeface(tf);
};
procs.fTypefaceProc = encode;
procs.fTypefaceCtx = &server;
stream = pic->serialize(&procs); stream = pic->serialize(&procs);
if (!write_SkData(writeFd, *stream)) { if (!write_SkData(writeFd, *stream)) {
@ -198,22 +250,18 @@ static int renderer(
return 1; return 1;
} }
std::vector<uint8_t> tmpBuffer;
while (true) { while (true) {
auto inBuffer = read_SkData(readFd); auto inBuffer = read_SkData(readFd);
if (inBuffer == nullptr) { if (inBuffer == nullptr) {
closeAll(); closeAll();
return 0; return 0;
} }
if (gPurgeFontCaches) discardableManager.purgeAll();
tmpBuffer.clear(); push_font_data(*pic.get(), &server, writeFd);
server.serve(*inBuffer, &tmpBuffer);
auto outBuffer = SkData::MakeWithoutCopy(tmpBuffer.data(), tmpBuffer.size());
write_SkData(writeFd, *outBuffer);
} }
} else { } else {
stream = skpData; stream = skpData;
final_draw("test-correct.png", nullptr, stream.get(), nullptr); final_draw("test-correct.png", stream.get(), nullptr, nullptr, -1, -1);
closeAll(); closeAll();
return 0; return 0;
} }