aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/gpu/GrAtlas.cpp62
-rw-r--r--src/gpu/GrAtlas.h27
-rw-r--r--src/gpu/GrTextContext.cpp6
-rw-r--r--src/gpu/GrTextStrike.cpp80
-rw-r--r--src/gpu/GrTextStrike.h6
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 {