Speed up diff canvas using small bit vector

GlyphIDs < 128 are the vast majority of ids in roman text websites.
This change exploits this to do a fast check to see if a glyph has
already been sent from the Renderer to the GPU. This does not change
the performance of CJK glyphs.

Remember each glyphID entry takes 4 bits to encode sub-pixel position
information.

SkDiffBench-wikicat: 143ms -> 118ms

Change-Id: If5905e0dc6b39ae69e0235e81a9b6050afd25a97
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/251760
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Khushal Sagar <khushalsagar@google.com>
This commit is contained in:
Herb Derby 2019-10-29 16:21:10 -04:00 committed by Skia Commit-Bot
parent 746acf1ebc
commit 57643aa1c2

View File

@ -7,12 +7,14 @@
#include "src/core/SkRemoteGlyphCache.h"
#include <bitset>
#include <iterator>
#include <memory>
#include <new>
#include <string>
#include <tuple>
#include "include/private/SkChecksum.h"
#include "src/core/SkDevice.h"
#include "src/core/SkDraw.h"
#include "src/core/SkEnumerate.h"
@ -192,6 +194,36 @@ struct StrikeSpec {
/* n X (glyphs ids) */
};
// Represent a set of x sub-pixel-position glyphs with a glyph id < kMaxGlyphID and
// y sub-pxiel-position must be 0. Most sub-pixel-positioned glyphs have been x-axis aligned
// forcing the y sub-pixel position to be zero. We can organize the SkPackedGlyphID to check that
// the glyph id and the y position == 0 with a single compare in the following way:
// <y-sub-pixel-position>:2 | <glyphid:16> | <x-sub-pixel-position>:2
// This organization allows a single check of a packed-id to be:
// packed-id < kMaxGlyphID * possible-x-sub-pixel-positions
// where possible-x-sub-pixel-positions == 4.
class LowerRangeBitVector {
public:
bool test(SkPackedGlyphID packedID) const {
uint32_t bit = packedID.value();
return bit < kMaxIndex && fBits.test(bit);
}
void setIfLower(SkPackedGlyphID packedID) {
uint32_t bit = packedID.value();
if (bit < kMaxIndex) {
fBits.set(bit);
}
}
private:
using GID = SkPackedGlyphID;
static_assert(GID::kSubPixelX < GID::kGlyphID && GID::kGlyphID < GID::kSubPixelY,
"SkPackedGlyphID must be organized: sub-y | glyph id | sub-x");
static constexpr int kMaxGlyphID = 128;
static constexpr int kMaxIndex = kMaxGlyphID * (1u << GID::kSubPixelPosLen);
std::bitset<kMaxIndex> fBits;
};
// -- RemoteStrike ----------------------------------------------------------------------------
class SkStrikeServer::RemoteStrike final : public SkStrikeForGPU {
public:
@ -280,6 +312,7 @@ private:
void writeGlyphPath(const SkGlyph& glyph, Serializer* serializer) const;
void ensureScalerContext();
const int fNumberOfGlyphs;
const SkAutoDescriptor fDescriptor;
const SkDiscardableHandleId fDiscardableHandleId;
@ -296,6 +329,8 @@ private:
// Have the metrics been sent for this strike. Only send them once.
bool fHaveSentFontMetrics{false};
LowerRangeBitVector fSentLowGlyphIDs;
// The masks and paths that currently reside in the GPU process.
SkTHashTable<MaskSummary, SkPackedGlyphID, MaskSummaryTraits> fSentGlyphs;
SkTHashTable<PathSummary, SkGlyphID, PathSummaryTraits> fSentPaths;
@ -313,11 +348,13 @@ SkStrikeServer::RemoteStrike::RemoteStrike(
const SkDescriptor& descriptor,
std::unique_ptr<SkScalerContext> context,
uint32_t discardableHandleId)
: fDescriptor{descriptor}
: fNumberOfGlyphs(context->getGlyphCount())
, fDescriptor{descriptor}
, fDiscardableHandleId(discardableHandleId)
, fRoundingSpec{context->isSubpixel(), context->computeAxisAlignmentForHText()}
// N.B. context must come last because it is used above.
, fContext{std::move(context)} {
, fContext{std::move(context)}
, fSentLowGlyphIDs{} {
SkASSERT(fDescriptor.getDesc() != nullptr);
SkASSERT(fContext != nullptr);
}
@ -735,8 +772,37 @@ void SkStrikeServer::RemoteStrike::commonMaskLoop(
void SkStrikeServer::RemoteStrike::prepareForMaskDrawing(
SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
this->commonMaskLoop(drawables, rejects,
[](MaskSummary summary){return !summary.canDrawAsMask;});
for (auto t : SkMakeEnumerate(drawables->input())) {
size_t i; SkPackedGlyphID packedID;
std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
if (fSentLowGlyphIDs.test(packedID)) {
SkASSERT(fSentGlyphs.find(packedID) != nullptr);
continue;
}
MaskSummary* summary = fSentGlyphs.find(packedID);
if (summary == nullptr) {
// Put the new SkGlyph in the glyphs to send.
fMasksToSend.emplace_back(packedID);
SkGlyph* glyph = &fMasksToSend.back();
// Build the glyph
this->ensureScalerContext();
fContext->getMetrics(glyph);
fSentLowGlyphIDs.setIfLower(packedID);
MaskSummary newSummary =
{packedID.value(), CanDrawAsMask(*glyph), CanDrawAsSDFT(*glyph)};
summary = fSentGlyphs.set(newSummary);
}
// Reject things that are too big.
if (!summary->canDrawAsMask) {
rejects->reject(i);
}
}
}
void SkStrikeServer::RemoteStrike::prepareForSDFTDrawing(