|
1 |
|
2 /* |
|
3 * Copyright 2010 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 #include "GrAtlas.h" |
|
10 #include "GrContext.h" |
|
11 #include "GrGpu.h" |
|
12 #include "GrRectanizer.h" |
|
13 |
|
14 #if 0 |
|
15 #define GR_PLOT_WIDTH 8 |
|
16 #define GR_PLOT_HEIGHT 4 |
|
17 #define GR_ATLAS_WIDTH 256 |
|
18 #define GR_ATLAS_HEIGHT 256 |
|
19 |
|
20 #define GR_ATLAS_TEXTURE_WIDTH (GR_PLOT_WIDTH * GR_ATLAS_WIDTH) |
|
21 #define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT) |
|
22 |
|
23 #else |
|
24 |
|
25 #define GR_ATLAS_TEXTURE_WIDTH 1024 |
|
26 #define GR_ATLAS_TEXTURE_HEIGHT 2048 |
|
27 |
|
28 #define GR_ATLAS_WIDTH 256 |
|
29 #define GR_ATLAS_HEIGHT 256 |
|
30 |
|
31 #define GR_PLOT_WIDTH (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH) |
|
32 #define GR_PLOT_HEIGHT (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT) |
|
33 |
|
34 #endif |
|
35 |
|
36 /////////////////////////////////////////////////////////////////////////////// |
|
37 |
|
38 // for testing |
|
39 #define FONT_CACHE_STATS 0 |
|
40 #if FONT_CACHE_STATS |
|
41 static int g_UploadCount = 0; |
|
42 #endif |
|
43 |
|
44 GrPlot::GrPlot() : fDrawToken(NULL, 0) |
|
45 , fTexture(NULL) |
|
46 , fAtlasMgr(NULL) |
|
47 , fBytesPerPixel(1) |
|
48 { |
|
49 fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH, |
|
50 GR_ATLAS_HEIGHT); |
|
51 fOffset.set(0, 0); |
|
52 } |
|
53 |
|
54 GrPlot::~GrPlot() { |
|
55 delete fRects; |
|
56 } |
|
57 |
|
58 static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) { |
|
59 loc->fX += offset.fX * GR_ATLAS_WIDTH; |
|
60 loc->fY += offset.fY * GR_ATLAS_HEIGHT; |
|
61 } |
|
62 |
|
63 bool GrPlot::addSubImage(int width, int height, const void* image, |
|
64 GrIPoint16* loc) { |
|
65 if (!fRects->addRect(width, height, loc)) { |
|
66 return false; |
|
67 } |
|
68 |
|
69 SkAutoSMalloc<1024> storage; |
|
70 adjust_for_offset(loc, fOffset); |
|
71 GrContext* context = fTexture->getContext(); |
|
72 // We pass the flag that does not force a flush. We assume our caller is |
|
73 // smart and hasn't referenced the part of the texture we're about to update |
|
74 // since the last flush. |
|
75 context->writeTexturePixels(fTexture, |
|
76 loc->fX, loc->fY, width, height, |
|
77 fTexture->config(), image, 0, |
|
78 GrContext::kDontFlush_PixelOpsFlag); |
|
79 |
|
80 #if FONT_CACHE_STATS |
|
81 ++g_UploadCount; |
|
82 #endif |
|
83 |
|
84 return true; |
|
85 } |
|
86 |
|
87 void GrPlot::resetRects() { |
|
88 SkASSERT(NULL != fRects); |
|
89 fRects->reset(); |
|
90 } |
|
91 |
|
92 /////////////////////////////////////////////////////////////////////////////// |
|
93 |
|
94 GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { |
|
95 fGpu = gpu; |
|
96 fPixelConfig = config; |
|
97 gpu->ref(); |
|
98 fTexture = NULL; |
|
99 |
|
100 // set up allocated plots |
|
101 size_t bpp = GrBytesPerPixel(fPixelConfig); |
|
102 fPlotArray = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT)); |
|
103 |
|
104 GrPlot* currPlot = fPlotArray; |
|
105 for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) { |
|
106 for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) { |
|
107 currPlot->fAtlasMgr = this; |
|
108 currPlot->fOffset.set(x, y); |
|
109 currPlot->fBytesPerPixel = bpp; |
|
110 |
|
111 // build LRU list |
|
112 fPlotList.addToHead(currPlot); |
|
113 ++currPlot; |
|
114 } |
|
115 } |
|
116 } |
|
117 |
|
118 GrAtlasMgr::~GrAtlasMgr() { |
|
119 SkSafeUnref(fTexture); |
|
120 SkDELETE_ARRAY(fPlotArray); |
|
121 |
|
122 fGpu->unref(); |
|
123 #if FONT_CACHE_STATS |
|
124 GrPrintf("Num uploads: %d\n", g_UploadCount); |
|
125 #endif |
|
126 } |
|
127 |
|
128 void GrAtlasMgr::moveToHead(GrPlot* plot) { |
|
129 if (fPlotList.head() == plot) { |
|
130 return; |
|
131 } |
|
132 |
|
133 fPlotList.remove(plot); |
|
134 fPlotList.addToHead(plot); |
|
135 }; |
|
136 |
|
137 GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas, |
|
138 int width, int height, const void* image, |
|
139 GrIPoint16* loc) { |
|
140 // iterate through entire plot list for this atlas, see if we can find a hole |
|
141 // last one was most recently added and probably most empty |
|
142 for (int i = atlas->fPlots.count()-1; i >= 0; --i) { |
|
143 GrPlot* plot = atlas->fPlots[i]; |
|
144 if (plot->addSubImage(width, height, image, loc)) { |
|
145 this->moveToHead(plot); |
|
146 return plot; |
|
147 } |
|
148 } |
|
149 |
|
150 // before we get a new plot, make sure we have a backing texture |
|
151 if (NULL == fTexture) { |
|
152 // TODO: Update this to use the cache rather than directly creating a texture. |
|
153 GrTextureDesc desc; |
|
154 desc.fFlags = kDynamicUpdate_GrTextureFlagBit; |
|
155 desc.fWidth = GR_ATLAS_TEXTURE_WIDTH; |
|
156 desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT; |
|
157 desc.fConfig = fPixelConfig; |
|
158 |
|
159 fTexture = fGpu->createTexture(desc, NULL, 0); |
|
160 if (NULL == fTexture) { |
|
161 return NULL; |
|
162 } |
|
163 } |
|
164 |
|
165 // now look through all allocated plots for one we can share, in MRU order |
|
166 GrPlotList::Iter plotIter; |
|
167 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); |
|
168 GrPlot* plot; |
|
169 while (NULL != (plot = plotIter.get())) { |
|
170 // make sure texture is set for quick lookup |
|
171 plot->fTexture = fTexture; |
|
172 if (plot->addSubImage(width, height, image, loc)) { |
|
173 this->moveToHead(plot); |
|
174 // new plot for atlas, put at end of array |
|
175 *(atlas->fPlots.append()) = plot; |
|
176 return plot; |
|
177 } |
|
178 plotIter.next(); |
|
179 } |
|
180 |
|
181 // If the above fails, then the current plot list has no room |
|
182 return NULL; |
|
183 } |
|
184 |
|
185 bool GrAtlasMgr::removePlot(GrAtlas* atlas, const GrPlot* plot) { |
|
186 // iterate through plot list for this atlas |
|
187 int count = atlas->fPlots.count(); |
|
188 for (int i = 0; i < count; ++i) { |
|
189 if (plot == atlas->fPlots[i]) { |
|
190 atlas->fPlots.remove(i); |
|
191 return true; |
|
192 } |
|
193 } |
|
194 |
|
195 return false; |
|
196 } |
|
197 |
|
198 // get a plot that's not being used by the current draw |
|
199 GrPlot* GrAtlasMgr::getUnusedPlot() { |
|
200 GrPlotList::Iter plotIter; |
|
201 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart); |
|
202 GrPlot* plot; |
|
203 while (NULL != (plot = plotIter.get())) { |
|
204 if (plot->drawToken().isIssued()) { |
|
205 return plot; |
|
206 } |
|
207 plotIter.prev(); |
|
208 } |
|
209 |
|
210 return NULL; |
|
211 } |
|
212 |
|
213 SkISize GrAtlas::getSize() const { |
|
214 return SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, GR_ATLAS_TEXTURE_HEIGHT); |
|
215 } |