Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2010 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrAtlas.h"
      9 #include "GrGpu.h"
     10 #include "GrRectanizer.h"
     11 #include "GrTextStrike.h"
     12 #include "GrTextStrike_impl.h"
     13 
     14 SK_DEFINE_INST_COUNT(GrFontScaler)
     15 SK_DEFINE_INST_COUNT(GrKey)
     16 
     17 ///////////////////////////////////////////////////////////////////////////////
     18 
     19 #define FONT_CACHE_STATS 0
     20 #if FONT_CACHE_STATS
     21 static int g_PurgeCount = 0;
     22 #endif
     23 
     24 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
     25     gpu->ref();
     26     fAtlasMgr = NULL;
     27 
     28     fHead = fTail = NULL;
     29 }
     30 
     31 GrFontCache::~GrFontCache() {
     32     fCache.deleteAll();
     33     delete fAtlasMgr;
     34     fGpu->unref();
     35 #if FONT_CACHE_STATS
     36       GrPrintf("Num purges: %d\n", g_PurgeCount);
     37 #endif
     38 }
     39 
     40 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
     41                                           const Key& key) {
     42     if (NULL == fAtlasMgr) {
     43         fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
     44     }
     45     GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
     46                                       (this, scaler->getKey(),
     47                                        scaler->getMaskFormat(), fAtlasMgr));
     48     fCache.insert(key, strike);
     49 
     50     if (fHead) {
     51         fHead->fPrev = strike;
     52     } else {
     53         GrAssert(NULL == fTail);
     54         fTail = strike;
     55     }
     56     strike->fPrev = NULL;
     57     strike->fNext = fHead;
     58     fHead = strike;
     59 
     60     return strike;
     61 }
     62 
     63 void GrFontCache::freeAll() {
     64     fCache.deleteAll();
     65     delete fAtlasMgr;
     66     fAtlasMgr = NULL;
     67     fHead = NULL;
     68     fTail = NULL;
     69 }
     70 
     71 void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
     72     GrTextStrike* strike = fTail;
     73     bool purge = true;
     74     while (strike) {
     75         if (strike == preserveStrike) {
     76             strike = strike->fPrev;
     77             continue;
     78         }
     79         GrTextStrike* strikeToPurge = strike;
     80         strike = strikeToPurge->fPrev;
     81         if (purge) {
     82             // keep purging if we won't free up any atlases with this strike.
     83             purge = (NULL == strikeToPurge->fAtlas);
     84             int index = fCache.slowFindIndex(strikeToPurge);
     85             GrAssert(index >= 0);
     86             fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
     87             this->detachStrikeFromList(strikeToPurge);
     88             delete strikeToPurge;
     89         } else {
     90             // for the remaining strikes, we just mark them unused
     91             GrAtlas::MarkAllUnused(strikeToPurge->fAtlas);
     92         }
     93     }
     94 #if FONT_CACHE_STATS
     95     ++g_PurgeCount;
     96 #endif
     97 }
     98 
     99 void GrFontCache::freeAtlasExceptFor(GrTextStrike* preserveStrike) {
    100     GrTextStrike* strike = fTail;
    101     while (strike) {
    102         if (strike == preserveStrike) {
    103             strike = strike->fPrev;
    104             continue;
    105         }
    106         GrTextStrike* strikeToPurge = strike;
    107         strike = strikeToPurge->fPrev;
    108         if (strikeToPurge->removeUnusedAtlases()) {
    109             if (NULL == strikeToPurge->fAtlas) {
    110                 int index = fCache.slowFindIndex(strikeToPurge);
    111                 GrAssert(index >= 0);
    112                 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
    113                 this->detachStrikeFromList(strikeToPurge);
    114                 delete strikeToPurge;
    115             }
    116             break;
    117         }
    118     }
    119 }
    120 
    121 #if GR_DEBUG
    122 void GrFontCache::validate() const {
    123     int count = fCache.count();
    124     if (0 == count) {
    125         GrAssert(!fHead);
    126         GrAssert(!fTail);
    127     } else if (1 == count) {
    128         GrAssert(fHead == fTail);
    129     } else {
    130         GrAssert(fHead != fTail);
    131     }
    132 
    133     int count2 = 0;
    134     const GrTextStrike* strike = fHead;
    135     while (strike) {
    136         count2 += 1;
    137         strike = strike->fNext;
    138     }
    139     GrAssert(count == count2);
    140 
    141     count2 = 0;
    142     strike = fTail;
    143     while (strike) {
    144         count2 += 1;
    145         strike = strike->fPrev;
    146     }
    147     GrAssert(count == count2);
    148 }
    149 #endif
    150 
    151 ///////////////////////////////////////////////////////////////////////////////
    152 
    153 #if GR_DEBUG
    154     static int gCounter;
    155 #endif
    156 
    157 /*
    158     The text strike is specific to a given font/style/matrix setup, which is
    159     represented by the GrHostFontScaler object we are given in getGlyph().
    160 
    161     We map a 32bit glyphID to a GrGlyph record, which in turn points to a
    162     atlas and a position within that texture.
    163  */
    164 
    165 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
    166                            GrMaskFormat format,
    167                            GrAtlasMgr* atlasMgr) : fPool(64) {
    168     fFontScalerKey = key;
    169     fFontScalerKey->ref();
    170 
    171     fFontCache = cache;     // no need to ref, it won't go away before we do
    172     fAtlasMgr = atlasMgr;   // no need to ref, it won't go away before we do
    173     fAtlas = NULL;
    174 
    175     fMaskFormat = format;
    176 
    177 #if GR_DEBUG
    178 //    GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
    179     gCounter += 1;
    180 #endif
    181 }
    182 
    183 // these signatures are needed because they're used with
    184 // SkTDArray::visitAll() (see destructor & removeUnusedAtlases())
    185 static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
    186 
    187 static void invalidate_glyph(GrGlyph*& glyph) {
    188     if (glyph->fAtlas && !glyph->fAtlas->used()) {
    189         glyph->fAtlas = NULL;
    190     }
    191 }
    192 
    193 GrTextStrike::~GrTextStrike() {
    194     GrAtlas::FreeLList(fAtlas);
    195     fFontScalerKey->unref();
    196     fCache.getArray().visitAll(free_glyph);
    197 
    198 #if GR_DEBUG
    199     gCounter -= 1;
    200 //    GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
    201 #endif
    202 }
    203 
    204 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
    205                                      GrFontScaler* scaler) {
    206     SkIRect bounds;
    207     if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
    208         return NULL;
    209     }
    210 
    211     GrGlyph* glyph = fPool.alloc();
    212     glyph->init(packed, bounds);
    213     fCache.insert(packed, glyph);
    214     return glyph;
    215 }
    216 
    217 bool GrTextStrike::removeUnusedAtlases() {
    218     fCache.getArray().visitAll(invalidate_glyph);
    219     return GrAtlas::RemoveUnusedAtlases(fAtlasMgr, &fAtlas);
    220 
    221     return false;
    222 }
    223 
    224 bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
    225 #if 0   // testing hack to force us to flush our cache often
    226     static int gCounter;
    227     if ((++gCounter % 10) == 0) return false;
    228 #endif
    229 
    230     GrAssert(glyph);
    231     GrAssert(scaler);
    232     GrAssert(fCache.contains(glyph));
    233     if (glyph->fAtlas) {
    234         glyph->fAtlas->setUsed(true);
    235         return true;
    236     }
    237 
    238     GrAutoRef ar(scaler);
    239 
    240     int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
    241     size_t size = glyph->fBounds.area() * bytesPerPixel;
    242     SkAutoSMalloc<1024> storage(size);
    243     if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
    244                                      glyph->height(),
    245                                      glyph->width() * bytesPerPixel,
    246                                      storage.get())) {
    247         return false;
    248     }
    249 
    250     GrAtlas* atlas = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
    251                                            glyph->height(), storage.get(),
    252                                            fMaskFormat,
    253                                            &glyph->fAtlasLocation);
    254     if (NULL == atlas) {
    255         return false;
    256     }
    257 
    258     glyph->fAtlas = atlas;
    259     atlas->setUsed(true);
    260     return true;
    261 }
    262