blob: b9834389003b309c8fa0597f2984c76ad935ec4c [file] [log] [blame]
joshualitt5bf99f12015-03-13 11:47:42 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrBatchAtlas.h"
9#include "GrBatchTarget.h"
10#include "GrGpu.h"
11#include "GrRectanizer.h"
12#include "GrTracing.h"
13
joshualitt5bf99f12015-03-13 11:47:42 -070014static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
15 loc->fX += offset.fX;
16 loc->fY += offset.fY;
17}
18
19static GrBatchAtlas::AtlasID create_id(int index, int generation) {
20 // Generation ID can roll over because we only check for equality
21 SkASSERT(index < (1 << 16));
22 return generation << 16 | index;
23}
24
25// The backing GrTexture for a GrBatchAtlas is broken into a spatial grid of GrBatchPlots.
26// The GrBatchPlots keep track of subimage placement via their GrRectanizer. In turn, a GrBatchPlot
27// manages the lifetime of its data using two tokens, a last ref toke and a last upload token.
28// Once a GrBatchPlot is "full" (i.e. there is no room for the new subimage according to the
29// GrRectanizer), it can no longer be used unless the last ref on the GrPlot has already been
30// flushed through to the gpu.
31
32class BatchPlot : public SkRefCnt {
33public:
34 typedef GrBatchAtlas::BatchToken BatchToken;
35 SK_DECLARE_INST_COUNT(BatchPlot);
36 SK_DECLARE_INTERNAL_LLIST_INTERFACE(BatchPlot);
37
38 // index() refers to the index of the plot in the owning GrAtlas's plot array. genID() is a
39 // monotonically incrementing number which is bumped every time the cpu backing store is
40 // wiped, or when the plot itself is evicted from the atlas(ie, there is continuity in genID()
41 // across atlas spills)
42 int index() const { return fIndex; }
43 int genID() const { return fGenID; }
44 GrBatchAtlas::AtlasID id() { return fID; }
45
46 GrTexture* texture() const { return fTexture; }
47
48 bool addSubImage(int width, int height, const void* image, SkIPoint16* loc, size_t rowBytes) {
49 if (!fRects->addRect(width, height, loc)) {
50 return false;
51 }
52
joshualitte062db92015-04-24 08:33:21 -070053 if (!fData) {
54 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
55 fHeight));
56 }
joshualitt5bf99f12015-03-13 11:47:42 -070057 const unsigned char* imagePtr = (const unsigned char*)image;
58 // point ourselves at the right starting spot
59 unsigned char* dataPtr = fData;
60 dataPtr += fBytesPerPixel * fWidth * loc->fY;
61 dataPtr += fBytesPerPixel * loc->fX;
62 // copy into the data buffer
63 for (int i = 0; i < height; ++i) {
64 memcpy(dataPtr, imagePtr, rowBytes);
65 dataPtr += fBytesPerPixel * fWidth;
66 imagePtr += rowBytes;
67 }
68
69 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
70 adjust_for_offset(loc, fOffset);
71 SkDEBUGCODE(fDirty = true;)
72
joshualitt5bf99f12015-03-13 11:47:42 -070073 return true;
74 }
75
76 // to manage the lifetime of a plot, we use two tokens. We use last upload token to know when
77 // we can 'piggy back' uploads, ie if the last upload hasn't been flushed to gpu, we don't need
78 // to issue a new upload even if we update the cpu backing store. We use lastref to determine
79 // when we can evict a plot from the cache, ie if the last ref has already flushed through
80 // the gpu then we can reuse the plot
81 BatchToken lastUploadToken() const { return fLastUpload; }
joshualittb4c507e2015-04-08 08:07:59 -070082 BatchToken lastUseToken() const { return fLastUse; }
83 void setLastUploadToken(BatchToken batchToken) {
84 SkASSERT(batchToken >= fLastUpload);
85 fLastUpload = batchToken;
86 }
87 void setLastUseToken(BatchToken batchToken) {
88 SkASSERT(batchToken >= fLastUse);
89 fLastUse = batchToken;
90 }
joshualitt5bf99f12015-03-13 11:47:42 -070091
92 void uploadToTexture(GrBatchTarget::TextureUploader uploader) {
93 // We should only be issuing uploads if we are in fact dirty
joshualitte062db92015-04-24 08:33:21 -070094 SkASSERT(fDirty && fData && fTexture);
joshualitt5bf99f12015-03-13 11:47:42 -070095 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrBatchPlot::uploadToTexture");
joshualitt5bf99f12015-03-13 11:47:42 -070096 size_t rowBytes = fBytesPerPixel * fRects->width();
97 const unsigned char* dataPtr = fData;
98 dataPtr += rowBytes * fDirtyRect.fTop;
99 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
100 uploader.writeTexturePixels(fTexture,
101 fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
102 fDirtyRect.width(), fDirtyRect.height(),
103 fTexture->config(), dataPtr, rowBytes);
104 fDirtyRect.setEmpty();
105 SkDEBUGCODE(fDirty = false;)
106 }
107
108 void resetRects() {
109 SkASSERT(fRects);
110 fRects->reset();
111 fGenID++;
112 fID = create_id(fIndex, fGenID);
113
114 // zero out the plot
joshualitte062db92015-04-24 08:33:21 -0700115 if (fData) {
116 sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
117 }
joshualitt5bf99f12015-03-13 11:47:42 -0700118
119 fDirtyRect.setEmpty();
120 SkDEBUGCODE(fDirty = false;)
121 }
122
123 int x() const { return fX; }
124 int y() const { return fY; }
125
126private:
127 BatchPlot()
128 : fLastUpload(0)
joshualittb4c507e2015-04-08 08:07:59 -0700129 , fLastUse(0)
joshualitt5bf99f12015-03-13 11:47:42 -0700130 , fIndex(-1)
131 , fGenID(-1)
132 , fID(0)
133 , fData(NULL)
134 , fWidth(0)
135 , fHeight(0)
136 , fX(0)
137 , fY(0)
138 , fTexture(NULL)
139 , fRects(NULL)
140 , fAtlas(NULL)
141 , fBytesPerPixel(1)
142 #ifdef SK_DEBUG
143 , fDirty(false)
144 #endif
145 {
146 fOffset.set(0, 0);
147 }
148
149 ~BatchPlot() {
joshualitt8e71d382015-04-24 18:47:11 -0700150 sk_free(fData);
joshualitt5bf99f12015-03-13 11:47:42 -0700151 fData = NULL;
152 delete fRects;
153 }
154
155 void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint32_t generation,
156 int offX, int offY, int width, int height, size_t bpp) {
157 fIndex = index;
158 fGenID = generation;
159 fID = create_id(index, generation);
160 fWidth = width;
161 fHeight = height;
162 fX = offX;
163 fY = offY;
164 fRects = GrRectanizer::Factory(width, height);
165 fAtlas = atlas;
166 fOffset.set(offX * width, offY * height);
167 fBytesPerPixel = bpp;
168 fData = NULL;
169 fDirtyRect.setEmpty();
170 SkDEBUGCODE(fDirty = false;)
171 fTexture = texture;
joshualitt5bf99f12015-03-13 11:47:42 -0700172 }
173
174 BatchToken fLastUpload;
joshualittb4c507e2015-04-08 08:07:59 -0700175 BatchToken fLastUse;
joshualitt5bf99f12015-03-13 11:47:42 -0700176
177 uint32_t fIndex;
178 uint32_t fGenID;
179 GrBatchAtlas::AtlasID fID;
180 unsigned char* fData;
181 int fWidth;
182 int fHeight;
183 int fX;
184 int fY;
185 GrTexture* fTexture;
186 GrRectanizer* fRects;
187 GrBatchAtlas* fAtlas;
188 SkIPoint16 fOffset; // the offset of the plot in the backing texture
189 size_t fBytesPerPixel;
190 SkIRect fDirtyRect;
191 SkDEBUGCODE(bool fDirty;)
192
193 friend class GrBatchAtlas;
194
195 typedef SkRefCnt INHERITED;
196};
197
198////////////////////////////////////////////////////////////////////////////////
199
200class GrPlotUploader : public GrBatchTarget::Uploader {
201public:
202 GrPlotUploader(BatchPlot* plot)
203 : INHERITED(plot->lastUploadToken())
204 , fPlot(SkRef(plot)) {
205 SkASSERT(plot);
206 }
207
mtklein36352bf2015-03-25 18:17:31 -0700208 void upload(GrBatchTarget::TextureUploader uploader) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700209 fPlot->uploadToTexture(uploader);
210 }
211
212private:
213 SkAutoTUnref<BatchPlot> fPlot;
214
215 typedef GrBatchTarget::Uploader INHERITED;
216};
217
218///////////////////////////////////////////////////////////////////////////////
219
220GrBatchAtlas::GrBatchAtlas(GrTexture* texture, int numPlotsX, int numPlotsY)
221 : fTexture(texture)
222 , fNumPlotsX(numPlotsX)
223 , fNumPlotsY(numPlotsY)
224 , fPlotWidth(texture->width() / numPlotsX)
joshualitt7c3a2f82015-03-31 13:32:05 -0700225 , fPlotHeight(texture->height() / numPlotsY)
226 , fAtlasGeneration(kInvalidAtlasGeneration + 1) {
joshualittb4c507e2015-04-08 08:07:59 -0700227 SkASSERT(fNumPlotsX * fNumPlotsY <= BulkUseTokenUpdater::kMaxPlots);
joshualitt5bf99f12015-03-13 11:47:42 -0700228 SkASSERT(fPlotWidth * fNumPlotsX == texture->width());
229 SkASSERT(fPlotHeight * fNumPlotsY == texture->height());
230
231 // We currently do not support compressed atlases...
232 SkASSERT(!GrPixelConfigIsCompressed(texture->desc().fConfig));
233
234 // set up allocated plots
235 fBPP = GrBytesPerPixel(texture->desc().fConfig);
236 fPlotArray = SkNEW_ARRAY(SkAutoTUnref<BatchPlot>, (fNumPlotsX * fNumPlotsY));
237
238 SkAutoTUnref<BatchPlot>* currPlot = fPlotArray;
239 for (int y = fNumPlotsY - 1, r = 0; y >= 0; --y, ++r) {
240 for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) {
241 int id = r * fNumPlotsX + c;
242 currPlot->reset(SkNEW(BatchPlot));
joshualitt7c3a2f82015-03-31 13:32:05 -0700243 (*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP);
joshualitt5bf99f12015-03-13 11:47:42 -0700244
245 // build LRU list
246 fPlotList.addToHead(currPlot->get());
247 ++currPlot;
248 }
249 }
250}
251
252GrBatchAtlas::~GrBatchAtlas() {
253 SkSafeUnref(fTexture);
254 SkDELETE_ARRAY(fPlotArray);
joshualitt5bf99f12015-03-13 11:47:42 -0700255}
256
257void GrBatchAtlas::processEviction(AtlasID id) {
258 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
259 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
260 }
261}
262
263void GrBatchAtlas::makeMRU(BatchPlot* plot) {
264 if (fPlotList.head() == plot) {
265 return;
266 }
267
268 fPlotList.remove(plot);
269 fPlotList.addToHead(plot);
270}
271
272inline void GrBatchAtlas::updatePlot(GrBatchTarget* batchTarget, AtlasID* id, BatchPlot* plot) {
273 this->makeMRU(plot);
274
275 // If our most recent upload has already occurred then we have to insert a new
276 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
277 // This new update will piggy back on that previously scheduled update.
278 if (batchTarget->isIssued(plot->lastUploadToken())) {
279 plot->setLastUploadToken(batchTarget->asapToken());
280 SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (plot)));
281 batchTarget->upload(uploader);
282 }
283 *id = plot->id();
284}
285
286bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
287 int width, int height, const void* image, SkIPoint16* loc) {
288 // We should already have a texture, TODO clean this up
289 SkASSERT(fTexture && width < fPlotWidth && height < fPlotHeight);
290
291 // now look through all allocated plots for one we can share, in Most Recently Refed order
292 GrBatchPlotList::Iter plotIter;
293 plotIter.init(fPlotList, GrBatchPlotList::Iter::kHead_IterStart);
294 BatchPlot* plot;
295 while ((plot = plotIter.get())) {
296 if (plot->addSubImage(width, height, image, loc, fBPP * width)) {
297 this->updatePlot(batchTarget, id, plot);
298 return true;
299 }
300 plotIter.next();
301 }
302
303 // If the above fails, then see if the least recently refed plot has already been flushed to the
304 // gpu
305 plotIter.init(fPlotList, GrBatchPlotList::Iter::kTail_IterStart);
306 plot = plotIter.get();
307 SkASSERT(plot);
joshualittb4c507e2015-04-08 08:07:59 -0700308 if (batchTarget->isIssued(plot->lastUseToken())) {
joshualitt5bf99f12015-03-13 11:47:42 -0700309 this->processEviction(plot->id());
310 plot->resetRects();
311 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc, fBPP * width);
312 SkASSERT(verify);
313 this->updatePlot(batchTarget, id, plot);
joshualitt7c3a2f82015-03-31 13:32:05 -0700314 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700315 return true;
316 }
317
318 // The least recently refed plot hasn't been flushed to the gpu yet, however, if we have flushed
319 // it to the batch target than we can reuse it. Our last ref token is guaranteed to be less
320 // than or equal to the current token. If its 'less than' the current token, than we can spin
321 // off the plot(ie let the batch target manage it) and create a new plot in its place in our
322 // array. If it is equal to the currentToken, then the caller has to flush draws to the batch
323 // target so we can spin off the plot
joshualittb4c507e2015-04-08 08:07:59 -0700324 if (plot->lastUseToken() == batchTarget->currentToken()) {
joshualitt5bf99f12015-03-13 11:47:42 -0700325 return false;
326 }
327
328 // We take an extra ref here so our plot isn't deleted when we reset its index in the array.
329 plot->ref();
330 int index = plot->index();
331 int x = plot->x();
332 int y = plot->y();
333 int generation = plot->genID();
334
335 this->processEviction(plot->id());
336 fPlotList.remove(plot);
337 SkAutoTUnref<BatchPlot>& newPlot = fPlotArray[plot->index()];
338 newPlot.reset(SkNEW(BatchPlot));
339 newPlot->init(this, fTexture, index, ++generation, x, y, fPlotWidth, fPlotHeight, fBPP);
340
341 fPlotList.addToHead(newPlot.get());
342 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc, fBPP * width);
343 SkASSERT(verify);
344 newPlot->setLastUploadToken(batchTarget->currentToken());
345 SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (newPlot)));
346 batchTarget->upload(uploader);
347 *id = newPlot->id();
348 plot->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -0700349 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700350 return true;
351}
352
353bool GrBatchAtlas::hasID(AtlasID id) {
joshualittb4c507e2015-04-08 08:07:59 -0700354 int index = GetIndexFromID(id);
joshualitt5bf99f12015-03-13 11:47:42 -0700355 SkASSERT(index < fNumPlotsX * fNumPlotsY);
joshualittb4c507e2015-04-08 08:07:59 -0700356 return fPlotArray[index]->genID() == GetGenerationFromID(id);
joshualitt5bf99f12015-03-13 11:47:42 -0700357}
358
joshualittb4c507e2015-04-08 08:07:59 -0700359void GrBatchAtlas::setLastUseToken(AtlasID id, BatchToken batchToken) {
joshualitt5bf99f12015-03-13 11:47:42 -0700360 SkASSERT(this->hasID(id));
joshualittb4c507e2015-04-08 08:07:59 -0700361 int index = GetIndexFromID(id);
362 SkASSERT(index < fNumPlotsX * fNumPlotsY);
joshualitt5bf99f12015-03-13 11:47:42 -0700363 this->makeMRU(fPlotArray[index]);
joshualittb4c507e2015-04-08 08:07:59 -0700364 fPlotArray[index]->setLastUseToken(batchToken);
365}
366
367void GrBatchAtlas::setLastUseTokenBulk(const BulkUseTokenUpdater& updater, BatchToken batchToken) {
joshualitt4314e082015-04-23 08:03:35 -0700368 int count = updater.fPlotsToUpdate.count();
369 for (int i = 0; i < count; i++) {
joshualittb4c507e2015-04-08 08:07:59 -0700370 BatchPlot* plot = fPlotArray[updater.fPlotsToUpdate[i]];
371 this->makeMRU(plot);
372 plot->setLastUseToken(batchToken);
373 }
joshualitt5bf99f12015-03-13 11:47:42 -0700374}