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:
parent
81afc04b53
commit
38a0843688
@ -226,6 +226,7 @@ tests_sources = [
|
||||
"$_tests/SkNxTest.cpp",
|
||||
"$_tests/SkPEGTest.cpp",
|
||||
"$_tests/SkRasterPipelineTest.cpp",
|
||||
"$_tests/SkRemoteGlyphCacheTest.cpp",
|
||||
"$_tests/SkResourceCacheTest.cpp",
|
||||
"$_tests/SkSharedMutexTest.cpp",
|
||||
"$_tests/SkSLErrorTest.cpp",
|
||||
|
@ -9,13 +9,51 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "SkDevice.h"
|
||||
#include "SkFindAndPlaceGlyph.h"
|
||||
#include "SkStrikeCache.h"
|
||||
#include "SkTextBlobRunIterator.h"
|
||||
#include "SkTraceEvent.h"
|
||||
#include "SkTypeface_remote.h"
|
||||
|
||||
static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc,
|
||||
SkFontID font_id,
|
||||
SkAutoDescriptor* ad) {
|
||||
ad->reset(source_desc->getLength());
|
||||
auto* desc = ad->getDesc();
|
||||
desc->init();
|
||||
|
||||
// Rec.
|
||||
{
|
||||
uint32_t size;
|
||||
auto ptr = source_desc->findEntry(kRec_SkDescriptorTag, &size);
|
||||
SkScalerContextRec rec;
|
||||
std::memcpy(&rec, ptr, size);
|
||||
rec.fFontID = font_id;
|
||||
desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
|
||||
}
|
||||
|
||||
// Path effect.
|
||||
{
|
||||
uint32_t size;
|
||||
auto ptr = source_desc->findEntry(kPathEffect_SkDescriptorTag, &size);
|
||||
if (ptr) desc->addEntry(kPathEffect_SkDescriptorTag, size, ptr);
|
||||
}
|
||||
|
||||
// Mask filter.
|
||||
{
|
||||
uint32_t size;
|
||||
auto ptr = source_desc->findEntry(kMaskFilter_SkDescriptorTag, &size);
|
||||
if (ptr) desc->addEntry(kMaskFilter_SkDescriptorTag, size, ptr);
|
||||
}
|
||||
|
||||
desc->computeChecksum();
|
||||
return desc;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class ArraySlice final : public std::tuple<const T*, size_t> {
|
||||
public:
|
||||
@ -26,7 +64,6 @@ public:
|
||||
const T* begin() {
|
||||
return this->data();
|
||||
}
|
||||
|
||||
const T* end() {
|
||||
return &this->data()[this->size()];
|
||||
}
|
||||
@ -46,28 +83,32 @@ private:
|
||||
|
||||
// -- Serializer ----------------------------------------------------------------------------------
|
||||
|
||||
static size_t pad(size_t size, size_t alignment) {
|
||||
return (size + (alignment - 1)) & ~(alignment - 1);
|
||||
}
|
||||
size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); }
|
||||
|
||||
// N.B. pointers are only valid until the next call.
|
||||
class Serializer {
|
||||
public:
|
||||
Serializer(std::vector<uint8_t>* buffer) : fBuffer{buffer} { }
|
||||
|
||||
template <typename T>
|
||||
T* push_back(const T& data) {
|
||||
auto result = allocate(sizeof(T), alignof(T));
|
||||
return new (result) T(data);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* emplace_back(Args&& ... args) {
|
||||
T* emplace(Args&&... args) {
|
||||
auto result = allocate(sizeof(T), alignof(T));
|
||||
return new (result) T{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& data) {
|
||||
T* result = (T*)allocate(sizeof(T), alignof(T));
|
||||
memcpy(result, &data, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocate() {
|
||||
T* result = (T*)allocate(sizeof(T), alignof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
void writeDescriptor(const SkDescriptor& desc) {
|
||||
write(desc.getLength());
|
||||
auto result = allocate(desc.getLength(), alignof(SkDescriptor));
|
||||
memcpy(result, &desc, desc.getLength());
|
||||
}
|
||||
@ -89,85 +130,68 @@ private:
|
||||
};
|
||||
|
||||
// -- Deserializer -------------------------------------------------------------------------------
|
||||
|
||||
// Note that the Deserializer is reading untrusted data, we need to guard against invalid data.
|
||||
class Deserializer {
|
||||
public:
|
||||
Deserializer(const SkData& buffer) : fBuffer{buffer} { }
|
||||
Deserializer(const volatile char* memory, size_t memorySize)
|
||||
: fMemory(memory), fMemorySize(memorySize) {}
|
||||
|
||||
template <typename T>
|
||||
T* read() {
|
||||
size_t padded = pad(fCursor, alignof(T));
|
||||
fCursor = padded + sizeof(T);
|
||||
auto data = (uint8_t*)fBuffer.data();
|
||||
return (T*)&data[padded];
|
||||
bool read(T* val) {
|
||||
auto* result = this->ensureAtLeast(sizeof(T), alignof(T));
|
||||
if (!result) return false;
|
||||
|
||||
memcpy(val, const_cast<const char*>(result), sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
SkDescriptor* readDescriptor() {
|
||||
size_t padded = pad(fCursor, alignof(SkDescriptor));
|
||||
auto data = (uint8_t*)fBuffer.data();
|
||||
SkDescriptor* result = (SkDescriptor*)&data[padded];
|
||||
fCursor = padded + result->getLength();
|
||||
return result;
|
||||
bool readDescriptor(SkAutoDescriptor* ad) {
|
||||
uint32_t desc_length = 0u;
|
||||
if (!read<uint32_t>(&desc_length)) return false;
|
||||
|
||||
auto* result = this->ensureAtLeast(desc_length, alignof(SkDescriptor));
|
||||
if (!result) return false;
|
||||
|
||||
ad->reset(desc_length);
|
||||
memcpy(ad->getDesc(), const_cast<const char*>(result), desc_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArraySlice<T> readArray(int count) {
|
||||
size_t padded = pad(fCursor, alignof(T));
|
||||
size_t size = count * sizeof(T);
|
||||
auto data = (uint8_t*)fBuffer.data();
|
||||
const T* base = (const T*)&data[padded];
|
||||
const T* base = (const T*)this->ensureAtLeast(size, alignof(T));
|
||||
if (!base) return ArraySlice<T>();
|
||||
|
||||
ArraySlice<T> result = ArraySlice<T>{base, (uint32_t)count};
|
||||
fCursor = padded + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t size() {return fCursor;}
|
||||
|
||||
private:
|
||||
const SkData& fBuffer;
|
||||
size_t fCursor{0};
|
||||
const volatile char* ensureAtLeast(size_t size, size_t alignment) {
|
||||
size_t padded = pad(fBytesRead, alignment);
|
||||
|
||||
// Not enough data
|
||||
if (padded + size > fMemorySize) return nullptr;
|
||||
|
||||
auto* result = fMemory + padded;
|
||||
fBytesRead = padded + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Note that we read each piece of memory only once to guard against TOCTOU violations.
|
||||
const volatile char* fMemory;
|
||||
size_t fMemorySize;
|
||||
size_t fBytesRead = 0u;
|
||||
};
|
||||
|
||||
|
||||
// -- SkStrikeCacheDifferenceSpec ------------------------------------------------------------------
|
||||
|
||||
SkStrikeDifferences::SkStrikeDifferences(
|
||||
SkFontID typefaceID, std::unique_ptr<SkDescriptor> desc)
|
||||
: fTypefaceID{typefaceID}
|
||||
, fDesc{std::move(desc)} { }
|
||||
|
||||
void SkStrikeDifferences::add(uint16_t glyphID, SkIPoint pos) {
|
||||
SkPackedGlyphID packedGlyphID{glyphID, pos.x(), pos.y()};
|
||||
fGlyphIDs->add(packedGlyphID);
|
||||
size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const {
|
||||
return key->getChecksum();
|
||||
}
|
||||
|
||||
SkStrikeDifferences& SkStrikeCacheDifferenceSpec::findStrikeDifferences(
|
||||
const SkDescriptor& desc, SkFontID typefaceID)
|
||||
{
|
||||
auto mapIter = fDescriptorToDifferencesMap.find(&desc);
|
||||
if (mapIter == fDescriptorToDifferencesMap.end()) {
|
||||
auto newDesc = desc.copy();
|
||||
auto newDescPtr = newDesc.get();
|
||||
SkStrikeDifferences strikeDiffs{typefaceID, std::move(newDesc)};
|
||||
|
||||
mapIter = fDescriptorToDifferencesMap.emplace_hint(
|
||||
mapIter, newDescPtr, std::move(strikeDiffs));
|
||||
}
|
||||
|
||||
return mapIter->second;
|
||||
}
|
||||
|
||||
template <typename PerStrike, typename PerGlyph>
|
||||
void SkStrikeCacheDifferenceSpec::iterateDifferences(PerStrike perStrike, PerGlyph perGlyph) const {
|
||||
for (auto& i : fDescriptorToDifferencesMap) {
|
||||
auto strikeDiff = &i.second;
|
||||
perStrike(strikeDiff->fTypefaceID,
|
||||
*strikeDiff->fDesc,
|
||||
strikeDiff->fGlyphIDs->count());
|
||||
strikeDiff->fGlyphIDs->foreach([&](SkPackedGlyphID id) {
|
||||
perGlyph(id);
|
||||
});
|
||||
}
|
||||
bool SkDescriptorMapOperators::operator()(const SkDescriptor* lhs,
|
||||
const SkDescriptor* rhs) const {
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
// -- TrackLayerDevice -----------------------------------------------------------------------------
|
||||
@ -182,17 +206,18 @@ public:
|
||||
};
|
||||
|
||||
// -- SkTextBlobCacheDiffCanvas -------------------------------------------------------------------
|
||||
SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(
|
||||
int width, int height,
|
||||
SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height,
|
||||
const SkMatrix& deviceMatrix,
|
||||
const SkSurfaceProps& props,
|
||||
SkScalerContextFlags flags,
|
||||
SkStrikeCacheDifferenceSpec* strikeDiffs)
|
||||
SkStrikeServer* strikeSever)
|
||||
: SkNoDrawCanvas{sk_make_sp<TrackLayerDevice>(SkIRect::MakeWH(width, height), props)}
|
||||
, fDeviceMatrix{deviceMatrix}
|
||||
, fSurfaceProps{props}
|
||||
, fScalerContextFlags{flags}
|
||||
, fStrikeCacheDiff{strikeDiffs} { }
|
||||
, fStrikeServer{strikeSever} {
|
||||
SkASSERT(fStrikeServer);
|
||||
}
|
||||
|
||||
SkTextBlobCacheDiffCanvas::~SkTextBlobCacheDiffCanvas() = default;
|
||||
|
||||
SkCanvas::SaveLayerStrategy SkTextBlobCacheDiffCanvas::getSaveLayerStrategy(
|
||||
const SaveLayerRec&rec)
|
||||
@ -235,6 +260,10 @@ void SkTextBlobCacheDiffCanvas::processLooper(
|
||||
}
|
||||
}
|
||||
|
||||
#define FAIL_AND_RETURN \
|
||||
SkDEBUGFAIL("Failed to process glyph run"); \
|
||||
return;
|
||||
|
||||
void SkTextBlobCacheDiffCanvas::processGlyphRun(
|
||||
const SkPoint& position,
|
||||
const SkTextBlobRunIterator& it,
|
||||
@ -242,20 +271,24 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
|
||||
{
|
||||
|
||||
if (runPaint.getTextEncoding() != SkPaint::TextEncoding::kGlyphID_TextEncoding) {
|
||||
return;
|
||||
TRACE_EVENT0("skia", "kGlyphID_TextEncoding");
|
||||
FAIL_AND_RETURN
|
||||
}
|
||||
|
||||
// All other alignment modes need the glyph advances. Use the slow drawing mode.
|
||||
if (runPaint.getTextAlign() != SkPaint::kLeft_Align) {
|
||||
return;
|
||||
TRACE_EVENT0("skia", "kLeft_Align");
|
||||
FAIL_AND_RETURN
|
||||
}
|
||||
|
||||
using PosFn = SkPoint(*)(int index, const SkScalar* pos);
|
||||
PosFn posFn;
|
||||
switch (it.positioning()) {
|
||||
case SkTextBlob::kDefault_Positioning:
|
||||
case SkTextBlob::kDefault_Positioning: {
|
||||
// Default positioning needs advances. Can't do that.
|
||||
return;
|
||||
TRACE_EVENT0("skia", "kDefault_Positioning");
|
||||
FAIL_AND_RETURN
|
||||
}
|
||||
|
||||
case SkTextBlob::kHorizontal_Positioning:
|
||||
posFn = [](int index, const SkScalar* pos) {
|
||||
@ -278,7 +311,8 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
|
||||
SkMatrix blobMatrix{fDeviceMatrix};
|
||||
blobMatrix.preConcat(this->getTotalMatrix());
|
||||
if (blobMatrix.hasPerspective()) {
|
||||
return;
|
||||
TRACE_EVENT0("skia", "hasPerspective");
|
||||
FAIL_AND_RETURN
|
||||
}
|
||||
blobMatrix.preTranslate(position.x(), position.y());
|
||||
|
||||
@ -315,19 +349,22 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
|
||||
SK_ABORT("Bad matrix.");
|
||||
}
|
||||
|
||||
SkAutoDescriptor ad;
|
||||
SkScalerContextRec rec;
|
||||
SkScalerContextEffects effects;
|
||||
|
||||
// TODO(crbug.com/831354): The typeface proxy on the client does not replicate the
|
||||
// filtering done by the typeface on the server.
|
||||
const bool enableTypefaceFiltering = false;
|
||||
SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix,
|
||||
fScalerContextFlags, &rec, &effects);
|
||||
SkScalerContextFlags::kFakeGammaAndBoostContrast, &rec,
|
||||
&effects, enableTypefaceFiltering);
|
||||
|
||||
auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
|
||||
TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(rec.dump().c_str()));
|
||||
auto desc = SkScalerContext::DescriptorGivenRecAndEffects(rec, effects);
|
||||
auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
|
||||
->getOrCreateCache(runPaint.getTypeface(), std::move(desc));
|
||||
SkASSERT(glyphCacheState);
|
||||
|
||||
auto typefaceID = SkTypefaceProxy::DownCast(runPaint.getTypeface())->remoteTypefaceID();
|
||||
auto& diffs = fStrikeCacheDiff->findStrikeDifferences(*desc, typefaceID);
|
||||
|
||||
auto cache = SkStrikeCache::FindStrikeExclusive(*desc);
|
||||
bool isSubpixel = SkToBool(rec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
|
||||
SkAxisAlignment axisAlignment = SkAxisAlignment::kNone_SkAxisAlignment;
|
||||
if (it.positioning() == SkTextBlob::kHorizontal_Positioning) {
|
||||
@ -342,120 +379,155 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
|
||||
subPixelPos = SkFindAndPlaceGlyph::SubpixelAlignment(axisAlignment, glyphPos);
|
||||
}
|
||||
|
||||
if (cache &&
|
||||
cache->isGlyphCached(glyphs[index], subPixelPos.x(), subPixelPos.y())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
diffs.add(glyphs[index], subPixelPos);
|
||||
glyphCacheState->addGlyph(runPaint.getTypeface(),
|
||||
effects,
|
||||
SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()));
|
||||
}
|
||||
}
|
||||
|
||||
// Op code semantics:
|
||||
// * FontMetrics - (SkFontID, SkDescriptor) -> SkPaint::FontMetrics
|
||||
// * GlyphPath - (SkFontID, SkDescriptor, SkPackedGlyphID) -> SkPath
|
||||
// * GlyphMetricsAndImage - (SkFontID, SkDescriptor, SkPackedGlyphID) -> (SkGlyph, <image bits>)
|
||||
// * PrepopulateCache - StrikeCacheDifferenceSpec -> StrikeCacheDifferenceData
|
||||
|
||||
enum class OpCode : int32_t {
|
||||
kFontMetrics = 0,
|
||||
kGlyphPath = 1,
|
||||
kGlyphMetricsAndImage = 2,
|
||||
kPrepopulateCache = 3,
|
||||
};
|
||||
|
||||
struct StrikeDiffHeader {
|
||||
StrikeDiffHeader() {}
|
||||
StrikeDiffHeader(int strikeCount_) : strikeCount{strikeCount_} {}
|
||||
int strikeCount;
|
||||
};
|
||||
|
||||
struct StrikeSpec {
|
||||
StrikeSpec(SkFontID typefaceID_, uint32_t descLength_, int glyphCount_)
|
||||
StrikeSpec() {}
|
||||
StrikeSpec(SkFontID typefaceID_, size_t glyphCount_, SkDiscardableHandleId discardableHandleId_)
|
||||
: typefaceID{typefaceID_}
|
||||
, descLength{descLength_}
|
||||
, glyphCount{glyphCount_} { }
|
||||
SkFontID typefaceID;
|
||||
uint32_t descLength;
|
||||
int glyphCount;
|
||||
, glyphCount{glyphCount_}
|
||||
, discardableHandleId(discardableHandleId_) {}
|
||||
SkFontID typefaceID = 0u;
|
||||
size_t glyphCount = 0u;
|
||||
SkDiscardableHandleId discardableHandleId = 0u;
|
||||
/* desc */
|
||||
/* n X (glyphs ids) */
|
||||
};
|
||||
|
||||
struct WireTypeface {
|
||||
WireTypeface() = default;
|
||||
WireTypeface(SkFontID typeface_id, int glyph_count, SkFontStyle style, bool is_fixed)
|
||||
: typefaceID(typeface_id), glyphCount(glyph_count), style(style), isFixed(is_fixed) {}
|
||||
|
||||
// std::thread::id thread_id; // TODO:need to figure a good solution
|
||||
SkFontID typefaceID;
|
||||
int glyphCount;
|
||||
SkFontStyle style;
|
||||
bool isFixed;
|
||||
};
|
||||
|
||||
class Op {
|
||||
public:
|
||||
Op(OpCode opCode, SkFontID typefaceId, const SkScalerContextRec& rec)
|
||||
: opCode{opCode}
|
||||
, typefaceId{typefaceId}
|
||||
, descriptor{rec} { }
|
||||
const OpCode opCode;
|
||||
const SkFontID typefaceId;
|
||||
const SkScalerContextRecDescriptor descriptor;
|
||||
union {
|
||||
// kGlyphPath and kGlyphMetricsAndImage
|
||||
SkPackedGlyphID glyphID;
|
||||
// kPrepopulateCache
|
||||
StrikeDiffHeader strikeSpecHeader;
|
||||
};
|
||||
};
|
||||
// SkStrikeServer -----------------------------------------
|
||||
|
||||
size_t SkStrikeCacheDifferenceSpec::sizeBytes() const {
|
||||
size_t sum = sizeof(Op) + sizeof(StrikeDiffHeader);
|
||||
for (auto& pair : fDescriptorToDifferencesMap) {
|
||||
const auto& strike = pair.second;
|
||||
sum += sizeof(StrikeSpec)
|
||||
+ strike.fDesc->getLength()
|
||||
+ strike.fGlyphIDs->count() * sizeof(SkPackedGlyphID);
|
||||
}
|
||||
return sum;
|
||||
SkStrikeServer::SkStrikeServer(DiscardableHandleManager* discardableHandleManager)
|
||||
: fDiscardableHandleManager(discardableHandleManager) {
|
||||
SkASSERT(fDiscardableHandleManager);
|
||||
}
|
||||
|
||||
static void write_strikes_spec(const SkStrikeCacheDifferenceSpec &spec,
|
||||
Serializer* serializer) {
|
||||
serializer->emplace_back<Op>(OpCode::kPrepopulateCache, SkFontID{0}, SkScalerContextRec{});
|
||||
SkStrikeServer::~SkStrikeServer() = default;
|
||||
|
||||
serializer->emplace_back<StrikeDiffHeader>(spec.strikeCount());
|
||||
|
||||
auto perStrike = [serializer](SkFontID typefaceID, const SkDescriptor& desc, int glyphCount) {
|
||||
serializer->emplace_back<StrikeSpec>(typefaceID, desc.getLength(), glyphCount);
|
||||
serializer->writeDescriptor(desc);
|
||||
};
|
||||
|
||||
auto perGlyph = [serializer](SkPackedGlyphID glyphID) {
|
||||
serializer->push_back<SkPackedGlyphID>(glyphID);
|
||||
};
|
||||
|
||||
spec.iterateDifferences(perStrike, perGlyph);
|
||||
sk_sp<SkData> SkStrikeServer::serializeTypeface(SkTypeface* tf) {
|
||||
WireTypeface wire(SkTypeface::UniqueID(tf), tf->countGlyphs(), tf->fontStyle(),
|
||||
tf->isFixedPitch());
|
||||
return SkData::MakeWithCopy(&wire, sizeof(wire));
|
||||
}
|
||||
|
||||
static void read_strikes_spec_write_strikes_data(
|
||||
Deserializer* deserializer, Serializer* serializer, SkStrikeServer* server)
|
||||
{
|
||||
// Don't start because the op started this deserialization.
|
||||
auto header = deserializer->read<StrikeDiffHeader>();
|
||||
serializer->push_back<StrikeDiffHeader>(*header);
|
||||
for (int i = 0; i < header->strikeCount; i++) {
|
||||
auto spec = deserializer->read<StrikeSpec>();
|
||||
auto desc = deserializer->readDescriptor();
|
||||
serializer->push_back<StrikeSpec>(*spec);
|
||||
serializer->writeDescriptor(*desc);
|
||||
SkScalerContextRecDescriptor recDesc{*desc};
|
||||
auto scaler = server->generateScalerContext(recDesc, spec->typefaceID);
|
||||
void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) {
|
||||
if (fLockedDescs.empty() && fTypefacesToSend.empty()) return;
|
||||
|
||||
Serializer serializer(memory);
|
||||
serializer.emplace<size_t>(fTypefacesToSend.size());
|
||||
for (const auto& tf : fTypefacesToSend) serializer.write<WireTypeface>(tf);
|
||||
fTypefacesToSend.clear();
|
||||
|
||||
serializer.emplace<size_t>(fLockedDescs.size());
|
||||
for (const auto* desc : fLockedDescs) {
|
||||
auto it = fRemoteGlyphStateMap.find(desc);
|
||||
SkASSERT(it != fRemoteGlyphStateMap.end());
|
||||
|
||||
// TODO: This is unnecessary, write only the descs which has any glyphs
|
||||
// to send. It was getting awkward to write the size after writing the
|
||||
// descs because the vector reallocs.
|
||||
serializer.emplace<bool>(it->second->has_pending_glyphs());
|
||||
if (!it->second->has_pending_glyphs()) continue;
|
||||
|
||||
it->second->writePendingGlyphs(&serializer);
|
||||
}
|
||||
fLockedDescs.clear();
|
||||
}
|
||||
|
||||
SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
|
||||
SkTypeface* tf, std::unique_ptr<SkDescriptor> desc) {
|
||||
SkASSERT(desc);
|
||||
|
||||
// Already locked.
|
||||
if (fLockedDescs.find(desc.get()) != fLockedDescs.end()) {
|
||||
auto it = fRemoteGlyphStateMap.find(desc.get());
|
||||
SkASSERT(it != fRemoteGlyphStateMap.end());
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// Try to lock.
|
||||
auto it = fRemoteGlyphStateMap.find(desc.get());
|
||||
if (it != fRemoteGlyphStateMap.end()) {
|
||||
bool locked = fDiscardableHandleManager->lockHandle(it->second->discardable_handle_id());
|
||||
if (locked) {
|
||||
fLockedDescs.insert(it->first);
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// If the lock failed, the entry was deleted on the client. Remove our
|
||||
// tracking.
|
||||
fRemoteGlyphStateMap.erase(it);
|
||||
}
|
||||
|
||||
const SkFontID typeface_id = tf->uniqueID();
|
||||
if (!fCachedTypefaces.contains(typeface_id)) {
|
||||
fCachedTypefaces.add(typeface_id);
|
||||
fTypefacesToSend.emplace_back(typeface_id, tf->countGlyphs(), tf->fontStyle(),
|
||||
tf->isFixedPitch());
|
||||
}
|
||||
|
||||
auto* desc_ptr = desc.get();
|
||||
auto new_handle = fDiscardableHandleManager->createHandle();
|
||||
auto cache_state = skstd::make_unique<SkGlyphCacheState>(std::move(desc), new_handle);
|
||||
auto* cache_state_ptr = cache_state.get();
|
||||
|
||||
fLockedDescs.insert(desc_ptr);
|
||||
fRemoteGlyphStateMap[desc_ptr] = std::move(cache_state);
|
||||
return cache_state_ptr;
|
||||
}
|
||||
|
||||
SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(std::unique_ptr<SkDescriptor> desc,
|
||||
uint32_t discardable_handle_id)
|
||||
: fDesc(std::move(desc)), fDiscardableHandleId(discardable_handle_id) {
|
||||
SkASSERT(fDesc);
|
||||
}
|
||||
|
||||
SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default;
|
||||
|
||||
void SkStrikeServer::SkGlyphCacheState::addGlyph(SkTypeface* typeface,
|
||||
const SkScalerContextEffects& effects,
|
||||
SkPackedGlyphID glyph) {
|
||||
// Already cached.
|
||||
if (fCachedGlyphs.contains(glyph)) return;
|
||||
|
||||
// Serialize and cache. Also create the scalar context to use when serializing
|
||||
// this glyph.
|
||||
fCachedGlyphs.add(glyph);
|
||||
fPendingGlyphs.push_back(glyph);
|
||||
if (!fContext) fContext = typeface->createScalerContext(effects, fDesc.get(), false);
|
||||
}
|
||||
|
||||
void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) {
|
||||
// Write the desc.
|
||||
serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fPendingGlyphs.size(),
|
||||
fDiscardableHandleId);
|
||||
serializer->writeDescriptor(*fDesc.get());
|
||||
|
||||
// Write FontMetrics.
|
||||
SkPaint::FontMetrics fontMetrics;
|
||||
scaler->getFontMetrics(&fontMetrics);
|
||||
serializer->push_back<SkPaint::FontMetrics>(fontMetrics);
|
||||
auto glyphIDs = deserializer->readArray<SkPackedGlyphID>(spec->glyphCount);
|
||||
for (auto glyphID : glyphIDs) {
|
||||
auto glyph = serializer->emplace_back<SkGlyph>();
|
||||
fContext->getFontMetrics(&fontMetrics);
|
||||
serializer->write<SkPaint::FontMetrics>(fontMetrics);
|
||||
|
||||
// Write Glyphs.
|
||||
for (const auto& glyphID : fPendingGlyphs) {
|
||||
auto glyph = serializer->emplace<SkGlyph>();
|
||||
glyph->initWithGlyphID(glyphID);
|
||||
scaler->getMetrics(glyph);
|
||||
fContext->getMetrics(glyph);
|
||||
auto imageSize = glyph->computeImageSize();
|
||||
glyph->fPathData = nullptr;
|
||||
glyph->fImage = nullptr;
|
||||
@ -464,270 +536,166 @@ static void read_strikes_spec_write_strikes_data(
|
||||
// Since the allocateArray can move glyph, make one that stays in one place.
|
||||
SkGlyph stationaryGlyph = *glyph;
|
||||
stationaryGlyph.fImage = serializer->allocateArray<uint8_t>(imageSize);
|
||||
scaler->getImage(stationaryGlyph);
|
||||
}
|
||||
}
|
||||
fContext->getImage(stationaryGlyph);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_caches_from_strikes_data(SkStrikeClient *client,
|
||||
Deserializer *deserializer) {
|
||||
auto header = deserializer->read<StrikeDiffHeader>();
|
||||
for (int i = 0; i < header->strikeCount; i++) {
|
||||
auto spec = deserializer->read<StrikeSpec>();
|
||||
auto desc = deserializer->readDescriptor();
|
||||
auto fontMetrics = deserializer->read<SkPaint::FontMetrics>();
|
||||
auto tf = client->lookupTypeface(spec->typefaceID);
|
||||
// Note that we reset the context after serializing pending glyphs since we
|
||||
// don't want to extend the lifetime of the typeface.
|
||||
fPendingGlyphs.clear();
|
||||
fContext.reset();
|
||||
}
|
||||
|
||||
// TODO: implement effects handling.
|
||||
SkScalerContextEffects effects;
|
||||
auto strike = SkStrikeCache::FindStrikeExclusive(*desc);
|
||||
// SkStrikeClient -----------------------------------------
|
||||
|
||||
class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner {
|
||||
public:
|
||||
DiscardableStrikePinner(SkDiscardableHandleId discardableHandleId,
|
||||
sk_sp<DiscardableHandleManager> manager)
|
||||
: fDiscardableHandleId(discardableHandleId), fManager(std::move(manager)) {}
|
||||
|
||||
~DiscardableStrikePinner() override = default;
|
||||
bool canDelete() override { return fManager->deleteHandle(fDiscardableHandleId); }
|
||||
|
||||
private:
|
||||
const SkDiscardableHandleId fDiscardableHandleId;
|
||||
sk_sp<DiscardableHandleManager> fManager;
|
||||
};
|
||||
|
||||
SkStrikeClient::SkStrikeClient(sk_sp<DiscardableHandleManager> discardableManager)
|
||||
: fDiscardableHandleManager(std::move(discardableManager)) {}
|
||||
|
||||
SkStrikeClient::~SkStrikeClient() = default;
|
||||
|
||||
#define READ_FAILURE \
|
||||
{ \
|
||||
SkDEBUGFAIL("Bad serialization"); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySize) {
|
||||
SkASSERT(memorySize != 0u);
|
||||
Deserializer deserializer(static_cast<const volatile char*>(memory), memorySize);
|
||||
|
||||
size_t typefaceSize = 0u;
|
||||
if (!deserializer.read<size_t>(&typefaceSize)) READ_FAILURE
|
||||
|
||||
for (size_t i = 0; i < typefaceSize; ++i) {
|
||||
WireTypeface wire;
|
||||
if (!deserializer.read<WireTypeface>(&wire)) READ_FAILURE
|
||||
|
||||
// TODO(khushalsagar): The typeface no longer needs a reference to the
|
||||
// SkStrikeClient, since all needed glyphs must have been pushed before
|
||||
// raster.
|
||||
addTypeface(wire);
|
||||
}
|
||||
|
||||
size_t strikeCount = 0u;
|
||||
if (!deserializer.read<size_t>(&strikeCount)) READ_FAILURE
|
||||
|
||||
for (size_t i = 0; i < strikeCount; ++i) {
|
||||
bool has_glyphs = false;
|
||||
if (!deserializer.read<bool>(&has_glyphs)) READ_FAILURE
|
||||
|
||||
if (!has_glyphs) continue;
|
||||
|
||||
StrikeSpec spec;
|
||||
if (!deserializer.read<StrikeSpec>(&spec)) READ_FAILURE
|
||||
|
||||
SkAutoDescriptor sourceAd;
|
||||
if (!deserializer.readDescriptor(&sourceAd)) READ_FAILURE
|
||||
|
||||
SkPaint::FontMetrics fontMetrics;
|
||||
if (!deserializer.read<SkPaint::FontMetrics>(&fontMetrics)) READ_FAILURE
|
||||
|
||||
// Get the local typeface from remote fontID.
|
||||
auto* tf = fRemoteFontIdToTypeface.find(spec.typefaceID)->get();
|
||||
// Received strikes for a typeface which doesn't exist.
|
||||
if (!tf) READ_FAILURE
|
||||
|
||||
// Replace the ContextRec in the desc from the server to create the client
|
||||
// side descriptor.
|
||||
// TODO: Can we do this in-place and re-compute checksum? Instead of a complete copy.
|
||||
SkAutoDescriptor ad;
|
||||
auto* client_desc = auto_descriptor_from_desc(sourceAd.getDesc(), tf->uniqueID(), &ad);
|
||||
|
||||
auto strike = SkStrikeCache::FindStrikeExclusive(*client_desc);
|
||||
if (strike == nullptr) {
|
||||
auto scaler = SkStrikeCache::CreateScalerContext(*desc, effects, *tf);
|
||||
strike = SkStrikeCache::CreateStrikeExclusive(*desc, std::move(scaler), fontMetrics);
|
||||
// Note that we don't need to deserialize the effects since we won't be generating any
|
||||
// glyphs here anyway, and the desc is still correct since it includes the serialized
|
||||
// effects.
|
||||
SkScalerContextEffects effects;
|
||||
auto scaler = SkStrikeCache::CreateScalerContext(*client_desc, effects, *tf);
|
||||
strike = SkStrikeCache::CreateStrikeExclusive(
|
||||
*client_desc, std::move(scaler), &fontMetrics,
|
||||
skstd::make_unique<DiscardableStrikePinner>(spec.discardableHandleId,
|
||||
fDiscardableHandleManager));
|
||||
}
|
||||
for (int j = 0; j < spec->glyphCount; j++) {
|
||||
auto glyph = deserializer->read<SkGlyph>();
|
||||
|
||||
for (size_t j = 0; j < spec.glyphCount; j++) {
|
||||
SkGlyph glyph;
|
||||
if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE
|
||||
|
||||
SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID());
|
||||
*allocatedGlyph = glyph;
|
||||
|
||||
ArraySlice<uint8_t> image;
|
||||
auto imageSize = glyph->computeImageSize();
|
||||
auto imageSize = glyph.computeImageSize();
|
||||
if (imageSize != 0) {
|
||||
image = deserializer->readArray<uint8_t>(imageSize);
|
||||
}
|
||||
SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph->getPackedID());
|
||||
*allocatedGlyph = *glyph;
|
||||
image = deserializer.readArray<uint8_t>(imageSize);
|
||||
if (!image.data()) READ_FAILURE
|
||||
allocatedGlyph->allocImage(strike->getAlloc());
|
||||
memcpy(allocatedGlyph->fImage, image.data(), image.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- SkStrikeServer -------------------------------------------------------------------------------
|
||||
SkStrikeServer::SkStrikeServer() { }
|
||||
|
||||
SkStrikeServer::~SkStrikeServer() {
|
||||
printf("Strike server - ops: %d\n", fOpCount);
|
||||
}
|
||||
|
||||
void SkStrikeServer::serve(const SkData& inBuffer, std::vector<uint8_t>* outBuffer) {
|
||||
|
||||
fOpCount += 1;
|
||||
|
||||
Serializer serializer{outBuffer};
|
||||
Deserializer deserializer{inBuffer};
|
||||
Op* op = deserializer.read<Op>();
|
||||
|
||||
switch (op->opCode) {
|
||||
case OpCode::kFontMetrics : {
|
||||
auto scaler = this->generateScalerContext(op->descriptor, op->typefaceId);
|
||||
SkPaint::FontMetrics metrics;
|
||||
scaler->getFontMetrics(&metrics);
|
||||
serializer.push_back<SkPaint::FontMetrics>(metrics);
|
||||
break;
|
||||
}
|
||||
case OpCode::kGlyphPath : {
|
||||
auto sc = this->generateScalerContext(op->descriptor, op->typefaceId);
|
||||
// TODO: check for buffer overflow.
|
||||
SkPath path;
|
||||
if (sc->getPath(op->glyphID, &path)) {
|
||||
size_t pathSize = path.writeToMemory(nullptr);
|
||||
serializer.push_back<size_t>(pathSize);
|
||||
auto pathData = serializer.allocateArray<uint8_t>(pathSize);
|
||||
path.writeToMemory(pathData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::kGlyphMetricsAndImage : {
|
||||
auto scaler = this->generateScalerContext(op->descriptor, op->typefaceId);
|
||||
|
||||
auto glyph = serializer.emplace_back<SkGlyph>();
|
||||
// TODO: check for buffer overflow.
|
||||
glyph->initWithGlyphID(op->glyphID);
|
||||
scaler->getMetrics(glyph);
|
||||
auto imageSize = glyph->computeImageSize();
|
||||
glyph->fPathData = nullptr;
|
||||
glyph->fImage = nullptr;
|
||||
if (imageSize > 0) {
|
||||
// Since the allocateArray can move glyph, make one that stays in one place.
|
||||
SkGlyph stationaryGlyph = *glyph;
|
||||
stationaryGlyph.fImage = serializer.allocateArray<uint8_t>(imageSize);
|
||||
scaler->getImage(stationaryGlyph);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::kPrepopulateCache : {
|
||||
read_strikes_spec_write_strikes_data(
|
||||
&deserializer, &serializer, this);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SK_ABORT("Bad op");
|
||||
}
|
||||
}
|
||||
|
||||
void SkStrikeServer::prepareSerializeProcs(SkSerialProcs* procs) {
|
||||
auto encode = [](SkTypeface* tf, void* ctx) {
|
||||
return reinterpret_cast<SkStrikeServer*>(ctx)->encodeTypeface(tf);
|
||||
};
|
||||
procs->fTypefaceProc = encode;
|
||||
procs->fTypefaceCtx = this;
|
||||
}
|
||||
|
||||
SkScalerContext* SkStrikeServer::generateScalerContext(
|
||||
const SkScalerContextRecDescriptor& desc, SkFontID typefaceId)
|
||||
{
|
||||
|
||||
auto scaler = fScalerContextMap.find(desc);
|
||||
if (scaler == nullptr) {
|
||||
auto typefaceIter = fTypefaceMap.find(typefaceId);
|
||||
if (typefaceIter == nullptr) {
|
||||
// TODO: handle this with some future fallback strategy.
|
||||
SK_ABORT("unknown type face");
|
||||
// Should never happen
|
||||
return nullptr;
|
||||
}
|
||||
auto tf = typefaceIter->get();
|
||||
// TODO: make effects really work.
|
||||
SkScalerContextEffects effects;
|
||||
auto mapSc = tf->createScalerContext(effects, &desc.desc(), false);
|
||||
scaler = fScalerContextMap.set(desc, std::move(mapSc));
|
||||
}
|
||||
return scaler->get();
|
||||
}
|
||||
|
||||
sk_sp<SkData> SkStrikeServer::encodeTypeface(SkTypeface* tf) {
|
||||
WireTypeface wire = {
|
||||
SkTypeface::UniqueID(tf),
|
||||
tf->countGlyphs(),
|
||||
tf->fontStyle(),
|
||||
tf->isFixedPitch()
|
||||
};
|
||||
auto typeFace = fTypefaceMap.find(SkTypeface::UniqueID(tf));
|
||||
if (typeFace == nullptr) {
|
||||
fTypefaceMap.set(SkTypeface::UniqueID(tf), sk_ref_sp(tf));
|
||||
}
|
||||
// Can this be done with no copy?
|
||||
return SkData::MakeWithCopy(&wire, sizeof(wire));
|
||||
}
|
||||
|
||||
// -- SkStrikeClient -------------------------------------------------------------------------------
|
||||
SkStrikeClient::SkStrikeClient(SkStrikeCacheClientRPC clientRPC)
|
||||
: fClientRPC{clientRPC} { }
|
||||
|
||||
void SkStrikeClient::generateFontMetrics(
|
||||
const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkPaint::FontMetrics* metrics)
|
||||
{
|
||||
fBuffer.clear();
|
||||
|
||||
Serializer serializer{&fBuffer};
|
||||
serializer.emplace_back<Op>(OpCode::kFontMetrics, typefaceProxy.remoteTypefaceID(), rec);
|
||||
|
||||
auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size());
|
||||
auto inbuffer = fClientRPC(*outBuffer);
|
||||
Deserializer deserializer(*inbuffer);
|
||||
*metrics = *deserializer.read<SkPaint::FontMetrics>();
|
||||
}
|
||||
|
||||
void SkStrikeClient::generateMetricsAndImage(
|
||||
const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkArenaAlloc* alloc,
|
||||
SkGlyph* glyph)
|
||||
{
|
||||
fBuffer.clear();
|
||||
Serializer serializer(&fBuffer);
|
||||
Op *op = serializer.emplace_back<Op>(
|
||||
OpCode::kGlyphMetricsAndImage, typefaceProxy.remoteTypefaceID(), rec);
|
||||
op->glyphID = glyph->getPackedID();
|
||||
|
||||
auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size());
|
||||
auto inbuffer = fClientRPC(*outBuffer);
|
||||
Deserializer deserializer(*inbuffer);
|
||||
*glyph = *deserializer.read<SkGlyph>();
|
||||
auto imageSize = glyph->computeImageSize();
|
||||
glyph->fPathData = nullptr;
|
||||
glyph->fImage = nullptr;
|
||||
if (imageSize > 0) {
|
||||
auto image = deserializer.readArray<uint8_t>(imageSize);
|
||||
SkASSERT(imageSize == image.size());
|
||||
glyph->allocImage(alloc);
|
||||
memcpy(glyph->fImage, image.data(), imageSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool SkStrikeClient::generatePath(
|
||||
const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkGlyphID glyphID,
|
||||
SkPath* path)
|
||||
{
|
||||
fBuffer.clear();
|
||||
|
||||
Serializer serializer{&fBuffer};
|
||||
Op *op = serializer.emplace_back<Op>(
|
||||
OpCode::kGlyphPath, typefaceProxy.remoteTypefaceID(), rec);
|
||||
op->glyphID = glyphID;
|
||||
|
||||
auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size());
|
||||
auto inbuffer = fClientRPC(*outBuffer);
|
||||
Deserializer deserializer(*inbuffer);
|
||||
size_t pathSize = *deserializer.read<size_t>();
|
||||
if (pathSize == 0) {
|
||||
return false;
|
||||
}
|
||||
auto rawPath = deserializer.readArray<uint8_t>(pathSize);
|
||||
path->readFromMemory(rawPath.data(), rawPath.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkStrikeClient::primeStrikeCache(const SkStrikeCacheDifferenceSpec& strikeDifferences) {
|
||||
fBuffer.clear();
|
||||
fBuffer.reserve(strikeDifferences.sizeBytes());
|
||||
|
||||
Serializer serializer{&fBuffer};
|
||||
write_strikes_spec(strikeDifferences, &serializer);
|
||||
|
||||
auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size());
|
||||
auto inbuffer = fClientRPC(*outBuffer);
|
||||
Deserializer deserializer(*inbuffer);
|
||||
update_caches_from_strikes_data(this, &deserializer);
|
||||
}
|
||||
|
||||
void SkStrikeClient::prepareDeserializeProcs(SkDeserialProcs* procs) {
|
||||
auto decode = [](const void* buf, size_t len, void* ctx) {
|
||||
return reinterpret_cast<SkStrikeClient*>(ctx)->decodeTypeface(buf, len);
|
||||
};
|
||||
procs->fTypefaceProc = decode;
|
||||
procs->fTypefaceCtx = this;
|
||||
|
||||
}
|
||||
|
||||
SkTypeface* SkStrikeClient::lookupTypeface(SkFontID id) {
|
||||
auto typeface = fMapIdToTypeface.find(id);
|
||||
SkASSERT(typeface != nullptr);
|
||||
return typeface->get();
|
||||
}
|
||||
|
||||
sk_sp<SkTypeface> SkStrikeClient::decodeTypeface(const void* buf, size_t len) {
|
||||
sk_sp<SkTypeface> SkStrikeClient::deserializeTypeface(const void* buf, size_t len) {
|
||||
WireTypeface wire;
|
||||
if (len < sizeof(wire)) {
|
||||
SK_ABORT("Incomplete transfer");
|
||||
return nullptr;
|
||||
}
|
||||
if (len != sizeof(wire)) return nullptr;
|
||||
|
||||
memcpy(&wire, buf, sizeof(wire));
|
||||
|
||||
auto typeFace = fMapIdToTypeface.find(wire.typefaceID);
|
||||
if (typeFace == nullptr) {
|
||||
auto newTypeface = sk_make_sp<SkTypefaceProxy>(
|
||||
wire.typefaceID,
|
||||
wire.glyphCount,
|
||||
wire.style,
|
||||
wire.isFixed,
|
||||
this);
|
||||
|
||||
typeFace = fMapIdToTypeface.set(wire.typefaceID, newTypeface);
|
||||
return addTypeface(wire);
|
||||
}
|
||||
return *typeFace;
|
||||
|
||||
sk_sp<SkTypeface> SkStrikeClient::addTypeface(const WireTypeface& wire) {
|
||||
auto* typeface = fRemoteFontIdToTypeface.find(wire.typefaceID);
|
||||
if (typeface) return *typeface;
|
||||
|
||||
auto newTypeface = sk_make_sp<SkTypefaceProxy>(wire.typefaceID, wire.glyphCount, wire.style,
|
||||
wire.isFixed, this);
|
||||
fRemoteFontIdToTypeface.set(wire.typefaceID, newTypeface);
|
||||
return newTypeface;
|
||||
}
|
||||
|
||||
void SkStrikeClient::generateFontMetrics(const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkPaint::FontMetrics* metrics) {
|
||||
TRACE_EVENT1("skia", "generateFontMetrics", "rec", TRACE_STR_COPY(rec.dump().c_str()));
|
||||
SkDebugf("generateFontMetrics: %s\n", rec.dump().c_str());
|
||||
SkStrikeCache::Dump();
|
||||
SkDEBUGFAIL("GlyphCacheMiss");
|
||||
}
|
||||
|
||||
void SkStrikeClient::generateMetricsAndImage(const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkArenaAlloc* alloc,
|
||||
SkGlyph* glyph) {
|
||||
TRACE_EVENT1("skia", "generateMetricsAndImage", "rec", TRACE_STR_COPY(rec.dump().c_str()));
|
||||
SkDebugf("generateMetricsAndImage: %s\n", rec.dump().c_str());
|
||||
SkStrikeCache::Dump();
|
||||
SkDEBUGFAIL("GlyphCacheMiss");
|
||||
}
|
||||
|
||||
void SkStrikeClient::generatePath(const SkTypefaceProxy& typefaceProxy,
|
||||
const SkScalerContextRec& rec,
|
||||
SkGlyphID glyphID,
|
||||
SkPath* path) {
|
||||
TRACE_EVENT1("skia", "generateMetricsAndImage", "rec", TRACE_STR_COPY(rec.dump().c_str()));
|
||||
SkDebugf("generatePath: %s\n", rec.dump().c_str());
|
||||
SkStrikeCache::Dump();
|
||||
SkDEBUGFAIL("GlyphCacheMiss");
|
||||
}
|
||||
|
@ -5,192 +5,196 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkRemoteGlyphCache_DEFINED
|
||||
#define SkRemoteGlyphCache_DEFINED
|
||||
#ifndef SkRemoteGlyphCachePriv_DEFINED
|
||||
#define SkRemoteGlyphCachePriv_DEFINED
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "../private/SkTHash.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDescriptor.h"
|
||||
#include "SkDrawLooper.h"
|
||||
#include "SkGlyphCache.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "SkNoDrawCanvas.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkRemoteGlyphCache.h"
|
||||
#include "SkSerialProcs.h"
|
||||
#include "SkStrikeCache.h"
|
||||
#include "SkTextBlobRunIterator.h"
|
||||
#include "SkTHash.h"
|
||||
#include "SkTypeface.h"
|
||||
#include "SkTypeface_remote.h"
|
||||
|
||||
// The client uses a SkStrikeCacheClientRPC to send and receive data.
|
||||
using SkStrikeCacheClientRPC = std::function<sk_sp<SkData>(const SkData&)>;
|
||||
class Serializer;
|
||||
class SkDescriptor;
|
||||
class SkGlyphCache;
|
||||
struct SkPackedGlyphID;
|
||||
class SkScalerContextRecDescriptor;
|
||||
class SkTextBlobRunIterator;
|
||||
class SkTypefaceProxy;
|
||||
struct WireTypeface;
|
||||
|
||||
class SkScalerContextRecDescriptor {
|
||||
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());
|
||||
}
|
||||
class SkStrikeServer;
|
||||
|
||||
explicit SkScalerContextRecDescriptor(const SkDescriptor& desc)
|
||||
: SkScalerContextRecDescriptor(ExtractRec(desc)) { }
|
||||
|
||||
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();
|
||||
}
|
||||
struct SkDescriptorMapOperators {
|
||||
size_t operator()(const SkDescriptor* key) const;
|
||||
bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
using SkDescriptorMap = std::unordered_map<const SkDescriptor*, T, SkDescriptorMapOperators,
|
||||
SkDescriptorMapOperators>;
|
||||
|
||||
class SkStrikeDifferences {
|
||||
using SkDescriptorSet =
|
||||
std::unordered_set<const SkDescriptor*, SkDescriptorMapOperators, SkDescriptorMapOperators>;
|
||||
|
||||
// A SkTextBlobCacheDiffCanvas is used to populate the SkStrikeServer with ops
|
||||
// which will be serialized and renderered using the SkStrikeClient.
|
||||
class SK_API SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas {
|
||||
public:
|
||||
SkStrikeDifferences(SkFontID typefaceID, std::unique_ptr<SkDescriptor> desc);
|
||||
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 {
|
||||
public:
|
||||
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:
|
||||
SkDescriptorMap<SkStrikeDifferences> fDescriptorToDifferencesMap{16};
|
||||
};
|
||||
|
||||
class SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas {
|
||||
public:
|
||||
SkTextBlobCacheDiffCanvas(int width, int height,
|
||||
const SkMatrix& deviceMatrix,
|
||||
const SkSurfaceProps& props,
|
||||
SkScalerContextFlags flags,
|
||||
SkStrikeCacheDifferenceSpec* strikeDiffs);
|
||||
SkTextBlobCacheDiffCanvas(int width, int height, const SkMatrix& deviceMatrix,
|
||||
const SkSurfaceProps& props, SkStrikeServer* strikeserver);
|
||||
~SkTextBlobCacheDiffCanvas() override;
|
||||
|
||||
protected:
|
||||
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
|
||||
SkCanvas::SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
|
||||
|
||||
void onDrawTextBlob(
|
||||
const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override;
|
||||
void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) override;
|
||||
|
||||
private:
|
||||
void processLooper(
|
||||
const SkPoint& position,
|
||||
void processLooper(const SkPoint& position,
|
||||
const SkTextBlobRunIterator& it,
|
||||
const SkPaint& origPaint,
|
||||
SkDrawLooper* looper);
|
||||
|
||||
void processGlyphRun(
|
||||
const SkPoint& position,
|
||||
void processGlyphRun(const SkPoint& position,
|
||||
const SkTextBlobRunIterator& it,
|
||||
const SkPaint& runPaint);
|
||||
|
||||
const SkMatrix fDeviceMatrix;
|
||||
const SkSurfaceProps fSurfaceProps;
|
||||
const SkScalerContextFlags fScalerContextFlags;
|
||||
|
||||
SkStrikeCacheDifferenceSpec* const fStrikeCacheDiff;
|
||||
SkStrikeServer* const fStrikeServer;
|
||||
};
|
||||
|
||||
class SkStrikeServer {
|
||||
using SkDiscardableHandleId = uint32_t;
|
||||
|
||||
// This class is not thread-safe.
|
||||
class SK_API SkStrikeServer {
|
||||
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();
|
||||
|
||||
// embedding clients call these methods
|
||||
void serve(const SkData&, std::vector<uint8_t>*);
|
||||
// Serializes the typeface to be remoted using this server.
|
||||
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
|
||||
SkScalerContext* generateScalerContext(
|
||||
const SkScalerContextRecDescriptor& desc, SkFontID typefaceId);
|
||||
|
||||
private:
|
||||
using DescriptorToContextMap = SkTHashMap<SkScalerContextRecDescriptor,
|
||||
std::unique_ptr<SkScalerContext>,
|
||||
SkScalerContextRecDescriptor::Hash>;
|
||||
|
||||
sk_sp<SkData> encodeTypeface(SkTypeface* tf);
|
||||
|
||||
int fOpCount = 0;
|
||||
SkTHashMap<SkFontID, sk_sp<SkTypeface>> fTypefaceMap;
|
||||
DescriptorToContextMap fScalerContextMap;
|
||||
};
|
||||
|
||||
class SkStrikeClient {
|
||||
// Methods used internally in skia ------------------------------------------
|
||||
class SkGlyphCacheState {
|
||||
public:
|
||||
SkStrikeClient(SkStrikeCacheClientRPC);
|
||||
SkGlyphCacheState(std::unique_ptr<SkDescriptor> desc,
|
||||
SkDiscardableHandleId discardableHandleId);
|
||||
~SkGlyphCacheState();
|
||||
|
||||
// embedding clients call these methods
|
||||
void primeStrikeCache(const SkStrikeCacheDifferenceSpec&);
|
||||
void prepareDeserializeProcs(SkDeserialProcs* procs);
|
||||
|
||||
// mostly called internally by Skia
|
||||
void generateFontMetrics(
|
||||
const SkTypefaceProxy&, const SkScalerContextRec&, SkPaint::FontMetrics*);
|
||||
void generateMetricsAndImage(
|
||||
const SkTypefaceProxy&, const SkScalerContextRec&, SkArenaAlloc*, SkGlyph*);
|
||||
bool generatePath(
|
||||
const SkTypefaceProxy&, const SkScalerContextRec&, SkGlyphID glyph, SkPath* path);
|
||||
SkTypeface* lookupTypeface(SkFontID id);
|
||||
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:
|
||||
sk_sp<SkTypeface> decodeTypeface(const void* buf, size_t len);
|
||||
// The set of glyphs cached on the remote client.
|
||||
SkTHashSet<SkPackedGlyphID> fCachedGlyphs;
|
||||
|
||||
// TODO: Figure out how to manage the entries for the following maps.
|
||||
SkTHashMap<SkFontID, sk_sp<SkTypefaceProxy>> fMapIdToTypeface;
|
||||
// The set of glyphs which has not yet been serialized and sent to the
|
||||
// remote client.
|
||||
std::vector<SkPackedGlyphID> fPendingGlyphs;
|
||||
|
||||
SkStrikeCacheClientRPC fClientRPC;
|
||||
std::unique_ptr<SkDescriptor> fDesc;
|
||||
const SkDiscardableHandleId fDiscardableHandleId = -1;
|
||||
std::unique_ptr<SkScalerContext> fContext;
|
||||
};
|
||||
SkGlyphCacheState* getOrCreateCache(SkTypeface*, std::unique_ptr<SkDescriptor>);
|
||||
|
||||
std::vector<uint8_t> fBuffer;
|
||||
private:
|
||||
SkDescriptorMap<std::unique_ptr<SkGlyphCacheState>> fRemoteGlyphStateMap;
|
||||
DiscardableHandleManager* const fDiscardableHandleManager;
|
||||
SkTHashSet<SkFontID> fCachedTypefaces;
|
||||
|
||||
// State cached until the next serialization.
|
||||
SkDescriptorSet fLockedDescs;
|
||||
std::vector<WireTypeface> fTypefacesToSend;
|
||||
};
|
||||
|
||||
#endif // SkRemoteGlyphCache_DEFINED
|
||||
class SK_API SkStrikeClient {
|
||||
public:
|
||||
// An interface to delete handles that may be pinned by the remote server.
|
||||
class DiscardableHandleManager : public SkRefCnt {
|
||||
public:
|
||||
virtual ~DiscardableHandleManager() {}
|
||||
|
||||
// Returns true if the handle was unlocked and can be safely deleted. Once
|
||||
// successful, subsequent attempts to delete the same handle are invalid.
|
||||
virtual bool deleteHandle(SkDiscardableHandleId) = 0;
|
||||
};
|
||||
|
||||
SkStrikeClient(sk_sp<DiscardableHandleManager>);
|
||||
~SkStrikeClient();
|
||||
|
||||
// Deserializes the typeface previously serialized using the SkStrikeServer. Returns null if the
|
||||
// data is invalid.
|
||||
sk_sp<SkTypeface> deserializeTypeface(const void* data, size_t length);
|
||||
|
||||
// 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:
|
||||
class DiscardableStrikePinner;
|
||||
|
||||
sk_sp<SkTypeface> addTypeface(const WireTypeface& wire);
|
||||
|
||||
SkTHashMap<SkFontID, sk_sp<SkTypeface>> fRemoteFontIdToTypeface;
|
||||
sk_sp<DiscardableHandleManager> fDiscardableHandleManager;
|
||||
};
|
||||
|
||||
#endif // SkRemoteGlyphCachePriv_DEFINED
|
||||
|
@ -135,8 +135,8 @@ public:
|
||||
fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]);
|
||||
msg.appendf(" frame %g miter %g format %d join %d cap %d flags %#hx\n",
|
||||
fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags);
|
||||
msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n",
|
||||
fLumBits, fDeviceGamma, fPaintGamma, fContrast);
|
||||
msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n", fLumBits,
|
||||
fDeviceGamma, fPaintGamma, fContrast);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define SkStrikeCache_DEFINED
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "SkDescriptor.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 {
|
||||
public:
|
||||
virtual ~SkStrikePinner() = default;
|
||||
|
@ -6,17 +6,15 @@
|
||||
*/
|
||||
|
||||
#include "SkTypeface_remote.h"
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkRemoteGlyphCache.h"
|
||||
|
||||
SkScalerContextProxy::SkScalerContextProxy(
|
||||
sk_sp<SkTypeface> tf,
|
||||
#include "SkPaint.h"
|
||||
|
||||
SkScalerContextProxy::SkScalerContextProxy(sk_sp<SkTypeface> tf,
|
||||
const SkScalerContextEffects& effects,
|
||||
const SkDescriptor* desc,
|
||||
SkStrikeClient* rsc)
|
||||
: SkScalerContext{std::move(tf), effects, desc}
|
||||
, fClient{rsc} {}
|
||||
: SkScalerContext{std::move(tf), effects, desc}, fClient{rsc} {}
|
||||
|
||||
unsigned SkScalerContextProxy::generateGlyphCount() {
|
||||
SK_ABORT("Should never be called.");
|
||||
@ -40,7 +38,8 @@ void SkScalerContextProxy::generateImage(const SkGlyph& glyph) {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -21,8 +21,7 @@ class SkTypefaceProxy;
|
||||
|
||||
class SkScalerContextProxy : public SkScalerContext {
|
||||
public:
|
||||
SkScalerContextProxy(
|
||||
sk_sp<SkTypeface> tf,
|
||||
SkScalerContextProxy(sk_sp<SkTypeface> tf,
|
||||
const SkScalerContextEffects& effects,
|
||||
const SkDescriptor* desc,
|
||||
SkStrikeClient* rsc);
|
||||
@ -52,16 +51,12 @@ private:
|
||||
|
||||
class SkTypefaceProxy : public SkTypeface {
|
||||
public:
|
||||
SkTypefaceProxy(
|
||||
SkFontID fontId,
|
||||
SkTypefaceProxy(SkFontID fontId,
|
||||
int glyphCount,
|
||||
const SkFontStyle& style,
|
||||
bool isFixed,
|
||||
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;}
|
||||
int glyphCount() const {return fGlyphCount;}
|
||||
static SkTypefaceProxy* DownCast(SkTypeface* typeface) {
|
||||
@ -138,6 +133,8 @@ protected:
|
||||
private:
|
||||
const SkFontID fFontId;
|
||||
const int fGlyphCount;
|
||||
|
||||
// TODO: Does this need a ref to the strike client? If yes, make it a weak ref.
|
||||
SkStrikeClient* const fRsc;
|
||||
|
||||
typedef SkTypeface INHERITED;
|
||||
|
223
tests/SkRemoteGlyphCacheTest.cpp
Normal file
223
tests/SkRemoteGlyphCacheTest.cpp
Normal 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());
|
||||
}
|
@ -16,8 +16,9 @@
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SkRemoteGlyphCache.h"
|
||||
#include "SkGraphics.h"
|
||||
#include "SkRemoteGlyphCache.h"
|
||||
#include "SkScalerContext.h"
|
||||
#include "SkSurface.h"
|
||||
|
||||
static std::string gSkpName;
|
||||
@ -25,6 +26,46 @@ static bool gUseGpu = true;
|
||||
static bool gPurgeFontCaches = 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) {
|
||||
size_t size = data.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) {
|
||||
|
||||
size_t size;
|
||||
ssize_t readSize = ::read(fd, &size, sizeof(size));
|
||||
if (readSize <= 0) {
|
||||
@ -92,44 +132,56 @@ private:
|
||||
std::chrono::duration<double> fElapsedSeconds{0.0};
|
||||
};
|
||||
|
||||
static void build_prime_cache_spec(const SkIRect &bounds,
|
||||
const SkSurfaceProps &props,
|
||||
const SkPicture &pic,
|
||||
SkStrikeCacheDifferenceSpec *strikeDifference) {
|
||||
static bool push_font_data(const SkPicture& pic, SkStrikeServer* strikeServer, int writeFd) {
|
||||
SkMatrix deviceMatrix = SkMatrix::I();
|
||||
|
||||
SkTextBlobCacheDiffCanvas filter(
|
||||
bounds.width(), bounds.height(), deviceMatrix, props,
|
||||
SkScalerContextFlags::kFakeGammaAndBoostContrast,
|
||||
strikeDifference);
|
||||
|
||||
const SkIRect bounds = pic.cullRect().round();
|
||||
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
||||
SkTextBlobCacheDiffCanvas filter(bounds.width(), bounds.height(), deviceMatrix, props,
|
||||
strikeServer);
|
||||
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,
|
||||
SkDeserialProcs* procs,
|
||||
SkData* picData,
|
||||
SkStrikeClient* client) {
|
||||
static void final_draw(std::string outFilename, SkData* picData, SkStrikeClient* client,
|
||||
ClientDiscardableManager* discardableManager, int readFd, int writeFd) {
|
||||
SkDeserialProcs procs;
|
||||
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 r = cullRect.round();
|
||||
|
||||
auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height());
|
||||
auto c = s->getCanvas();
|
||||
auto picUnderTest = SkPicture::MakeFromData(picData, procs);
|
||||
auto picUnderTest = SkPicture::MakeFromData(picData, &procs);
|
||||
|
||||
Timer drawTime;
|
||||
auto randomData = SkData::MakeUninitialized(1u);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (gPurgeFontCaches) {
|
||||
ClientDiscardableManager::ScopedPurgeCache purge(discardableManager);
|
||||
SkGraphics::PurgeFontCache();
|
||||
SkASSERT(SkGraphics::GetFontCacheUsed() == 0u);
|
||||
}
|
||||
|
||||
drawTime.start();
|
||||
if (client != nullptr) {
|
||||
SkStrikeCacheDifferenceSpec strikeDifference;
|
||||
build_prime_cache_spec(r, s->props(), *picUnderTest, &strikeDifference);
|
||||
client->primeStrikeCache(strikeDifference);
|
||||
// Kick the renderer to send us the fonts.
|
||||
write_SkData(writeFd, *randomData);
|
||||
auto fontData = read_SkData(readFd);
|
||||
if (fontData && !fontData->isEmpty()) {
|
||||
if (!client->readStrikeData(fontData->data(), fontData->size()))
|
||||
SK_ABORT("Bad serialization");
|
||||
}
|
||||
}
|
||||
c->drawPicture(picUnderTest);
|
||||
drawTime.stop();
|
||||
@ -150,22 +202,16 @@ static void final_draw(std::string outFilename,
|
||||
static void gpu(int readFd, int writeFd) {
|
||||
|
||||
if (gUseGpu) {
|
||||
auto clientRPC = [readFd, writeFd](const SkData& inBuffer) {
|
||||
write_SkData(writeFd, inBuffer);
|
||||
return read_SkData(readFd);
|
||||
};
|
||||
|
||||
auto picData = read_SkData(readFd);
|
||||
if (picData == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkStrikeClient client{clientRPC};
|
||||
sk_sp<ClientDiscardableManager> discardableManager = sk_make_sp<ClientDiscardableManager>();
|
||||
SkStrikeClient strikeClient(discardableManager);
|
||||
|
||||
SkDeserialProcs procs;
|
||||
client.prepareDeserializeProcs(&procs);
|
||||
|
||||
final_draw("test.png", &procs, picData.get(), &client);
|
||||
final_draw("test.png", picData.get(), &strikeClient, discardableManager.get(), readFd,
|
||||
writeFd);
|
||||
}
|
||||
|
||||
::close(writeFd);
|
||||
@ -177,7 +223,8 @@ static void gpu(int readFd, int writeFd) {
|
||||
static int renderer(
|
||||
const std::string& skpName, int readFd, int writeFd)
|
||||
{
|
||||
SkStrikeServer server{};
|
||||
ServerDiscardableManager discardableManager;
|
||||
SkStrikeServer server(&discardableManager);
|
||||
auto closeAll = [readFd, writeFd]() {
|
||||
::close(writeFd);
|
||||
::close(readFd);
|
||||
@ -186,11 +233,16 @@ static int renderer(
|
||||
auto skpData = SkData::MakeFromFileName(skpName.c_str());
|
||||
std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl;
|
||||
|
||||
SkSerialProcs procs;
|
||||
sk_sp<SkData> stream;
|
||||
if (gUseGpu) {
|
||||
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);
|
||||
|
||||
if (!write_SkData(writeFd, *stream)) {
|
||||
@ -198,22 +250,18 @@ static int renderer(
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> tmpBuffer;
|
||||
while (true) {
|
||||
auto inBuffer = read_SkData(readFd);
|
||||
if (inBuffer == nullptr) {
|
||||
closeAll();
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmpBuffer.clear();
|
||||
server.serve(*inBuffer, &tmpBuffer);
|
||||
auto outBuffer = SkData::MakeWithoutCopy(tmpBuffer.data(), tmpBuffer.size());
|
||||
write_SkData(writeFd, *outBuffer);
|
||||
if (gPurgeFontCaches) discardableManager.purgeAll();
|
||||
push_font_data(*pic.get(), &server, writeFd);
|
||||
}
|
||||
} else {
|
||||
stream = skpData;
|
||||
final_draw("test-correct.png", nullptr, stream.get(), nullptr);
|
||||
final_draw("test-correct.png", stream.get(), nullptr, nullptr, -1, -1);
|
||||
closeAll();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user