aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/effects/GrTextureStripAtlas.h
blob: 1e1e5088c7a11986b554b42566235f9939a8883d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrTextureStripAtlas_DEFINED
#define GrTextureStripAtlas_DEFINED

#include "SkBitmap.h"
#include "GrTHashCache.h"
#include "SkGr.h"
#include "SkTDArray.h"
#include "GrBinHashKey.h"

/**
 * Maintains a single large texture whose rows store many textures of a small fixed height,
 * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
 */
class GrTextureStripAtlas {
public:
    /**
     * Descriptor struct which we'll use as a hash table key
     **/
    struct Desc {
        Desc() { memset(this, 0, sizeof(*this)); }
        uint16_t fWidth, fHeight, fRowHeight;
        GrPixelConfig fConfig;
        GrContext* fContext;
        const uint32_t* asKey() const { return reinterpret_cast<const uint32_t*>(this); }
    };

    /**
     * Try to find an atlas with the required parameters, creates a new one if necessary
     */
    static GrTextureStripAtlas* GetAtlas(const Desc& desc);

    ~GrTextureStripAtlas();

    /**
     * Add a texture to the atlas
     *  @param data Bitmap data to copy into the row
     *  @return The row index we inserted into, or -1 if we failed to find an open row. The caller
     *      is responsible for calling unlockRow() with this row index when it's done with it.
     */
    int lockRow(const SkBitmap& data);
    void unlockRow(int row);

    /**
     * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
     * texture coordinate in [0, 1] that we can use in a shader.
     *
     * If a regular texture access without using the atlas looks like:
     *
     *      texture2D(sampler, vec2(x, y))
     *
     * Then when using the atlas we'd replace it with:
     *
     *       texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
     *
     * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
     * atlas and scaleFactor, returned by getVerticalScaleFactor(), is the y-scale of the row,
     * relative to the height of the overall atlas texture.
     */
    SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
    SkScalar getVerticalScaleFactor() const { return SkIntToScalar(fDesc.fRowHeight) / fDesc.fHeight; }

    GrContext* getContext() const { return fDesc.fContext; }
    GrTexture* getTexture() const { return fTexture; }

private:

    // Key to indicate an atlas row without any meaningful data stored in it
    const static uint32_t kEmptyAtlasRowKey = 0xffffffff;

    /**
     * The state of a single row in our cache, next/prev pointers allow these to be chained
     * together to represent LRU status
     */
    struct AtlasRow : public GrNoncopyable {
        AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(NULL), fPrev(NULL) { }
        // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
        uint32_t fKey;
        // How many times this has been locked (0 == unlocked)
        int32_t fLocks;
        // We maintain an LRU linked list between unlocked nodes with these pointers
        AtlasRow* fNext;
        AtlasRow* fPrev;
    };

    /**
     * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
     */
    GrTextureStripAtlas(Desc desc);

    void lockTexture();
    void unlockTexture();

    /**
     * Initialize our LRU list (if one already exists, clear it and start anew)
     */
    void initLRU();

    /**
     * Grabs the least recently used free row out of the LRU list, returns NULL if no rows are free.
     */
    AtlasRow* getLRU();

    void appendLRU(AtlasRow* row);
    void removeFromLRU(AtlasRow* row);

    /**
     * Searches the key table for a key and returns the index if found; if not found, it returns
     * the bitwise not of the index at which we could insert the key to maintain a sorted list.
     **/
    int searchByKey(uint32_t key);

    /**
     * Compare two atlas rows by key, so we can sort/search by key
     */
    static int compareKeys(const AtlasRow* lhs, const AtlasRow* rhs) {
        return lhs->fKey - rhs->fKey;
    }

#ifdef SK_DEBUG
    void validate();
#endif

    /**
     * Clean up callback registered with GrContext. Allows this class to
     * free up any allocated AtlasEntry and GrTextureStripAtlas objects
     */
    static void CleanUp(const GrContext* context, void* info);

    // Hash table entry for atlases
    class AtlasEntry;
    typedef GrTBinHashKey<AtlasEntry, sizeof(GrTextureStripAtlas::Desc)> AtlasHashKey;
    class AtlasEntry : public ::GrNoncopyable {
    public:
        AtlasEntry() : fAtlas(NULL) {}
        ~AtlasEntry() { SkDELETE(fAtlas); }
        int compare(const AtlasHashKey& key) const { return fKey.compare(key); }
        AtlasHashKey fKey;
        GrTextureStripAtlas* fAtlas;
    };

    static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* gAtlasCache;

    static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* GetCache();

    // We increment gCacheCount for each atlas
    static int32_t gCacheCount;

    // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
    // get a texture back from the texture cache, that it's the same one we last used.
    const int32_t fCacheKey;

    // Total locks on all rows (when this reaches zero, we can unlock our texture)
    int32_t fLockedRows;

    const Desc fDesc;
    const uint16_t fNumRows;
    GrTexture* fTexture;

    // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
    // order that they appear in our texture, this means we can subtract this pointer from a row
    // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
    AtlasRow* fRows;

    // Head and tail for linked list of least-recently-used rows (front = least recently used).
    // Note that when a texture is locked, it gets removed from this list until it is unlocked.
    AtlasRow* fLRUFront;
    AtlasRow* fLRUBack;

    // A list of pointers to AtlasRows that currently contain cached images, sorted by key
    SkTDArray<AtlasRow*> fKeyTable;
};

#endif