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 #define BORDER 1
39
40 #ifdef SK_DEBUG
41 static int gCounter;
42 #endif
43
44 // for testing
45 #define FONT_CACHE_STATS 0
46 #if FONT_CACHE_STATS
47 static int g_UploadCount = 0;
48 #endif
49
GrPlot()50 GrPlot::GrPlot() : fDrawToken(NULL, 0)
51 , fNext(NULL)
52 , fTexture(NULL)
53 , fAtlasMgr(NULL)
54 , fBytesPerPixel(1)
55 {
56 fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER,
57 GR_ATLAS_HEIGHT - BORDER);
58 fOffset.set(0, 0);
59 }
60
~GrPlot()61 GrPlot::~GrPlot() {
62 delete fRects;
63 }
64
adjust_for_offset(GrIPoint16 * loc,const GrIPoint16 & offset)65 static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) {
66 loc->fX += offset.fX * GR_ATLAS_WIDTH;
67 loc->fY += offset.fY * GR_ATLAS_HEIGHT;
68 }
69
zero_fill(uint8_t * ptr,size_t count)70 static inline uint8_t* zero_fill(uint8_t* ptr, size_t count) {
71 sk_bzero(ptr, count);
72 return ptr + count;
73 }
74
addSubImage(int width,int height,const void * image,GrIPoint16 * loc)75 bool GrPlot::addSubImage(int width, int height, const void* image,
76 GrIPoint16* loc) {
77 if (!fRects->addRect(width + BORDER, height + BORDER, loc)) {
78 return false;
79 }
80
81 SkAutoSMalloc<1024> storage;
82 int dstW = width + 2*BORDER;
83 int dstH = height + 2*BORDER;
84 if (BORDER) {
85 const size_t dstRB = dstW * fBytesPerPixel;
86 uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB);
87 sk_bzero(dst, dstRB); // zero top row
88 dst += dstRB;
89 for (int y = 0; y < height; y++) {
90 dst = zero_fill(dst, fBytesPerPixel); // zero left edge
91 memcpy(dst, image, width * fBytesPerPixel);
92 dst += width * fBytesPerPixel;
93 dst = zero_fill(dst, fBytesPerPixel); // zero right edge
94 image = (const void*)((const char*)image + width * fBytesPerPixel);
95 }
96 sk_bzero(dst, dstRB); // zero bottom row
97 image = storage.get();
98 }
99 adjust_for_offset(loc, fOffset);
100 GrContext* context = fTexture->getContext();
101 // We pass the flag that does not force a flush. We assume our caller is
102 // smart and hasn't referenced the part of the texture we're about to update
103 // since the last flush.
104 context->writeTexturePixels(fTexture,
105 loc->fX, loc->fY, dstW, dstH,
106 fTexture->config(), image, 0,
107 GrContext::kDontFlush_PixelOpsFlag);
108
109 // now tell the caller to skip the top/left BORDER
110 loc->fX += BORDER;
111 loc->fY += BORDER;
112
113 #if FONT_CACHE_STATS
114 ++g_UploadCount;
115 #endif
116
117 return true;
118 }
119
120 ///////////////////////////////////////////////////////////////////////////////
121
GrAtlasMgr(GrGpu * gpu,GrPixelConfig config)122 GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) {
123 fGpu = gpu;
124 fPixelConfig = config;
125 gpu->ref();
126 fTexture = NULL;
127
128 // set up allocated plots
129 size_t bpp = GrBytesPerPixel(fPixelConfig);
130 fPlots = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT));
131 fFreePlots = NULL;
132 GrPlot* currPlot = fPlots;
133 for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) {
134 for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) {
135 currPlot->fAtlasMgr = this;
136 currPlot->fOffset.set(x, y);
137 currPlot->fBytesPerPixel = bpp;
138
139 // add to free list
140 currPlot->fNext = fFreePlots;
141 fFreePlots = currPlot;
142
143 ++currPlot;
144 }
145 }
146 }
147
~GrAtlasMgr()148 GrAtlasMgr::~GrAtlasMgr() {
149 SkSafeUnref(fTexture);
150 SkDELETE_ARRAY(fPlots);
151
152 fGpu->unref();
153 #if FONT_CACHE_STATS
154 GrPrintf("Num uploads: %d\n", g_UploadCount);
155 #endif
156 }
157
addToAtlas(GrAtlas * atlas,int width,int height,const void * image,GrIPoint16 * loc)158 GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas,
159 int width, int height, const void* image,
160 GrIPoint16* loc) {
161 // iterate through entire plot list, see if we can find a hole
162 GrPlot* plotIter = atlas->fPlots;
163 while (plotIter) {
164 if (plotIter->addSubImage(width, height, image, loc)) {
165 return plotIter;
166 }
167 plotIter = plotIter->fNext;
168 }
169
170 // If the above fails, then either we have no starting plot, or the current
171 // plot list is full. Either way we need to allocate a new plot
172 GrPlot* newPlot = this->allocPlot();
173 if (NULL == newPlot) {
174 return NULL;
175 }
176
177 if (NULL == fTexture) {
178 // TODO: Update this to use the cache rather than directly creating a texture.
179 GrTextureDesc desc;
180 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
181 desc.fWidth = GR_ATLAS_TEXTURE_WIDTH;
182 desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT;
183 desc.fConfig = fPixelConfig;
184
185 fTexture = fGpu->createTexture(desc, NULL, 0);
186 if (NULL == fTexture) {
187 return NULL;
188 }
189 }
190 // be sure to set texture for fast lookup
191 newPlot->fTexture = fTexture;
192
193 if (!newPlot->addSubImage(width, height, image, loc)) {
194 this->freePlot(newPlot);
195 return NULL;
196 }
197
198 // new plot, put at head
199 newPlot->fNext = atlas->fPlots;
200 atlas->fPlots = newPlot;
201
202 return newPlot;
203 }
204
removeUnusedPlots(GrAtlas * atlas)205 bool GrAtlasMgr::removeUnusedPlots(GrAtlas* atlas) {
206
207 // GrPlot** is used so that the head element can be easily
208 // modified when the first element is deleted
209 GrPlot** plotRef = &atlas->fPlots;
210 GrPlot* plot = atlas->fPlots;
211 bool removed = false;
212 while (NULL != plot) {
213 if (plot->drawToken().isIssued()) {
214 *plotRef = plot->fNext;
215 this->freePlot(plot);
216 plot = *plotRef;
217 removed = true;
218 } else {
219 plotRef = &plot->fNext;
220 plot = plot->fNext;
221 }
222 }
223
224 return removed;
225 }
226
deletePlotList(GrPlot * plot)227 void GrAtlasMgr::deletePlotList(GrPlot* plot) {
228 while (NULL != plot) {
229 GrPlot* next = plot->fNext;
230 this->freePlot(plot);
231 plot = next;
232 }
233 }
234
allocPlot()235 GrPlot* GrAtlasMgr::allocPlot() {
236 if (NULL == fFreePlots) {
237 return NULL;
238 } else {
239 GrPlot* alloc = fFreePlots;
240 fFreePlots = alloc->fNext;
241 #ifdef SK_DEBUG
242 // GrPrintf(" GrPlot %p [%d %d] %d\n", this, alloc->fOffset.fX, alloc->fOffset.fY, gCounter);
243 gCounter += 1;
244 #endif
245 return alloc;
246 }
247
248 }
249
freePlot(GrPlot * plot)250 void GrAtlasMgr::freePlot(GrPlot* plot) {
251 SkASSERT(this == plot->fAtlasMgr);
252
253 plot->fRects->reset();
254 plot->fNext = fFreePlots;
255 fFreePlots = plot;
256
257 #ifdef SK_DEBUG
258 --gCounter;
259 // GrPrintf("~GrPlot %p [%d %d] %d\n", this, plot->fOffset.fX, plot->fOffset.fY, gCounter);
260 #endif
261 }
262