Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2015 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 "GrBatchFontCache.h"
      9 #include "GrFontAtlasSizes.h"
     10 #include "GrGpu.h"
     11 #include "GrRectanizer.h"
     12 #include "GrSurfacePriv.h"
     13 #include "SkString.h"
     14 
     15 #include "SkDistanceFieldGen.h"
     16 
     17 ///////////////////////////////////////////////////////////////////////////////
     18 
     19 static GrBatchAtlas* make_atlas(GrContext* context, GrPixelConfig config,
     20                                 int textureWidth, int textureHeight,
     21                                 int numPlotsX, int numPlotsY) {
     22     GrSurfaceDesc desc;
     23     desc.fFlags = kNone_GrSurfaceFlags;
     24     desc.fWidth = textureWidth;
     25     desc.fHeight = textureHeight;
     26     desc.fConfig = config;
     27 
     28     // We don't want to flush the context so we claim we're in the middle of flushing so as to
     29     // guarantee we do not recieve a texture with pending IO
     30     GrTexture* texture = context->textureProvider()->refScratchTexture(
     31         desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
     32     if (!texture) {
     33         return NULL;
     34     }
     35     return SkNEW_ARGS(GrBatchAtlas, (texture, numPlotsX, numPlotsY));
     36 }
     37 
     38 bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
     39     int index = MaskFormatToAtlasIndex(format);
     40     if (!fAtlases[index]) {
     41         GrPixelConfig config = this->getPixelConfig(format);
     42         if (kA8_GrMaskFormat == format) {
     43             fAtlases[index] = make_atlas(fContext, config,
     44                                          GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
     45                                          GR_FONT_ATLAS_TEXTURE_HEIGHT,
     46                                          GR_FONT_ATLAS_A8_NUM_PLOTS_X,
     47                                          GR_FONT_ATLAS_NUM_PLOTS_Y);
     48         } else {
     49             fAtlases[index] = make_atlas(fContext, config,
     50                                          GR_FONT_ATLAS_TEXTURE_WIDTH,
     51                                          GR_FONT_ATLAS_TEXTURE_HEIGHT,
     52                                          GR_FONT_ATLAS_NUM_PLOTS_X,
     53                                          GR_FONT_ATLAS_NUM_PLOTS_Y);
     54         }
     55 
     56         // Atlas creation can fail
     57         if (fAtlases[index]) {
     58             fAtlases[index]->registerEvictionCallback(&GrBatchFontCache::HandleEviction,
     59                                                       (void*)this);
     60         } else {
     61             return false;
     62         }
     63     }
     64     return true;
     65 }
     66 
     67 GrBatchFontCache::GrBatchFontCache(GrContext* context)
     68     : fContext(context)
     69     , fPreserveStrike(NULL) {
     70     for (int i = 0; i < kMaskFormatCount; ++i) {
     71         fAtlases[i] = NULL;
     72     }
     73 }
     74 
     75 GrBatchFontCache::~GrBatchFontCache() {
     76     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
     77     while (!iter.done()) {
     78         (*iter).unref();
     79         ++iter;
     80     }
     81     for (int i = 0; i < kMaskFormatCount; ++i) {
     82         SkDELETE(fAtlases[i]);
     83     }
     84 }
     85 
     86 void GrBatchFontCache::freeAll() {
     87     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
     88     while (!iter.done()) {
     89         (*iter).unref();
     90         ++iter;
     91     }
     92     fCache.rewind();
     93     for (int i = 0; i < kMaskFormatCount; ++i) {
     94         SkDELETE(fAtlases[i]);
     95         fAtlases[i] = NULL;
     96     }
     97 }
     98 
     99 GrPixelConfig GrBatchFontCache::getPixelConfig(GrMaskFormat format) const {
    100     static const GrPixelConfig kPixelConfigs[] = {
    101         kAlpha_8_GrPixelConfig,
    102         kRGB_565_GrPixelConfig,
    103         kSkia8888_GrPixelConfig
    104     };
    105     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
    106 
    107     return kPixelConfigs[format];
    108 }
    109 
    110 void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
    111     GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
    112 
    113     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
    114     for (; !iter.done(); ++iter) {
    115         GrBatchTextStrike* strike = &*iter;
    116         strike->removeID(id);
    117 
    118         // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
    119         // triggered the eviction
    120         if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
    121             fontCache->fCache.remove(*(strike->fFontScalerKey));
    122             strike->fIsAbandoned = true;
    123             strike->unref();
    124         }
    125     }
    126 }
    127 
    128 void GrBatchFontCache::dump() const {
    129     static int gDumpCount = 0;
    130     for (int i = 0; i < kMaskFormatCount; ++i) {
    131         if (fAtlases[i]) {
    132             GrTexture* texture = fAtlases[i]->getTexture();
    133             if (texture) {
    134                 SkString filename;
    135 #ifdef SK_BUILD_FOR_ANDROID
    136                 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
    137 #else
    138                 filename.printf("fontcache_%d%d.png", gDumpCount, i);
    139 #endif
    140                 texture->surfacePriv().savePixels(filename.c_str());
    141             }
    142         }
    143     }
    144     ++gDumpCount;
    145 }
    146 
    147 ///////////////////////////////////////////////////////////////////////////////
    148 
    149 /*
    150     The text strike is specific to a given font/style/matrix setup, which is
    151     represented by the GrHostFontScaler object we are given in getGlyph().
    152 
    153     We map a 32bit glyphID to a GrGlyph record, which in turn points to a
    154     atlas and a position within that texture.
    155  */
    156 
    157 GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
    158     : fFontScalerKey(SkRef(key))
    159     , fPool(9/*start allocations at 512 bytes*/)
    160     , fAtlasedGlyphs(0)
    161     , fIsAbandoned(false) {
    162 
    163     fBatchFontCache = cache;     // no need to ref, it won't go away before we do
    164 }
    165 
    166 GrBatchTextStrike::~GrBatchTextStrike() {
    167     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    168     while (!iter.done()) {
    169         (*iter).free();
    170         ++iter;
    171     }
    172 }
    173 
    174 GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed,
    175                                           GrFontScaler* scaler) {
    176     SkIRect bounds;
    177     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
    178         if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
    179             return NULL;
    180         }
    181     } else {
    182         if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
    183             return NULL;
    184         }
    185     }
    186     GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
    187 
    188     GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
    189     glyph->init(packed, bounds, format);
    190     fCache.add(glyph);
    191     return glyph;
    192 }
    193 
    194 void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
    195     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    196     while (!iter.done()) {
    197         if (id == (*iter).fID) {
    198             (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
    199             fAtlasedGlyphs--;
    200             SkASSERT(fAtlasedGlyphs >= 0);
    201         }
    202         ++iter;
    203     }
    204 }
    205 
    206 bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
    207                                         GrFontScaler* scaler) {
    208     SkASSERT(glyph);
    209     SkASSERT(scaler);
    210     SkASSERT(fCache.find(glyph->fPackedID));
    211     SkASSERT(NULL == glyph->fPlot);
    212 
    213     SkAutoUnref ar(SkSafeRef(scaler));
    214 
    215     int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
    216 
    217     size_t size = glyph->fBounds.area() * bytesPerPixel;
    218     GrAutoMalloc<1024> storage(size);
    219 
    220     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
    221         if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
    222                                            glyph->height(),
    223                                            storage.get())) {
    224             return false;
    225         }
    226     } else {
    227         if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
    228                                          glyph->height(),
    229                                          glyph->width() * bytesPerPixel,
    230                                          storage.get())) {
    231             return false;
    232         }
    233     }
    234 
    235     bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat,
    236                                                glyph->width(), glyph->height(),
    237                                                storage.get(), &glyph->fAtlasLocation);
    238     if (success) {
    239         fAtlasedGlyphs++;
    240     }
    241     return success;
    242 }
    243