blob: 10b50c01897744822750d08433015cfefbc1532e [file] [log] [blame]
rileya@google.com2e2aedc2012-08-13 20:28:48 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#ifndef GrTextureStripAtlas_DEFINED
10#define GrTextureStripAtlas_DEFINED
11
12#include "SkBitmap.h"
13#include "GrTHashCache.h"
14#include "SkGr.h"
15#include "SkTDArray.h"
16
17/**
18 * Maintains a single large texture whose rows store many textures of a small fixed height,
19 * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
20 */
21class GrTextureStripAtlas {
22public:
23 GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetTextureStripAtlasDomain)
24
25 /**
26 * Descriptor struct which we'll use as a hash table key
27 **/
28 struct Desc {
29 Desc() { memset(this, 0, sizeof(*this)); }
30 uint16_t fWidth, fHeight, fRowHeight;
31 GrPixelConfig fConfig;
32 GrContext* fContext;
33 const uint32_t* asKey() const { return reinterpret_cast<const uint32_t*>(this); }
34 };
35
36 /**
37 * Try to find an atlas with the required parameters, creates a new one if necessary
38 */
39 static GrTextureStripAtlas* GetAtlas(const Desc& desc);
40
41 ~GrTextureStripAtlas();
42
43 /**
44 * Add a texture to the atlas
45 * @param data Bitmap data to copy into the row
46 * @return The row index we inserted into, or -1 if we failed to find an open row. The caller
47 * is responsible for calling unlockRow() with this row index when it's done with it.
48 */
49 int lockRow(const SkBitmap& data);
50 void unlockRow(int row);
51
52 /**
53 * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
54 * texture coordinate in [0, 1] that we can use in a shader.
55 *
56 * If a regular texture access without using the atlas looks like:
57 *
58 * texture2D(sampler, vec2(x, y))
59 *
60 * Then when using the atlas we'd replace it with:
61 *
62 * texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
63 *
64 * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
65 * atlas and scaleFactor, returned by getVerticalScaleFactor(), is the y-scale of the row,
66 * relative to the height of the overall atlas texture.
67 */
68 GrScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
69 GrScalar getVerticalScaleFactor() const { return SkIntToScalar(fDesc.fRowHeight) / fDesc.fHeight; }
70
71 GrContext* getContext() const { return fDesc.fContext; }
72 GrTexture* getTexture() const { return fEntry.texture(); }
73
74private:
75
76 // Key to indicate an atlas row without any meaningful data stored in it
77 const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
78
79 /**
80 * The state of a single row in our cache, next/prev pointers allow these to be chained
81 * together to represent LRU status
82 */
83 struct AtlasRow : public GrNoncopyable {
84 AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(NULL), fPrev(NULL) { }
85 // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
86 uint32_t fKey;
87 // How many times this has been locked (0 == unlocked)
88 int32_t fLocks;
89 // We maintain an LRU linked list between unlocked nodes with these pointers
90 AtlasRow* fNext;
91 AtlasRow* fPrev;
92 };
93
94 /**
95 * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
96 */
97 GrTextureStripAtlas(Desc desc);
98
99 void lockTexture();
100 void unlockTexture();
101
102 /**
103 * Initialize our LRU list (if one already exists, clear it and start anew)
104 */
105 void initLRU();
106
107 /**
108 * Grabs the least recently used free row out of the LRU list, returns NULL if no rows are free.
109 */
110 AtlasRow* getLRU();
111
112 void appendLRU(AtlasRow* row);
113 void removeFromLRU(AtlasRow* row);
114
115 /**
116 * Searches the key table for a key and returns the index if found; if not found, it returns
117 * the bitwise not of the index at which we could insert the key to maintain a sorted list.
118 **/
119 int searchByKey(uint32_t key);
120
121 /**
122 * Compare two atlas rows by key, so we can sort/search by key
123 */
124 static int compareKeys(const AtlasRow* lhs, const AtlasRow* rhs) {
125 return lhs->fKey - rhs->fKey;
126 }
127
128#ifdef SK_DEBUG
129 void validate();
130#endif
131
132 // We increment fCacheCount for each atlas, kCacheDomain is just an arbitrary number we add to
133 // it in hopes of preventing collisions (this is only until resource domains get worked out).
134 static int32_t gCacheCount;
135 static const uint64_t kCacheDomain = 0xfffffffffffffff0;
136
137 // A unique ID for this texture (formed with: kCacheDomain + gCacheCount++), so we can be sure
138 // that if we get a texture back from the texture cache, that it's the same one we last used.
139 const uint64_t fCacheID;
140
141 // Total locks on all rows (when this reaches zero, we can unlock our texture)
142 int32_t fLockedRows;
143
144 const Desc fDesc;
145 const uint16_t fNumRows;
146 GrContext::TextureCacheEntry fEntry;
147
148 // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
149 // order that they appear in our texture, this means we can subtract this pointer from a row
150 // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
151 AtlasRow* fRows;
152
153 // Head and tail for linked list of least-recently-used rows (front = least recently used).
154 // Note that when a texture is locked, it gets removed from this list until it is unlocked.
155 AtlasRow* fLRUFront;
156 AtlasRow* fLRUBack;
157
158 // A list of pointers to AtlasRows that currently contain cached images, sorted by key
159 SkTDArray<AtlasRow*> fKeyTable;
160};
161
162#endif
163