diff options
-rw-r--r-- | src/gpu/GrAtlas.cpp | 62 | ||||
-rw-r--r-- | src/gpu/GrAtlas.h | 27 | ||||
-rw-r--r-- | src/gpu/GrTextContext.cpp | 6 | ||||
-rw-r--r-- | src/gpu/GrTextStrike.cpp | 80 | ||||
-rw-r--r-- | src/gpu/GrTextStrike.h | 6 |
5 files changed, 154 insertions, 27 deletions
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp index c1d6d3d693..f2daca11c5 100644 --- a/src/gpu/GrAtlas.cpp +++ b/src/gpu/GrAtlas.cpp @@ -44,9 +44,17 @@ static int gCounter; #endif +// for testing +#define FONT_CACHE_STATS 0 +#if FONT_CACHE_STATS +static int g_UploadCount = 0; +#endif + GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY, GrMaskFormat format) { fAtlasMgr = mgr; // just a pointer, not an owner fNext = NULL; + fUsed = false; + fTexture = mgr->getTexture(format); // we're not an owner, just a pointer fPlot.set(plotX, plotY); @@ -62,7 +70,7 @@ GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY, GrMaskFormat format) { } GrAtlas::~GrAtlas() { - fAtlasMgr->freePlot(fPlot.fX, fPlot.fY); + fAtlasMgr->freePlot(fMaskFormat, fPlot.fX, fPlot.fY); delete fRects; @@ -72,6 +80,27 @@ GrAtlas::~GrAtlas() { #endif } +bool GrAtlas::RemoveUnusedAtlases(GrAtlasMgr* atlasMgr, GrAtlas** startAtlas) { + // GrAtlas** is used so that a pointer to the head element can be passed in and + // modified when the first element is deleted + GrAtlas** atlasRef = startAtlas; + GrAtlas* atlas = *startAtlas; + bool removed = false; + while (NULL != atlas) { + if (!atlas->used()) { + *atlasRef = atlas->fNext; + atlasMgr->deleteAtlas(atlas); + atlas = *atlasRef; + removed = true; + } else { + atlasRef = &atlas->fNext; + atlas = atlas->fNext; + } + } + + return removed; +} + static void adjustForPlot(GrIPoint16* loc, const GrIPoint16& plot) { loc->fX += plot.fX * GR_ATLAS_WIDTH; loc->fY += plot.fY * GR_ATLAS_HEIGHT; @@ -122,6 +151,11 @@ bool GrAtlas::addSubImage(int width, int height, const void* image, // now tell the caller to skip the top/left BORDER loc->fX += BORDER; loc->fY += BORDER; + +#if FONT_CACHE_STATS + ++g_UploadCount; +#endif + return true; } @@ -139,7 +173,11 @@ GrAtlasMgr::~GrAtlasMgr() { GrSafeUnref(fTexture[i]); } delete fPlotMgr; + fGpu->unref(); +#if FONT_CACHE_STATS + GrPrintf("Num uploads: %d\n", g_UploadCount); +#endif } static GrPixelConfig maskformat2pixelconfig(GrMaskFormat format) { @@ -156,18 +194,23 @@ static GrPixelConfig maskformat2pixelconfig(GrMaskFormat format) { return kUnknown_GrPixelConfig; } -GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas* atlas, +GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas** atlas, int width, int height, const void* image, GrMaskFormat format, GrIPoint16* loc) { - GrAssert(NULL == atlas || atlas->getMaskFormat() == format); + GrAssert(NULL == *atlas || (*atlas)->getMaskFormat() == format); - if (atlas && atlas->addSubImage(width, height, image, loc)) { - return atlas; + // iterate through entire atlas list, see if we can find a hole + GrAtlas* atlasIter = *atlas; + while (atlasIter) { + if (atlasIter->addSubImage(width, height, image, loc)) { + return atlasIter; + } + atlasIter = atlasIter->fNext; } // If the above fails, then either we have no starting atlas, or the current - // one is full. Either way we need to allocate a new atlas + // atlas list is full. Either way we need to allocate a new atlas GrIPoint16 plot; if (!fPlotMgr->newPlot(&plot)) { @@ -196,11 +239,14 @@ GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas* atlas, return NULL; } - newAtlas->fNext = atlas; + // new atlas, put at head + newAtlas->fNext = *atlas; + *atlas = newAtlas; + return newAtlas; } -void GrAtlasMgr::freePlot(int x, int y) { +void GrAtlasMgr::freePlot(GrMaskFormat format, int x, int y) { GrAssert(fPlotMgr->isBusy(x, y)); fPlotMgr->freePlot(x, y); } diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h index 40a21545e1..b6f25c210a 100644 --- a/src/gpu/GrAtlas.h +++ b/src/gpu/GrAtlas.h @@ -20,8 +20,6 @@ class GrAtlasMgr; class GrAtlas { public: - GrAtlas(GrAtlasMgr*, int plotX, int plotY, GrMaskFormat); - int getPlotX() const { return fPlot.fX; } int getPlotY() const { return fPlot.fY; } GrMaskFormat getMaskFormat() const { return fMaskFormat; } @@ -31,20 +29,34 @@ public: bool addSubImage(int width, int height, const void*, GrIPoint16*); static void FreeLList(GrAtlas* atlas) { - while (atlas) { + while (NULL != atlas) { GrAtlas* next = atlas->fNext; delete atlas; atlas = next; } } - // testing - GrAtlas* nextAtlas() const { return fNext; } + static void MarkAllUnused(GrAtlas* atlas) { + while (NULL != atlas) { + atlas->fUsed = false; + atlas = atlas->fNext; + } + } + + static bool RemoveUnusedAtlases(GrAtlasMgr* atlasMgr, GrAtlas** startAtlas); + + bool used() const { return fUsed; } + void setUsed(bool used) { fUsed = used; } private: + GrAtlas(GrAtlasMgr*, int plotX, int plotY, GrMaskFormat format); ~GrAtlas(); // does not try to delete the fNext field GrAtlas* fNext; + + // for recycling + bool fUsed; + GrTexture* fTexture; GrRectanizer* fRects; GrAtlasMgr* fAtlasMgr; @@ -61,8 +73,9 @@ public: GrAtlasMgr(GrGpu*); ~GrAtlasMgr(); - GrAtlas* addToAtlas(GrAtlas*, int width, int height, const void*, + GrAtlas* addToAtlas(GrAtlas**, int width, int height, const void*, GrMaskFormat, GrIPoint16*); + void deleteAtlas(GrAtlas* atlas) { delete atlas; } GrTexture* getTexture(GrMaskFormat format) const { GrAssert((unsigned)format < kCount_GrMaskFormats); @@ -70,7 +83,7 @@ public: } // to be called by ~GrAtlas() - void freePlot(int x, int y); + void freePlot(GrMaskFormat format, int x, int y); private: GrGpu* fGpu; diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp index 5af0851f4c..8f0f1cfde6 100644 --- a/src/gpu/GrTextContext.cpp +++ b/src/gpu/GrTextContext.cpp @@ -153,6 +153,12 @@ void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, goto HAS_ATLAS; } + // try to clear out an unused atlas before we flush + fContext->getFontCache()->freeAtlasExceptFor(fStrike); + if (fStrike->getGlyphAtlas(glyph, scaler)) { + goto HAS_ATLAS; + } + // before we purge the cache, we must flush any accumulated draws this->flushGlyphs(); fContext->flush(); diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp index 071c5d2724..9373351f22 100644 --- a/src/gpu/GrTextStrike.cpp +++ b/src/gpu/GrTextStrike.cpp @@ -16,6 +16,11 @@ SK_DEFINE_INST_COUNT(GrKey) /////////////////////////////////////////////////////////////////////////////// +#define FONT_CACHE_STATS 0 +#if FONT_CACHE_STATS +static int g_PurgeCount = 0; +#endif + GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { gpu->ref(); fAtlasMgr = NULL; @@ -27,6 +32,9 @@ GrFontCache::~GrFontCache() { fCache.deleteAll(); delete fAtlasMgr; fGpu->unref(); +#if FONT_CACHE_STATS + GrPrintf("Num purges: %d\n", g_PurgeCount); +#endif } GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, @@ -62,19 +70,51 @@ void GrFontCache::freeAll() { void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) { GrTextStrike* strike = fTail; + bool purge = true; + while (strike) { + if (strike == preserveStrike) { + strike = strike->fPrev; + continue; + } + GrTextStrike* strikeToPurge = strike; + strike = strikeToPurge->fPrev; + if (purge) { + // keep purging if we won't free up any atlases with this strike. + purge = (NULL == strikeToPurge->fAtlas); + int index = fCache.slowFindIndex(strikeToPurge); + GrAssert(index >= 0); + fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); + this->detachStrikeFromList(strikeToPurge); + delete strikeToPurge; + } else { + // for the remaining strikes, we just mark them unused + GrAtlas::MarkAllUnused(strikeToPurge->fAtlas); + } + } +#if FONT_CACHE_STATS + ++g_PurgeCount; +#endif +} + +void GrFontCache::freeAtlasExceptFor(GrTextStrike* preserveStrike) { + GrTextStrike* strike = fTail; while (strike) { if (strike == preserveStrike) { strike = strike->fPrev; continue; } GrTextStrike* strikeToPurge = strike; - // keep going if we won't free up any atlases with this strike. - strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL; - int index = fCache.slowFindIndex(strikeToPurge); - GrAssert(index >= 0); - fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); - this->detachStrikeFromList(strikeToPurge); - delete strikeToPurge; + strike = strikeToPurge->fPrev; + if (strikeToPurge->removeUnusedAtlases()) { + if (NULL == strikeToPurge->fAtlas) { + int index = fCache.slowFindIndex(strikeToPurge); + GrAssert(index >= 0); + fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); + this->detachStrikeFromList(strikeToPurge); + delete strikeToPurge; + } + break; + } } } @@ -140,12 +180,20 @@ GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, #endif } -static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); } +// these signatures are needed because they're used with +// SkTDArray::visitAll() (see destructor & removeUnusedAtlases()) +static void free_glyph(GrGlyph*& glyph) { glyph->free(); } + +static void invalidate_glyph(GrGlyph*& glyph) { + if (glyph->fAtlas && !glyph->fAtlas->used()) { + glyph->fAtlas = NULL; + } +} GrTextStrike::~GrTextStrike() { GrAtlas::FreeLList(fAtlas); fFontScalerKey->unref(); - fCache.getArray().visitAll(FreeGlyph); + fCache.getArray().visitAll(free_glyph); #if GR_DEBUG gCounter -= 1; @@ -166,6 +214,13 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, return glyph; } +bool GrTextStrike::removeUnusedAtlases() { + fCache.getArray().visitAll(invalidate_glyph); + return GrAtlas::RemoveUnusedAtlases(fAtlasMgr, &fAtlas); + + return false; +} + bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { #if 0 // testing hack to force us to flush our cache often static int gCounter; @@ -176,6 +231,7 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { GrAssert(scaler); GrAssert(fCache.contains(glyph)); if (glyph->fAtlas) { + glyph->fAtlas->setUsed(true); return true; } @@ -191,7 +247,7 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { return false; } - GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(), + GrAtlas* atlas = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), glyph->height(), storage.get(), fMaskFormat, &glyph->fAtlasLocation); @@ -199,7 +255,7 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { return false; } - // update fAtlas as well, since they may be chained in a linklist - glyph->fAtlas = fAtlas = atlas; + glyph->fAtlas = atlas; + atlas->setUsed(true); return true; } diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h index 25ced6de9b..e359e267cf 100644 --- a/src/gpu/GrTextStrike.h +++ b/src/gpu/GrTextStrike.h @@ -46,6 +46,9 @@ public: } GrAtlas* getAtlas() const { return fAtlas; } + // returns true if an atlas was removed + bool removeUnusedAtlases(); + public: // for LRU GrTextStrike* fPrev; @@ -81,6 +84,9 @@ public: void purgeExceptFor(GrTextStrike*); + // remove an unused atlas and its strike (if necessary) + void freeAtlasExceptFor(GrTextStrike*); + // testing int countStrikes() const { return fCache.getArray().count(); } const GrTextStrike* strikeAt(int index) const { |