aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrTextureStripAtlas.h
blob: b948566e2797c3c0943e7812b73d433af193b25a (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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * 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 "SkGr.h"
#include "SkOpts.h"
#include "SkRefCnt.h"
#include "SkTDArray.h"
#include "SkTDynamicHash.h"
#include "SkTypes.h"

class GrSurfaceContext;
class GrTextureProxy;

/**
 * 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 SkRefCnt {
public:
    /**
     * Descriptor struct which we'll use as a hash table key
     */
    struct Desc {
        Desc() { sk_bzero(this, sizeof(*this)); }
        GrPixelConfig fConfig;
        uint16_t fWidth, fHeight, fRowHeight;
        uint16_t fUnusedPadding;
        bool operator==(const Desc& other) const {
            return 0 == memcmp(this, &other, sizeof(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(GrContext*, const SkBitmap&);

    /**
     * This is intended to be used when cloning a processor that already holds a lock. It is
     * assumed that the row already has at least one lock.
     */
    void lockRow(int row);
    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, float2(x, y))
     *
     * Then when using the atlas we'd replace it with:
     *
     *       texture2D(sampler, float2(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 getNormalizedTexelHeight, is the normalized height of
     * one texel row.
     */
    SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
    SkScalar getNormalizedTexelHeight() const { return fNormalizedYHeight; }

    sk_sp<GrTextureProxy> asTextureProxyRef() const;

private:
    friend class GrTextureStripAtlasManager; // for ctor

    static uint32_t CreateUniqueID();

    // 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 : ::SkNoncopyable {
        AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(nullptr), fPrev(nullptr) { }
        // 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;
    };

    /**
     * Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
     */
    GrTextureStripAtlas(const Desc& desc);

    void lockTexture(GrContext*);
    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 nullptr 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 bool KeyLess(const AtlasRow& lhs, const AtlasRow& rhs) {
        return lhs.fKey < rhs.fKey;
    }

#ifdef SK_DEBUG
    void validate();
#endif

    // A unique ID for this atlas, 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 uint32_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;
    sk_sp<GrSurfaceContext> fTexContext;

    SkScalar fNormalizedYHeight;

    // 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;
};

class GrTextureStripAtlasManager {
public:
    GrTextureStripAtlasManager() {}
    ~GrTextureStripAtlasManager();

    void abandon();

    /**
     * Try to find an atlas with the required parameters, creates a new one if necessary
     */
    sk_sp<GrTextureStripAtlas> refAtlas(const GrTextureStripAtlas::Desc&);

private:
    void deleteAllAtlases();

    // Hash table entry for atlases
    class AtlasEntry : public ::SkNoncopyable {
    public:
        AtlasEntry(const GrTextureStripAtlas::Desc& desc, sk_sp<GrTextureStripAtlas> atlas)
            : fDesc(desc)
            , fAtlas(std::move(atlas)) {
        }
        ~AtlasEntry() { }

        // for SkTDynamicHash
        static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) {
            return entry.fDesc;
        }
        static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) {
            return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc));
        }

        const GrTextureStripAtlas::Desc fDesc;
        sk_sp<GrTextureStripAtlas> fAtlas;
    };

    typedef SkTDynamicHash<AtlasEntry, GrTextureStripAtlas::Desc> AtlasHash;

    AtlasHash fAtlasCache;
};

#endif