blob: 210d88ec906cfac5f58edd8f7869f4137fa1c22f [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"
robertphillips@google.comcdb426d2012-09-24 19:33:59 +000016#include "GrBinHashKey.h"
rileya@google.com2e2aedc2012-08-13 20:28:48 +000017
18/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +000019 * Maintains a single large texture whose rows store many textures of a small fixed height,
rileya@google.com2e2aedc2012-08-13 20:28:48 +000020 * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
21 */
22class GrTextureStripAtlas {
23public:
24 GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetTextureStripAtlasDomain)
25
26 /**
27 * Descriptor struct which we'll use as a hash table key
28 **/
29 struct Desc {
30 Desc() { memset(this, 0, sizeof(*this)); }
31 uint16_t fWidth, fHeight, fRowHeight;
32 GrPixelConfig fConfig;
33 GrContext* fContext;
34 const uint32_t* asKey() const { return reinterpret_cast<const uint32_t*>(this); }
35 };
36
37 /**
38 * Try to find an atlas with the required parameters, creates a new one if necessary
39 */
40 static GrTextureStripAtlas* GetAtlas(const Desc& desc);
41
42 ~GrTextureStripAtlas();
43
44 /**
45 * Add a texture to the atlas
46 * @param data Bitmap data to copy into the row
47 * @return The row index we inserted into, or -1 if we failed to find an open row. The caller
48 * is responsible for calling unlockRow() with this row index when it's done with it.
49 */
50 int lockRow(const SkBitmap& data);
51 void unlockRow(int row);
52
rmistry@google.comfbfcd562012-08-23 18:09:54 +000053 /**
54 * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
rileya@google.com2e2aedc2012-08-13 20:28:48 +000055 * texture coordinate in [0, 1] that we can use in a shader.
56 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000057 * If a regular texture access without using the atlas looks like:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000058 *
59 * texture2D(sampler, vec2(x, y))
60 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000061 * Then when using the atlas we'd replace it with:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000062 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000063 * texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
rileya@google.com2e2aedc2012-08-13 20:28:48 +000064 *
65 * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
rmistry@google.comfbfcd562012-08-23 18:09:54 +000066 * atlas and scaleFactor, returned by getVerticalScaleFactor(), is the y-scale of the row,
rileya@google.com2e2aedc2012-08-13 20:28:48 +000067 * relative to the height of the overall atlas texture.
68 */
bsalomon@google.com81712882012-11-01 17:12:34 +000069 SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
70 SkScalar getVerticalScaleFactor() const { return SkIntToScalar(fDesc.fRowHeight) / fDesc.fHeight; }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000071
72 GrContext* getContext() const { return fDesc.fContext; }
robertphillips@google.com1f47f4f2012-08-16 14:49:16 +000073 GrTexture* getTexture() const { return fTexture; }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000074
75private:
76
77 // Key to indicate an atlas row without any meaningful data stored in it
78 const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
79
rmistry@google.comfbfcd562012-08-23 18:09:54 +000080 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +000081 * The state of a single row in our cache, next/prev pointers allow these to be chained
82 * together to represent LRU status
83 */
84 struct AtlasRow : public GrNoncopyable {
85 AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(NULL), fPrev(NULL) { }
86 // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
rmistry@google.comfbfcd562012-08-23 18:09:54 +000087 uint32_t fKey;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000088 // How many times this has been locked (0 == unlocked)
rmistry@google.comfbfcd562012-08-23 18:09:54 +000089 int32_t fLocks;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000090 // We maintain an LRU linked list between unlocked nodes with these pointers
91 AtlasRow* fNext;
92 AtlasRow* fPrev;
93 };
94
rmistry@google.comfbfcd562012-08-23 18:09:54 +000095 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +000096 * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
97 */
98 GrTextureStripAtlas(Desc desc);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000099
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000100 void lockTexture();
101 void unlockTexture();
102
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000103 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000104 * Initialize our LRU list (if one already exists, clear it and start anew)
105 */
106 void initLRU();
107
108 /**
109 * Grabs the least recently used free row out of the LRU list, returns NULL if no rows are free.
110 */
111 AtlasRow* getLRU();
112
113 void appendLRU(AtlasRow* row);
114 void removeFromLRU(AtlasRow* row);
115
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000116 /**
117 * Searches the key table for a key and returns the index if found; if not found, it returns
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000118 * the bitwise not of the index at which we could insert the key to maintain a sorted list.
119 **/
120 int searchByKey(uint32_t key);
121
122 /**
123 * Compare two atlas rows by key, so we can sort/search by key
124 */
125 static int compareKeys(const AtlasRow* lhs, const AtlasRow* rhs) {
126 return lhs->fKey - rhs->fKey;
127 }
128
129#ifdef SK_DEBUG
130 void validate();
131#endif
132
robertphillips@google.comcdb426d2012-09-24 19:33:59 +0000133 /**
134 * Clean up callback registered with GrContext. Allows this class to
135 * free up any allocated AtlasEntry and GrTextureStripAtlas objects
136 */
137 static void CleanUp(const GrContext* context, void* info);
138
139 // Hash table entry for atlases
140 class AtlasEntry;
141 typedef GrTBinHashKey<AtlasEntry, sizeof(GrTextureStripAtlas::Desc)> AtlasHashKey;
142 class AtlasEntry : public ::GrNoncopyable {
143 public:
144 AtlasEntry() : fAtlas(NULL) {}
145 ~AtlasEntry() { SkDELETE(fAtlas); }
146 int compare(const AtlasHashKey& key) const { return fKey.compare(key); }
147 AtlasHashKey fKey;
148 GrTextureStripAtlas* fAtlas;
149 };
150
151 static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* gAtlasCache;
152
153 static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* GetCache();
154
rileya@google.comf61c7462012-08-13 21:03:39 +0000155 // We increment gCacheCount for each atlas
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000156 static int32_t gCacheCount;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000157
rileya@google.comf61c7462012-08-13 21:03:39 +0000158 // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
159 // get a texture back from the texture cache, that it's the same one we last used.
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000160 const uint64_t fCacheID;
161
162 // Total locks on all rows (when this reaches zero, we can unlock our texture)
163 int32_t fLockedRows;
164
165 const Desc fDesc;
166 const uint16_t fNumRows;
robertphillips@google.com1f47f4f2012-08-16 14:49:16 +0000167 GrTexture* fTexture;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000168
169 // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000170 // order that they appear in our texture, this means we can subtract this pointer from a row
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000171 // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
172 AtlasRow* fRows;
173
174 // Head and tail for linked list of least-recently-used rows (front = least recently used).
175 // Note that when a texture is locked, it gets removed from this list until it is unlocked.
176 AtlasRow* fLRUFront;
177 AtlasRow* fLRUBack;
178
179 // A list of pointers to AtlasRows that currently contain cached images, sorted by key
180 SkTDArray<AtlasRow*> fKeyTable;
181};
182
183#endif
184