Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2012 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 #ifndef GrTextureStripAtlas_DEFINED
      9 #define GrTextureStripAtlas_DEFINED
     10 
     11 #include "SkBitmap.h"
     12 #include "SkOpts.h"
     13 #include "SkGr.h"
     14 #include "SkTDArray.h"
     15 #include "SkTDynamicHash.h"
     16 #include "SkTypes.h"
     17 
     18 class GrSurfaceContext;
     19 class GrTextureProxy;
     20 
     21 /**
     22  * Maintains a single large texture whose rows store many textures of a small fixed height,
     23  * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
     24  */
     25 class GrTextureStripAtlas {
     26 public:
     27     /**
     28      * Descriptor struct which we'll use as a hash table key
     29      **/
     30     struct Desc {
     31         Desc() { sk_bzero(this, sizeof(*this)); }
     32         GrContext* fContext;
     33         GrPixelConfig fConfig;
     34         uint16_t fWidth, fHeight, fRowHeight;
     35         uint16_t fUnusedPadding;
     36         bool operator==(const Desc& other) const {
     37             return 0 == memcmp(this, &other, sizeof(Desc));
     38         }
     39     };
     40 
     41     /**
     42      * Try to find an atlas with the required parameters, creates a new one if necessary
     43      */
     44     static GrTextureStripAtlas* GetAtlas(const Desc& desc);
     45 
     46     ~GrTextureStripAtlas();
     47 
     48     /**
     49      * Add a texture to the atlas
     50      *  @param data Bitmap data to copy into the row
     51      *  @return The row index we inserted into, or -1 if we failed to find an open row. The caller
     52      *      is responsible for calling unlockRow() with this row index when it's done with it.
     53      */
     54     int lockRow(const SkBitmap& data);
     55     void unlockRow(int row);
     56 
     57     /**
     58      * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
     59      * texture coordinate in [0, 1] that we can use in a shader.
     60      *
     61      * If a regular texture access without using the atlas looks like:
     62      *
     63      *      texture2D(sampler, vec2(x, y))
     64      *
     65      * Then when using the atlas we'd replace it with:
     66      *
     67      *       texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
     68      *
     69      * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
     70      * atlas and scaleFactor, returned by getNormalizedTexelHeight, is the normalized height of
     71      * one texel row.
     72      */
     73     SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
     74     SkScalar getNormalizedTexelHeight() const { return fNormalizedYHeight; }
     75 
     76     GrContext* getContext() const { return fDesc.fContext; }
     77 
     78     sk_sp<GrTextureProxy> asTextureProxyRef() const;
     79 
     80 private:
     81 
     82     // Key to indicate an atlas row without any meaningful data stored in it
     83     const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
     84 
     85     /**
     86      * The state of a single row in our cache, next/prev pointers allow these to be chained
     87      * together to represent LRU status
     88      */
     89     struct AtlasRow : SkNoncopyable {
     90         AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(nullptr), fPrev(nullptr) { }
     91         // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
     92         uint32_t fKey;
     93         // How many times this has been locked (0 == unlocked)
     94         int32_t fLocks;
     95         // We maintain an LRU linked list between unlocked nodes with these pointers
     96         AtlasRow* fNext;
     97         AtlasRow* fPrev;
     98     };
     99 
    100     /**
    101      * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
    102      */
    103     GrTextureStripAtlas(Desc desc);
    104 
    105     void lockTexture();
    106     void unlockTexture();
    107 
    108     /**
    109      * Initialize our LRU list (if one already exists, clear it and start anew)
    110      */
    111     void initLRU();
    112 
    113     /**
    114      * Grabs the least recently used free row out of the LRU list, returns nullptr if no rows are free.
    115      */
    116     AtlasRow* getLRU();
    117 
    118     void appendLRU(AtlasRow* row);
    119     void removeFromLRU(AtlasRow* row);
    120 
    121     /**
    122      * Searches the key table for a key and returns the index if found; if not found, it returns
    123      * the bitwise not of the index at which we could insert the key to maintain a sorted list.
    124      **/
    125     int searchByKey(uint32_t key);
    126 
    127     /**
    128      * Compare two atlas rows by key, so we can sort/search by key
    129      */
    130     static bool KeyLess(const AtlasRow& lhs, const AtlasRow& rhs) {
    131         return lhs.fKey < rhs.fKey;
    132     }
    133 
    134 #ifdef SK_DEBUG
    135     void validate();
    136 #endif
    137 
    138     /**
    139      * Clean up callback registered with GrContext. Allows this class to
    140      * free up any allocated AtlasEntry and GrTextureStripAtlas objects
    141      */
    142     static void CleanUp(const GrContext* context, void* info);
    143 
    144     // Hash table entry for atlases
    145     class AtlasEntry : public ::SkNoncopyable {
    146     public:
    147         // for SkTDynamicHash
    148         static const Desc& GetKey(const AtlasEntry& entry) { return entry.fDesc; }
    149         static uint32_t Hash(const Desc& desc) { return SkOpts::hash(&desc, sizeof(Desc)); }
    150 
    151         // AtlasEntry proper
    152         AtlasEntry() : fAtlas(nullptr) {}
    153         ~AtlasEntry() { delete fAtlas; }
    154         Desc fDesc;
    155         GrTextureStripAtlas* fAtlas;
    156     };
    157 
    158     class Hash;
    159     static Hash* gAtlasCache;
    160 
    161     static Hash* GetCache();
    162 
    163     // We increment gCacheCount for each atlas
    164     static int32_t gCacheCount;
    165 
    166     // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
    167     // get a texture back from the texture cache, that it's the same one we last used.
    168     const int32_t fCacheKey;
    169 
    170     // Total locks on all rows (when this reaches zero, we can unlock our texture)
    171     int32_t fLockedRows;
    172 
    173     const Desc fDesc;
    174     const uint16_t fNumRows;
    175     sk_sp<GrSurfaceContext> fTexContext;
    176 
    177     SkScalar fNormalizedYHeight;
    178 
    179     // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
    180     // order that they appear in our texture, this means we can subtract this pointer from a row
    181     // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
    182     AtlasRow* fRows;
    183 
    184     // Head and tail for linked list of least-recently-used rows (front = least recently used).
    185     // Note that when a texture is locked, it gets removed from this list until it is unlocked.
    186     AtlasRow* fLRUFront;
    187     AtlasRow* fLRUBack;
    188 
    189     // A list of pointers to AtlasRows that currently contain cached images, sorted by key
    190     SkTDArray<AtlasRow*> fKeyTable;
    191 };
    192 
    193 #endif
    194