BitmapTextBatch and BitmapTextBlob

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/eed1dae04932483579b02c10f0706127d3f5d984

Review URL: https://codereview.chromium.org/1011403004
This commit is contained in:
joshualitt 2015-03-31 13:32:05 -07:00 committed by Commit bot
parent 7b6db09805
commit 7c3a2f834e
19 changed files with 1609 additions and 37 deletions

View File

@ -243,8 +243,21 @@ Error SKPSrc::draw(SkCanvas* canvas) const {
return SkStringPrintf("Couldn't decode %s as a picture.", fPath.c_str());
}
stream.reset((SkStream*)NULL); // Might as well drop this when we're done with it.
canvas->clipRect(kSKPViewport);
// Testing TextBlob batching requires that we see individual text blobs more than once
// TODO remove this and add a flag to DM so we can run skps multiple times
//#define DOUBLE_LOOP
#ifdef DOUBLE_LOOP
{
SkAutoCanvasRestore acr(canvas, true);
#endif
canvas->drawPicture(pic);
#ifdef DOUBLE_LOOP
}
canvas->clear(0);
canvas->drawPicture(pic);
#endif
return "";
}

View File

@ -65,6 +65,8 @@
'<(skia_src_path)/gpu/GrBatch.h',
'<(skia_src_path)/gpu/GrBatchAtlas.cpp',
'<(skia_src_path)/gpu/GrBatchAtlas.h',
'<(skia_src_path)/gpu/GrBatchFontCache.cpp',
'<(skia_src_path)/gpu/GrBatchFontCache.h',
'<(skia_src_path)/gpu/GrBatchTarget.cpp',
'<(skia_src_path)/gpu/GrBatchTarget.h',
'<(skia_src_path)/gpu/GrBitmapTextContext.cpp',

View File

@ -1122,6 +1122,7 @@ private:
friend class SkGraphics; // So Term() can be called.
friend class SkPDFDevice;
friend class GrBitmapTextContext;
friend class GrBitmapTextContextB;
friend class GrDistanceFieldTextContext;
friend class GrStencilAndCoverTextContext;
friend class GrPathRendering;

View File

@ -91,6 +91,7 @@ private:
static unsigned ScalarsPerGlyph(GlyphPositioning pos);
friend class GrBitmapTextContextB;
friend class GrTextContext;
friend class SkBaseDevice;
friend class SkTextBlobBuilder;

View File

@ -19,6 +19,7 @@
#include "SkTypes.h"
class GrAARectRenderer;
class GrBatchFontCache;
class GrDrawTarget;
class GrFontCache;
class GrFragmentProcessor;
@ -654,6 +655,7 @@ public:
// Functions intended for internal use only.
GrGpu* getGpu() { return fGpu; }
const GrGpu* getGpu() const { return fGpu; }
GrBatchFontCache* getBatchFontCache() { return fBatchFontCache; }
GrFontCache* getFontCache() { return fFontCache; }
GrLayerCache* getLayerCache() { return fLayerCache.get(); }
GrDrawTarget* getTextTarget();
@ -695,6 +697,7 @@ private:
GrGpu* fGpu;
GrResourceCache* fResourceCache;
GrBatchFontCache* fBatchFontCache;
GrFontCache* fFontCache;
SkAutoTDelete<GrLayerCache> fLayerCache;

View File

@ -293,8 +293,7 @@ public:
instancesToFlush++;
}
this->flush(batchTarget, dfProcessor, pipeline, &drawInfo, instancesToFlush,
maxInstancesPerDraw);
this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw);
}
SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
@ -421,8 +420,7 @@ private:
bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(),
&atlasLocation);
if (!success) {
this->flush(batchTarget, dfProcessor, pipeline, drawInfo, *instancesToFlush,
maxInstancesPerDraw);
this->flush(batchTarget, drawInfo, *instancesToFlush, maxInstancesPerDraw);
this->initDraw(batchTarget, dfProcessor, pipeline);
*instancesToFlush = 0;
@ -516,8 +514,6 @@ private:
}
void flush(GrBatchTarget* batchTarget,
const GrGeometryProcessor* dfProcessor,
const GrPipeline* pipeline,
GrDrawTarget::DrawInfo* drawInfo,
int instanceCount,
int maxInstancesPerDraw) {

View File

@ -227,7 +227,8 @@ GrBatchAtlas::GrBatchAtlas(GrTexture* texture, int numPlotsX, int numPlotsY)
, fNumPlotsX(numPlotsX)
, fNumPlotsY(numPlotsY)
, fPlotWidth(texture->width() / numPlotsX)
, fPlotHeight(texture->height() / numPlotsY) {
, fPlotHeight(texture->height() / numPlotsY)
, fAtlasGeneration(kInvalidAtlasGeneration + 1) {
SkASSERT(fPlotWidth * fNumPlotsX == texture->width());
SkASSERT(fPlotHeight * fNumPlotsY == texture->height());
@ -243,7 +244,7 @@ GrBatchAtlas::GrBatchAtlas(GrTexture* texture, int numPlotsX, int numPlotsY)
for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) {
int id = r * fNumPlotsX + c;
currPlot->reset(SkNEW(BatchPlot));
(*currPlot)->init(this, texture, id, 0, x, y, fPlotWidth, fPlotHeight, fBPP);
(*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP);
// build LRU list
fPlotList.addToHead(currPlot->get());
@ -318,6 +319,7 @@ bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc, fBPP * width);
SkASSERT(verify);
this->updatePlot(batchTarget, id, plot);
fAtlasGeneration++;
return true;
}
@ -352,6 +354,7 @@ bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
batchTarget->upload(uploader);
*id = newPlot->id();
plot->unref();
fAtlasGeneration++;
return true;
}

View File

@ -25,6 +25,8 @@ public:
// An AtlasID is an opaque handle which callers can use to determine if the atlas contains
// a specific piece of data
typedef uint32_t AtlasID;
static const uint32_t kInvalidAtlasID = 0;
static const uint64_t kInvalidAtlasGeneration = 0;
// A function pointer for use as a callback during eviction. Whenever GrBatchAtlas evicts a
// specific AtlasID, it will call all of the registered listeners so they can optionally process
@ -43,6 +45,7 @@ public:
GrTexture* getTexture() const { return fTexture; }
uint64_t atlasGeneration() const { return fAtlasGeneration; }
bool hasID(AtlasID id);
void setLastRefToken(AtlasID id, BatchToken batchToken);
void registerEvictionCallback(EvictionFunc func, void* userData) {
@ -72,6 +75,7 @@ private:
int fPlotWidth;
int fPlotHeight;
size_t fBPP;
uint64_t fAtlasGeneration;
struct EvictionData {
EvictionFunc fFunc;

View File

@ -0,0 +1,314 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrBatchFontCache.h"
#include "GrFontAtlasSizes.h"
#include "GrGpu.h"
#include "GrRectanizer.h"
#include "GrSurfacePriv.h"
#include "SkString.h"
#include "SkDistanceFieldGen.h"
///////////////////////////////////////////////////////////////////////////////
static GrBatchAtlas* make_atlas(GrContext* context, GrPixelConfig config,
int textureWidth, int textureHeight,
int numPlotsX, int numPlotsY) {
GrSurfaceDesc desc;
desc.fFlags = kNone_GrSurfaceFlags;
desc.fWidth = textureWidth;
desc.fHeight = textureHeight;
desc.fConfig = config;
// We don't want to flush the context so we claim we're in the middle of flushing so as to
// guarantee we do not recieve a texture with pending IO
GrTexture* texture = context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch, true);
if (!texture) {
return NULL;
}
return SkNEW_ARGS(GrBatchAtlas, (texture, numPlotsX, numPlotsY));
}
int GrBatchFontCache::MaskFormatToAtlasIndex(GrMaskFormat format) {
static const int sAtlasIndices[] = {
kA8_GrMaskFormat,
kA565_GrMaskFormat,
kARGB_GrMaskFormat,
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
return sAtlasIndices[format];
}
GrMaskFormat GrBatchFontCache::AtlasIndexToMaskFormat(int atlasIndex) {
static GrMaskFormat sMaskFormats[] = {
kA8_GrMaskFormat,
kA565_GrMaskFormat,
kARGB_GrMaskFormat,
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sMaskFormats) == kMaskFormatCount, array_size_mismatch);
SkASSERT(sMaskFormats[atlasIndex] < kMaskFormatCount);
return sMaskFormats[atlasIndex];
}
GrBatchFontCache::GrBatchFontCache()
: fPreserveStrike(NULL) {
}
void GrBatchFontCache::init(GrContext* context) {
for (int i = 0; i < kMaskFormatCount; i++) {
GrMaskFormat format = AtlasIndexToMaskFormat(i);
GrPixelConfig config = this->getPixelConfig(format);
if (kA8_GrMaskFormat == format) {
fAtlases[i] = make_atlas(context, config,
GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
GR_FONT_ATLAS_TEXTURE_HEIGHT,
GR_FONT_ATLAS_A8_NUM_PLOTS_X,
GR_FONT_ATLAS_NUM_PLOTS_Y);
} else {
fAtlases[i] = make_atlas(context, config,
GR_FONT_ATLAS_TEXTURE_WIDTH,
GR_FONT_ATLAS_TEXTURE_HEIGHT,
GR_FONT_ATLAS_NUM_PLOTS_X,
GR_FONT_ATLAS_NUM_PLOTS_Y);
}
if (fAtlases[i]) {
fAtlases[i]->registerEvictionCallback(&GrBatchFontCache::HandleEviction, (void*)this);
}
}
}
GrBatchFontCache::~GrBatchFontCache() {
SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
while (!iter.done()) {
SkDELETE(&(*iter));
++iter;
}
for (int i = 0; i < kMaskFormatCount; ++i) {
SkDELETE(fAtlases[i]);
}
}
GrBatchTextStrike* GrBatchFontCache::generateStrike(GrFontScaler* scaler) {
GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
fCache.add(strike);
return strike;
}
void GrBatchFontCache::freeAll() {
SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
while (!iter.done()) {
SkDELETE(&(*iter));
++iter;
}
fCache.rewind();
for (int i = 0; i < kMaskFormatCount; ++i) {
SkDELETE(fAtlases[i]);
fAtlases[i] = NULL;
}
}
inline GrBatchAtlas* GrBatchFontCache::getAtlas(GrMaskFormat format) const {
int atlasIndex = MaskFormatToAtlasIndex(format);
SkASSERT(fAtlases[atlasIndex]);
return fAtlases[atlasIndex];
}
bool GrBatchFontCache::hasGlyph(GrGlyph* glyph) {
SkASSERT(glyph);
return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
}
void GrBatchFontCache::setGlyphRefToken(GrGlyph* glyph, GrBatchAtlas::BatchToken batchToken) {
SkASSERT(glyph);
SkASSERT(this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID));
this->getAtlas(glyph->fMaskFormat)->setLastRefToken(glyph->fID, batchToken);
}
bool GrBatchFontCache::addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
GrBatchTarget* batchTarget,
GrMaskFormat format, int width, int height, const void* image,
SkIPoint16* loc) {
fPreserveStrike = strike;
return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
}
uint64_t GrBatchFontCache::atlasGeneration(GrMaskFormat format) const {
return this->getAtlas(format)->atlasGeneration();
}
GrTexture* GrBatchFontCache::getTexture(GrMaskFormat format) {
int atlasIndex = MaskFormatToAtlasIndex(format);
SkASSERT(fAtlases[atlasIndex]);
return fAtlases[atlasIndex]->getTexture();
}
GrPixelConfig GrBatchFontCache::getPixelConfig(GrMaskFormat format) const {
static const GrPixelConfig kPixelConfigs[] = {
kAlpha_8_GrPixelConfig,
kRGB_565_GrPixelConfig,
kSkia8888_GrPixelConfig
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
return kPixelConfigs[format];
}
void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
for (; !iter.done(); ++iter) {
GrBatchTextStrike* strike = &*iter;
strike->removeID(id);
// clear out any empty strikes. We will preserve the strike whose call to addToAtlas
// triggered the eviction
if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
fontCache->fCache.remove(*(strike->fFontScalerKey));
SkDELETE(strike);
}
}
}
void GrBatchFontCache::dump() const {
static int gDumpCount = 0;
for (int i = 0; i < kMaskFormatCount; ++i) {
if (fAtlases[i]) {
GrTexture* texture = fAtlases[i]->getTexture();
if (texture) {
SkString filename;
#ifdef SK_BUILD_FOR_ANDROID
filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
#else
filename.printf("fontcache_%d%d.png", gDumpCount, i);
#endif
texture->surfacePriv().savePixels(filename.c_str());
}
}
}
++gDumpCount;
}
///////////////////////////////////////////////////////////////////////////////
/*
The text strike is specific to a given font/style/matrix setup, which is
represented by the GrHostFontScaler object we are given in getGlyph().
We map a 32bit glyphID to a GrGlyph record, which in turn points to a
atlas and a position within that texture.
*/
GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
: fFontScalerKey(SkRef(key))
, fPool(9/*start allocations at 512 bytes*/)
, fAtlasedGlyphs(0) {
fBatchFontCache = cache; // no need to ref, it won't go away before we do
}
GrBatchTextStrike::~GrBatchTextStrike() {
SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
while (!iter.done()) {
(*iter).free();
++iter;
}
}
GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed,
GrFontScaler* scaler) {
SkIRect bounds;
if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
return NULL;
}
} else {
if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
return NULL;
}
}
GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
glyph->init(packed, bounds, format);
fCache.add(glyph);
return glyph;
}
void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
while (!iter.done()) {
if (id == (*iter).fID) {
(*iter).fID = GrBatchAtlas::kInvalidAtlasID;
fAtlasedGlyphs--;
SkASSERT(fAtlasedGlyphs >= 0);
}
++iter;
}
}
bool GrBatchTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
int width = glyph->fBounds.width();
int height = glyph->fBounds.height();
bool useDistanceField =
(GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID));
int pad = useDistanceField ? 2 * SK_DistanceFieldPad : 0;
int plotWidth = (kA8_GrMaskFormat == glyph->fMaskFormat) ? GR_FONT_ATLAS_A8_PLOT_WIDTH
: GR_FONT_ATLAS_PLOT_WIDTH;
if (width + pad > plotWidth) {
return true;
}
if (height + pad > GR_FONT_ATLAS_PLOT_HEIGHT) {
return true;
}
return false;
}
bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
GrFontScaler* scaler) {
SkASSERT(glyph);
SkASSERT(scaler);
SkASSERT(fCache.find(glyph->fPackedID));
SkASSERT(NULL == glyph->fPlot);
SkAutoUnref ar(SkSafeRef(scaler));
int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
size_t size = glyph->fBounds.area() * bytesPerPixel;
GrAutoMalloc<1024> storage(size);
if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
glyph->height(),
storage.get())) {
return false;
}
} else {
if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
glyph->height(),
glyph->width() * bytesPerPixel,
storage.get())) {
return false;
}
}
bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat,
glyph->width(), glyph->height(),
storage.get(), &glyph->fAtlasLocation);
if (success) {
fAtlasedGlyphs++;
}
return success;
}

137
src/gpu/GrBatchFontCache.h Normal file
View File

@ -0,0 +1,137 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrBatchFontCache_DEFINED
#define GrBatchFontCache_DEFINED
#include "GrBatchAtlas.h"
#include "GrDrawTarget.h"
#include "GrFontScaler.h"
#include "GrGlyph.h"
#include "SkTDynamicHash.h"
#include "SkVarAlloc.h"
class GrBatchFontCache;
class GrBatchTarget;
class GrGpu;
/**
* The GrBatchTextStrike manages a pool of CPU backing memory for Glyph Masks. This backing memory
* is abstracted by GrGlyph, and indexed by a PackedID and GrFontScaler. The GrFontScaler is what
* actually creates the mask.
*/
class GrBatchTextStrike {
public:
GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
~GrBatchTextStrike();
const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
GrGlyph* glyph = fCache.find(packed);
if (NULL == glyph) {
glyph = this->generateGlyph(packed, scaler);
}
return glyph;
}
// returns true if glyph (or glyph+padding for distance field)
// is too large to ever fit in texture atlas subregions (GrPlots)
bool glyphTooLargeForAtlas(GrGlyph*);
// returns true if glyph successfully added to texture atlas, false otherwise
bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
// testing
int countGlyphs() const { return fCache.count(); }
// remove any references to this plot
void removeID(GrBatchAtlas::AtlasID);
static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
return *(ts.fFontScalerKey);
}
static uint32_t Hash(const GrFontDescKey& key) {
return key.getHash();
}
private:
SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
SkVarAlloc fPool;
GrBatchFontCache* fBatchFontCache;
int fAtlasedGlyphs;
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
friend class GrBatchFontCache;
};
/*
* GrBatchFontCache manages strikes which are indexed by a GrFontScaler. These strikes can then be
* used to individual Glyph Masks. The GrBatchFontCache also manages GrBatchAtlases, though this is
* more or less transparent to the client(aside from atlasGeneration, described below)
*/
class GrBatchFontCache {
public:
GrBatchFontCache();
~GrBatchFontCache();
// Initializes the GrBatchFontCache on the owning GrContext
void init(GrContext*);
inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
if (NULL == strike) {
strike = this->generateStrike(scaler);
}
return strike;
}
bool hasGlyph(GrGlyph* glyph);
// To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
// the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph
void setGlyphRefToken(GrGlyph*, GrBatchAtlas::BatchToken);
// add to texture atlas that matches this format
bool addToAtlas(GrBatchTextStrike*, GrBatchAtlas::AtlasID*, GrBatchTarget*,
GrMaskFormat, int width, int height, const void* image,
SkIPoint16* loc);
// Some clients may wish to verify the integrity of the texture backing store of the
// GrBatchAtlas. The atlasGeneration returned below is a monitonically increasing number which
// changes everytime something is removed from the texture backing store.
uint64_t atlasGeneration(GrMaskFormat) const;
void freeAll();
GrTexture* getTexture(GrMaskFormat);
GrPixelConfig getPixelConfig(GrMaskFormat) const;
void dump() const;
private:
// There is a 1:1 mapping between GrMaskFormats and atlas indices
static int MaskFormatToAtlasIndex(GrMaskFormat);
static GrMaskFormat AtlasIndexToMaskFormat(int atlasIndex);
GrBatchTextStrike* generateStrike(GrFontScaler*);
inline GrBatchAtlas* getAtlas(GrMaskFormat) const;
static void HandleEviction(GrBatchAtlas::AtlasID, void*);
SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
GrBatchAtlas* fAtlases[kMaskFormatCount];
GrBatchTextStrike* fPreserveStrike;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,127 @@
#include "GrTextContext.h"
#include "GrGeometryProcessor.h"
#include "SkTHash.h"
class GrBatchTextStrike;
class GrPipelineBuilder;
/*
* This class implements GrTextContext using standard bitmap fonts, and can also process textblobs.
* TODO replace GrBitmapTextContext
*/
class GrBitmapTextContextB : public GrTextContext {
public:
static GrBitmapTextContextB* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
virtual ~GrBitmapTextContextB();
private:
GrBitmapTextContextB(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
const SkPaint&, const SkMatrix& viewMatrix) override;
void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& regionClipBounds) override;
void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
SkDrawFilter*, const SkIRect& clipBounds) override;
void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
const SkIRect& regionClipBounds);
/*
* A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
* on the GPU. These are initially created with valid positions and colors, but invalid
* texture coordinates. The BitmapTextBlob itself has a few Blob-wide properties, and also
* consists of a number of runs. Runs inside a blob are flushed individually so they can be
* reordered.
*
* The only thing(aside from a memcopy) required to flush a BitmapTextBlob is to ensure that
* the GrAtlas will not evict anything the Blob needs.
* TODO this is currently a bug
*/
struct BitmapTextBlob : public SkRefCnt {
// Each Run inside of the blob can have its texture coordinates regenerated if required.
// To determine if regeneration is necessary, fAtlasGeneration is used. If there have been
// any evictions inside of the atlas, then we will simply regenerate Runs. We could track
// this at a more fine grained level, but its not clear if this is worth it, as evictions
// should be fairly rare.
// One additional point, each run can contain glyphs with any of the three mask formats.
// We maintain separate arrays for each format type, and flush them separately. In practice
// most of the time a run will have the same format type
struct Run {
Run() : fColor(GrColor_ILLEGAL) { fVertexBounds.setLargestInverted(); }
struct PerFormatInfo {
PerFormatInfo() : fAtlasGeneration(GrBatchAtlas::kInvalidAtlasGeneration) {}
SkTDArray<unsigned char> fVertices;
SkTDArray<GrGlyph::PackedID> fGlyphIDs;
uint64_t fAtlasGeneration;
};
SkAutoTUnref<const SkData> fDescriptor;
SkAutoTUnref<SkTypeface> fTypeface;
PerFormatInfo fInfos[kMaskFormatCount];
SkRect fVertexBounds;
GrColor fColor;
};
SkSTArray<1, Run, true> fRuns;
struct BigGlyph {
BigGlyph(const SkPath& path, int vx, int vy) : fPath(path), fVx(vx), fVy(vy) {}
SkPath fPath;
int fVx;
int fVy;
};
SkTArray<BigGlyph> fBigGlyphs;
SkTextBlob* fBlob;
SkMatrix fViewMatrix;
SkScalar fX;
SkScalar fY;
SkPaint::Style fStyle;
static uint32_t Hash(const uint32_t& key) {
return SkChecksum::Mix(key);
}
};
void appendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
GrFontScaler*, const SkIRect& clipRect);
void flushSubRun(GrDrawTarget*, BitmapTextBlob*, int i, GrPipelineBuilder*, GrMaskFormat,
GrColor color, int paintAlpha);
void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const GrPaint&, const GrClip&,
const SkMatrix& viewMatrix, int paintAlpha);
void internalDrawText(BitmapTextBlob*, int runIndex, const SkPaint&,
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
SkScalar x, SkScalar y, const SkIRect& clipRect);
void internalDrawPosText(BitmapTextBlob*, int runIndex, const SkPaint&,
const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& clipRect);
static inline bool MustRegenerateBlob(const BitmapTextBlob&, const SkPaint&,
const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
void regenerateTextBlob(BitmapTextBlob* bmp, const SkPaint& skPaint, const SkMatrix& viewMatrix,
const SkTextBlob* blob, SkScalar x, SkScalar y,
SkDrawFilter* drawFilter, const SkIRect& clipRect);
GrBatchTextStrike* fCurrStrike;
// TODO use real cache
static void ClearCacheEntry(uint32_t key, BitmapTextBlob**);
SkTHashMap<uint32_t, BitmapTextBlob*, BitmapTextBlob::Hash> fCache;
friend class BitmapTextBatch;
typedef GrTextContext INHERITED;
};
class GrTextStrike;

View File

@ -10,7 +10,9 @@
#include "GrAARectRenderer.h"
#include "GrBatch.h"
#include "GrBatchFontCache.h"
#include "GrBatchTarget.h"
#include "GrBitmapTextContext.h"
#include "GrBufferAllocPool.h"
#include "GrDefaultGeoProcFactory.h"
#include "GrFontCache.h"
@ -94,6 +96,7 @@ GrContext::GrContext(const Options& opts) : fOptions(opts) {
fPathRendererChain = NULL;
fSoftwarePathRenderer = NULL;
fResourceCache = NULL;
fBatchFontCache = NULL;
fFontCache = NULL;
fDrawBuffer = NULL;
fDrawBufferVBAllocPool = NULL;
@ -129,6 +132,10 @@ void GrContext::initCommon() {
fDidTestPMConversions = false;
this->setupDrawBuffer();
// GrBatchFontCache will eventually replace GrFontCache
fBatchFontCache = SkNEW(GrBatchFontCache);
fBatchFontCache->init(this);
}
GrContext::~GrContext() {
@ -143,6 +150,7 @@ GrContext::~GrContext() {
}
SkDELETE(fResourceCache);
SkDELETE(fBatchFontCache);
SkDELETE(fFontCache);
SkDELETE(fDrawBuffer);
SkDELETE(fDrawBufferVBAllocPool);
@ -180,6 +188,7 @@ void GrContext::abandonContext() {
fAARectRenderer->reset();
fOvalRenderer->reset();
fBatchFontCache->freeAll();
fFontCache->freeAll();
fLayerCache->freeAll();
}
@ -198,6 +207,7 @@ void GrContext::freeGpuResources() {
fAARectRenderer->reset();
fOvalRenderer->reset();
fBatchFontCache->freeAll();
fFontCache->freeAll();
fLayerCache->freeAll();
// a path renderer may be holding onto resources
@ -226,8 +236,12 @@ GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
}
}
#ifdef USE_BITMAP_TEXTBLOBS
return GrBitmapTextContextB::Create(this, gpuDevice, leakyProperties);
#else
return GrDistanceFieldTextContext::Create(this, gpuDevice, leakyProperties,
enableDistanceFieldFonts);
#endif
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -74,7 +74,11 @@ GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* contex
bool enable) {
GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
(context, gpuDevice, props, enable));
#ifdef USE_BITMAP_TEXTBLOBS
textContext->fFallbackTextContext = GrBitmapTextContextB::Create(context, gpuDevice, props);
#else
textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
#endif
return textContext;
}

View File

@ -9,6 +9,30 @@
#ifndef GrFontAtlasSizes_DEFINED
#define GrFontAtlasSizes_DEFINED
// For debugging atlas which evict all of the time
//#define DEBUG_CONSTANT_EVICT
#ifdef DEBUG_CONSTANT_EVICT
#define GR_FONT_ATLAS_TEXTURE_WIDTH 256//1024
#define GR_FONT_ATLAS_A8_TEXTURE_WIDTH 256//2048
#define GR_FONT_ATLAS_TEXTURE_HEIGHT 256//2048
#define GR_FONT_ATLAS_PLOT_WIDTH 256
#define GR_FONT_ATLAS_A8_PLOT_WIDTH 256//512
#define GR_FONT_ATLAS_PLOT_HEIGHT 256
#define GR_FONT_ATLAS_NUM_PLOTS_X (GR_FONT_ATLAS_TEXTURE_WIDTH / GR_FONT_ATLAS_PLOT_WIDTH)
#define GR_FONT_ATLAS_A8_NUM_PLOTS_X (GR_FONT_ATLAS_A8_TEXTURE_WIDTH / GR_FONT_ATLAS_A8_PLOT_WIDTH)
#define GR_FONT_ATLAS_NUM_PLOTS_Y (GR_FONT_ATLAS_TEXTURE_HEIGHT / GR_FONT_ATLAS_PLOT_HEIGHT)
// one over width and height
#define GR_FONT_ATLAS_RECIP_WIDTH "0.00390625"//"0.0009765625"
#define GR_FONT_ATLAS_A8_RECIP_WIDTH "0.00390625"//"0.00048828125"
#define GR_FONT_ATLAS_RECIP_HEIGHT "0.00390625"//"0.00048828125"
// 1/(3*width)
// only used for distance fields, which are A8
#define GR_FONT_ATLAS_LCD_DELTA "0.001302083"//"0.000162760417"
#else
#define GR_FONT_ATLAS_TEXTURE_WIDTH 1024
#define GR_FONT_ATLAS_A8_TEXTURE_WIDTH 2048
#define GR_FONT_ATLAS_TEXTURE_HEIGHT 2048
@ -29,5 +53,5 @@
// 1/(3*width)
// only used for distance fields, which are A8
#define GR_FONT_ATLAS_LCD_DELTA "0.000162760417"
#endif
#endif

View File

@ -8,6 +8,7 @@
#ifndef GrGlyph_DEFINED
#define GrGlyph_DEFINED
#include "GrBatchAtlas.h"
#include "GrRect.h"
#include "GrTypes.h"
@ -30,14 +31,17 @@ struct GrGlyph {
typedef uint32_t PackedID;
GrPlot* fPlot;
SkPath* fPath;
PackedID fPackedID;
GrMaskFormat fMaskFormat;
GrIRect16 fBounds;
SkIPoint16 fAtlasLocation;
// TODO either plot or AtlasID will be valid, not both
GrBatchAtlas::AtlasID fID;
GrPlot* fPlot;
SkPath* fPath;
PackedID fPackedID;
GrMaskFormat fMaskFormat;
GrIRect16 fBounds;
SkIPoint16 fAtlasLocation;
void init(GrGlyph::PackedID packed, const SkIRect& bounds, GrMaskFormat format) {
fID = GrBatchAtlas::kInvalidAtlasID;
fPlot = NULL;
fPath = NULL;
fPackedID = packed;

View File

@ -34,7 +34,11 @@ GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
const SkDeviceProperties& props) {
GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
(context, gpuDevice, props));
#ifdef USE_BITMAP_TEXTBLOBS
textContext->fFallbackTextContext = GrBitmapTextContextB::Create(context, gpuDevice, props);
#else
textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
#endif
return textContext;
}

View File

@ -23,6 +23,9 @@ class SkDrawFilter;
class SkGpuDevice;
class SkTextBlob;
// For testing textblobs on GPU.
//#define USE_BITMAP_TEXTBLOBS
/*
* This class wraps the state for a single text render
*/
@ -38,9 +41,9 @@ public:
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& clipBounds);
void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
SkDrawFilter*, const SkIRect& clipBounds);
virtual void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
SkDrawFilter*, const SkIRect& clipBounds);
protected:
GrTextContext* fFallbackTextContext;
@ -90,6 +93,8 @@ protected:
// sets extent in stopVector and returns glyph count
static int MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
const char text[], size_t byteLength, SkVector* stopVector);
friend class BitmapTextBatch;
};
#endif

View File

@ -219,6 +219,7 @@ private:
static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
int sampleCount);
friend class GrBitmapTextContextB;
friend class GrTextContext;
typedef SkBaseDevice INHERITED;
};