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