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 #include "GrTracing.h"
14
15 ///////////////////////////////////////////////////////////////////////////////
16
17 // for testing
18 #define FONT_CACHE_STATS 0
19 #if FONT_CACHE_STATS
20 static int g_UploadCount = 0;
21 #endif
22
GrPlot()23 GrPlot::GrPlot()
24 : fDrawToken(NULL, 0)
25 , fID(-1)
26 , fTexture(NULL)
27 , fRects(NULL)
28 , fAtlas(NULL)
29 , fBytesPerPixel(1)
30 , fDirty(false)
31 , fBatchUploads(false)
32 {
33 fOffset.set(0, 0);
34 }
35
~GrPlot()36 GrPlot::~GrPlot() {
37 SkDELETE_ARRAY(fPlotData);
38 fPlotData = NULL;
39 delete fRects;
40 }
41
init(GrAtlas * atlas,int id,int offX,int offY,int width,int height,size_t bpp,bool batchUploads)42 void GrPlot::init(GrAtlas* atlas, int id, int offX, int offY, int width, int height, size_t bpp,
43 bool batchUploads) {
44 fID = id;
45 fRects = GrRectanizer::Factory(width, height);
46 fAtlas = atlas;
47 fOffset.set(offX * width, offY * height);
48 fBytesPerPixel = bpp;
49 fPlotData = NULL;
50 fDirtyRect.setEmpty();
51 fDirty = false;
52 fBatchUploads = batchUploads;
53 }
54
adjust_for_offset(SkIPoint16 * loc,const SkIPoint16 & offset)55 static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
56 loc->fX += offset.fX;
57 loc->fY += offset.fY;
58 }
59
addSubImage(int width,int height,const void * image,SkIPoint16 * loc)60 bool GrPlot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
61 float percentFull = fRects->percentFull();
62 if (!fRects->addRect(width, height, loc)) {
63 return false;
64 }
65
66 // if batching uploads, create backing memory on first use
67 // once the plot is nearly full we will revert to uploading each subimage individually
68 int plotWidth = fRects->width();
69 int plotHeight = fRects->height();
70 if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) {
71 fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight);
72 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight);
73 }
74
75 // if we have backing memory, copy to the memory and set for future upload
76 if (fPlotData) {
77 const unsigned char* imagePtr = (const unsigned char*) image;
78 // point ourselves at the right starting spot
79 unsigned char* dataPtr = fPlotData;
80 dataPtr += fBytesPerPixel*plotWidth*loc->fY;
81 dataPtr += fBytesPerPixel*loc->fX;
82 // copy into the data buffer
83 for (int i = 0; i < height; ++i) {
84 memcpy(dataPtr, imagePtr, fBytesPerPixel*width);
85 dataPtr += fBytesPerPixel*plotWidth;
86 imagePtr += fBytesPerPixel*width;
87 }
88
89 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
90 adjust_for_offset(loc, fOffset);
91 fDirty = true;
92 // otherwise, just upload the image directly
93 } else if (image) {
94 adjust_for_offset(loc, fOffset);
95 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
96 fTexture->writePixels(loc->fX, loc->fY, width, height, fTexture->config(), image, 0,
97 GrContext::kDontFlush_PixelOpsFlag);
98 } else {
99 adjust_for_offset(loc, fOffset);
100 }
101
102 #if FONT_CACHE_STATS
103 ++g_UploadCount;
104 #endif
105
106 return true;
107 }
108
uploadToTexture()109 void GrPlot::uploadToTexture() {
110 static const float kNearlyFullTolerance = 0.85f;
111
112 // should only do this if batching is enabled
113 SkASSERT(fBatchUploads);
114
115 if (fDirty) {
116 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
117 SkASSERT(fTexture);
118 // We pass the flag that does not force a flush. We assume our caller is
119 // smart and hasn't referenced the part of the texture we're about to update
120 // since the last flush.
121 size_t rowBytes = fBytesPerPixel*fRects->width();
122 const unsigned char* dataPtr = fPlotData;
123 dataPtr += rowBytes*fDirtyRect.fTop;
124 dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
125 fTexture->writePixels(fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
126 fDirtyRect.width(), fDirtyRect.height(), fTexture->config(), dataPtr,
127 rowBytes, GrContext::kDontFlush_PixelOpsFlag);
128 fDirtyRect.setEmpty();
129 fDirty = false;
130 // If the Plot is nearly full, anything else we add will probably be small and one
131 // at a time, so free up the memory and after this upload any new images directly.
132 if (fRects->percentFull() > kNearlyFullTolerance) {
133 SkDELETE_ARRAY(fPlotData);
134 fPlotData = NULL;
135 }
136 }
137 }
138
resetRects()139 void GrPlot::resetRects() {
140 SkASSERT(fRects);
141 fRects->reset();
142 }
143
144 ///////////////////////////////////////////////////////////////////////////////
145
GrAtlas(GrGpu * gpu,GrPixelConfig config,GrSurfaceFlags flags,const SkISize & backingTextureSize,int numPlotsX,int numPlotsY,bool batchUploads)146 GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrSurfaceFlags flags,
147 const SkISize& backingTextureSize,
148 int numPlotsX, int numPlotsY, bool batchUploads) {
149 fGpu = SkRef(gpu);
150 fPixelConfig = config;
151 fFlags = flags;
152 fBackingTextureSize = backingTextureSize;
153 fNumPlotsX = numPlotsX;
154 fNumPlotsY = numPlotsY;
155 fBatchUploads = batchUploads;
156 fTexture = NULL;
157
158 int textureWidth = fBackingTextureSize.width();
159 int textureHeight = fBackingTextureSize.height();
160
161 int plotWidth = textureWidth / fNumPlotsX;
162 int plotHeight = textureHeight / fNumPlotsY;
163
164 SkASSERT(plotWidth * fNumPlotsX == textureWidth);
165 SkASSERT(plotHeight * fNumPlotsY == textureHeight);
166
167 // We currently do not support compressed atlases...
168 SkASSERT(!GrPixelConfigIsCompressed(config));
169
170 // set up allocated plots
171 size_t bpp = GrBytesPerPixel(fPixelConfig);
172 fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
173
174 GrPlot* currPlot = fPlotArray;
175 for (int y = numPlotsY-1; y >= 0; --y) {
176 for (int x = numPlotsX-1; x >= 0; --x) {
177 currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp, batchUploads);
178
179 // build LRU list
180 fPlotList.addToHead(currPlot);
181 ++currPlot;
182 }
183 }
184 }
185
~GrAtlas()186 GrAtlas::~GrAtlas() {
187 SkSafeUnref(fTexture);
188 SkDELETE_ARRAY(fPlotArray);
189
190 fGpu->unref();
191 #if FONT_CACHE_STATS
192 SkDebugf("Num uploads: %d\n", g_UploadCount);
193 #endif
194 }
195
makeMRU(GrPlot * plot)196 void GrAtlas::makeMRU(GrPlot* plot) {
197 if (fPlotList.head() == plot) {
198 return;
199 }
200
201 fPlotList.remove(plot);
202 fPlotList.addToHead(plot);
203 };
204
addToAtlas(ClientPlotUsage * usage,int width,int height,const void * image,SkIPoint16 * loc)205 GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage,
206 int width, int height, const void* image,
207 SkIPoint16* loc) {
208 // iterate through entire plot list for this atlas, see if we can find a hole
209 // last one was most recently added and probably most empty
210 for (int i = usage->fPlots.count()-1; i >= 0; --i) {
211 GrPlot* plot = usage->fPlots[i];
212 // client may have plots from more than one atlas, must check for ours before adding
213 if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)) {
214 this->makeMRU(plot);
215 return plot;
216 }
217 }
218
219 // before we get a new plot, make sure we have a backing texture
220 if (NULL == fTexture) {
221 // TODO: Update this to use the cache rather than directly creating a texture.
222 GrSurfaceDesc desc;
223 desc.fFlags = fFlags;
224 desc.fWidth = fBackingTextureSize.width();
225 desc.fHeight = fBackingTextureSize.height();
226 desc.fConfig = fPixelConfig;
227
228 fTexture = fGpu->createTexture(desc, true, NULL, 0);
229 if (NULL == fTexture) {
230 return NULL;
231 }
232 }
233
234 // now look through all allocated plots for one we can share, in MRU order
235 GrPlotList::Iter plotIter;
236 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
237 GrPlot* plot;
238 while ((plot = plotIter.get())) {
239 // make sure texture is set for quick lookup
240 plot->fTexture = fTexture;
241 if (plot->addSubImage(width, height, image, loc)) {
242 this->makeMRU(plot);
243 // new plot for atlas, put at end of array
244 SkASSERT(!usage->fPlots.contains(plot));
245 *(usage->fPlots.append()) = plot;
246 return plot;
247 }
248 plotIter.next();
249 }
250
251 // If the above fails, then the current plot list has no room
252 return NULL;
253 }
254
RemovePlot(ClientPlotUsage * usage,const GrPlot * plot)255 void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) {
256 int index = usage->fPlots.find(const_cast<GrPlot*>(plot));
257 if (index >= 0) {
258 usage->fPlots.remove(index);
259 }
260 }
261
262 // get a plot that's not being used by the current draw
getUnusedPlot()263 GrPlot* GrAtlas::getUnusedPlot() {
264 GrPlotList::Iter plotIter;
265 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
266 GrPlot* plot;
267 while ((plot = plotIter.get())) {
268 if (plot->drawToken().isIssued()) {
269 return plot;
270 }
271 plotIter.prev();
272 }
273
274 return NULL;
275 }
276
uploadPlotsToTexture()277 void GrAtlas::uploadPlotsToTexture() {
278 if (fBatchUploads) {
279 GrPlotList::Iter plotIter;
280 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
281 GrPlot* plot;
282 while ((plot = plotIter.get())) {
283 plot->uploadToTexture();
284 plotIter.next();
285 }
286 }
287 }
288