blob: 1a4c371b6996651d0b26dfce6f49d12afaec9a97 [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/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +000018 * Maintains a single large texture whose rows store many textures of a small fixed height,
rileya@google.com2e2aedc2012-08-13 20:28:48 +000019 * 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
rmistry@google.comfbfcd562012-08-23 18:09:54 +000052 /**
53 * 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 +000054 * texture coordinate in [0, 1] that we can use in a shader.
55 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000056 * If a regular texture access without using the atlas looks like:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000057 *
58 * texture2D(sampler, vec2(x, y))
59 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000060 * Then when using the atlas we'd replace it with:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000061 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000062 * texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
rileya@google.com2e2aedc2012-08-13 20:28:48 +000063 *
64 * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
rmistry@google.comfbfcd562012-08-23 18:09:54 +000065 * atlas and scaleFactor, returned by getVerticalScaleFactor(), is the y-scale of the row,
rileya@google.com2e2aedc2012-08-13 20:28:48 +000066 * 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; }
robertphillips@google.com1f47f4f2012-08-16 14:49:16 +000072 GrTexture* getTexture() const { return fTexture; }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000073
74private:
75
76 // Key to indicate an atlas row without any meaningful data stored in it
77 const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
78
rmistry@google.comfbfcd562012-08-23 18:09:54 +000079 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +000080 * 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"
rmistry@google.comfbfcd562012-08-23 18:09:54 +000086 uint32_t fKey;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000087 // How many times this has been locked (0 == unlocked)
rmistry@google.comfbfcd562012-08-23 18:09:54 +000088 int32_t fLocks;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000089 // We maintain an LRU linked list between unlocked nodes with these pointers
90 AtlasRow* fNext;
91 AtlasRow* fPrev;
92 };
93
rmistry@google.comfbfcd562012-08-23 18:09:54 +000094 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +000095 * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
96 */
97 GrTextureStripAtlas(Desc desc);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000098
rileya@google.com2e2aedc2012-08-13 20:28:48 +000099 void lockTexture();
100 void unlockTexture();
101
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000102 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000103 * 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
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000115 /**
116 * 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 +0000117 * 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
rileya@google.comf61c7462012-08-13 21:03:39 +0000132 // We increment gCacheCount for each atlas
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000133 static int32_t gCacheCount;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000134
rileya@google.comf61c7462012-08-13 21:03:39 +0000135 // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
136 // 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 +0000137 const uint64_t fCacheID;
138
139 // Total locks on all rows (when this reaches zero, we can unlock our texture)
140 int32_t fLockedRows;
141
142 const Desc fDesc;
143 const uint16_t fNumRows;
robertphillips@google.com1f47f4f2012-08-16 14:49:16 +0000144 GrTexture* fTexture;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000145
146 // 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 +0000147 // 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 +0000148 // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
149 AtlasRow* fRows;
150
151 // Head and tail for linked list of least-recently-used rows (front = least recently used).
152 // Note that when a texture is locked, it gets removed from this list until it is unlocked.
153 AtlasRow* fLRUFront;
154 AtlasRow* fLRUBack;
155
156 // A list of pointers to AtlasRows that currently contain cached images, sorted by key
157 SkTDArray<AtlasRow*> fKeyTable;
158};
159
160#endif
161