blob: 3ce157c710a7bb869585fadb8adfa07d0b4d384b [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"
bsalomon72e3ae42015-04-28 08:08:46 -070013#include "GrVertexBuffer.h"
joshualitt5bf99f12015-03-13 11:47:42 -070014
joshualitt5bf99f12015-03-13 11:47:42 -070015static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
16 loc->fX += offset.fX;
17 loc->fY += offset.fY;
18}
19
joshualitt8db6fdc2015-07-31 08:25:07 -070020static GrBatchAtlas::AtlasID create_id(uint32_t index, uint64_t generation) {
joshualitt5bf99f12015-03-13 11:47:42 -070021 SkASSERT(index < (1 << 16));
joshualitt8db6fdc2015-07-31 08:25:07 -070022 SkASSERT(generation < ((uint64_t)1 << 48));
joshualitt5bf99f12015-03-13 11:47:42 -070023 return generation << 16 | index;
24}
25
26// The backing GrTexture for a GrBatchAtlas is broken into a spatial grid of GrBatchPlots.
27// The GrBatchPlots keep track of subimage placement via their GrRectanizer. In turn, a GrBatchPlot
28// manages the lifetime of its data using two tokens, a last ref toke and a last upload token.
29// Once a GrBatchPlot is "full" (i.e. there is no room for the new subimage according to the
30// GrRectanizer), it can no longer be used unless the last ref on the GrPlot has already been
31// flushed through to the gpu.
32
33class BatchPlot : public SkRefCnt {
34public:
35 typedef GrBatchAtlas::BatchToken BatchToken;
mtklein2766c002015-06-26 11:45:03 -070036
joshualitt5bf99f12015-03-13 11:47:42 -070037 SK_DECLARE_INTERNAL_LLIST_INTERFACE(BatchPlot);
38
39 // index() refers to the index of the plot in the owning GrAtlas's plot array. genID() is a
40 // monotonically incrementing number which is bumped every time the cpu backing store is
41 // wiped, or when the plot itself is evicted from the atlas(ie, there is continuity in genID()
42 // across atlas spills)
joshualitt8db6fdc2015-07-31 08:25:07 -070043 uint32_t index() const { return fIndex; }
44 uint64_t genID() const { return fGenID; }
joshualitt65e96b42015-07-31 11:45:22 -070045 GrBatchAtlas::AtlasID id() {
46 SkASSERT(GrBatchAtlas::kInvalidAtlasID != fID);
47 return fID;
48 }
joshualitt5bf99f12015-03-13 11:47:42 -070049
50 GrTexture* texture() const { return fTexture; }
51
52 bool addSubImage(int width, int height, const void* image, SkIPoint16* loc, size_t rowBytes) {
53 if (!fRects->addRect(width, height, loc)) {
54 return false;
55 }
56
joshualitte062db92015-04-24 08:33:21 -070057 if (!fData) {
58 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
59 fHeight));
60 }
joshualitt5bf99f12015-03-13 11:47:42 -070061 const unsigned char* imagePtr = (const unsigned char*)image;
62 // point ourselves at the right starting spot
63 unsigned char* dataPtr = fData;
64 dataPtr += fBytesPerPixel * fWidth * loc->fY;
65 dataPtr += fBytesPerPixel * loc->fX;
66 // copy into the data buffer
67 for (int i = 0; i < height; ++i) {
68 memcpy(dataPtr, imagePtr, rowBytes);
69 dataPtr += fBytesPerPixel * fWidth;
70 imagePtr += rowBytes;
71 }
72
73 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
74 adjust_for_offset(loc, fOffset);
75 SkDEBUGCODE(fDirty = true;)
76
joshualitt5bf99f12015-03-13 11:47:42 -070077 return true;
78 }
79
80 // to manage the lifetime of a plot, we use two tokens. We use last upload token to know when
81 // we can 'piggy back' uploads, ie if the last upload hasn't been flushed to gpu, we don't need
82 // to issue a new upload even if we update the cpu backing store. We use lastref to determine
83 // when we can evict a plot from the cache, ie if the last ref has already flushed through
84 // the gpu then we can reuse the plot
85 BatchToken lastUploadToken() const { return fLastUpload; }
joshualittb4c507e2015-04-08 08:07:59 -070086 BatchToken lastUseToken() const { return fLastUse; }
87 void setLastUploadToken(BatchToken batchToken) {
88 SkASSERT(batchToken >= fLastUpload);
89 fLastUpload = batchToken;
90 }
91 void setLastUseToken(BatchToken batchToken) {
92 SkASSERT(batchToken >= fLastUse);
93 fLastUse = batchToken;
94 }
joshualitt5bf99f12015-03-13 11:47:42 -070095
96 void uploadToTexture(GrBatchTarget::TextureUploader uploader) {
97 // We should only be issuing uploads if we are in fact dirty
joshualitte062db92015-04-24 08:33:21 -070098 SkASSERT(fDirty && fData && fTexture);
joshualitt5bf99f12015-03-13 11:47:42 -070099 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrBatchPlot::uploadToTexture");
joshualitt5bf99f12015-03-13 11:47:42 -0700100 size_t rowBytes = fBytesPerPixel * fRects->width();
101 const unsigned char* dataPtr = fData;
102 dataPtr += rowBytes * fDirtyRect.fTop;
103 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
104 uploader.writeTexturePixels(fTexture,
105 fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
106 fDirtyRect.width(), fDirtyRect.height(),
107 fTexture->config(), dataPtr, rowBytes);
108 fDirtyRect.setEmpty();
109 SkDEBUGCODE(fDirty = false;)
110 }
111
112 void resetRects() {
113 SkASSERT(fRects);
114 fRects->reset();
115 fGenID++;
116 fID = create_id(fIndex, fGenID);
117
118 // zero out the plot
joshualitte062db92015-04-24 08:33:21 -0700119 if (fData) {
120 sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
121 }
joshualitt5bf99f12015-03-13 11:47:42 -0700122
123 fDirtyRect.setEmpty();
124 SkDEBUGCODE(fDirty = false;)
125 }
126
joshualitt8db6fdc2015-07-31 08:25:07 -0700127 uint32_t x() const { return fX; }
128 uint32_t y() const { return fY; }
joshualitt5bf99f12015-03-13 11:47:42 -0700129
130private:
131 BatchPlot()
132 : fLastUpload(0)
joshualittb4c507e2015-04-08 08:07:59 -0700133 , fLastUse(0)
joshualitt5bf99f12015-03-13 11:47:42 -0700134 , fIndex(-1)
135 , fGenID(-1)
136 , fID(0)
137 , fData(NULL)
138 , fWidth(0)
139 , fHeight(0)
140 , fX(0)
141 , fY(0)
142 , fTexture(NULL)
143 , fRects(NULL)
144 , fAtlas(NULL)
145 , fBytesPerPixel(1)
146 #ifdef SK_DEBUG
147 , fDirty(false)
148 #endif
149 {
150 fOffset.set(0, 0);
151 }
152
153 ~BatchPlot() {
joshualitt8e71d382015-04-24 18:47:11 -0700154 sk_free(fData);
joshualitt5bf99f12015-03-13 11:47:42 -0700155 fData = NULL;
156 delete fRects;
157 }
158
joshualitt8db6fdc2015-07-31 08:25:07 -0700159 void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint64_t generation,
joshualitt5bf99f12015-03-13 11:47:42 -0700160 int offX, int offY, int width, int height, size_t bpp) {
161 fIndex = index;
162 fGenID = generation;
163 fID = create_id(index, generation);
164 fWidth = width;
165 fHeight = height;
166 fX = offX;
167 fY = offY;
168 fRects = GrRectanizer::Factory(width, height);
169 fAtlas = atlas;
170 fOffset.set(offX * width, offY * height);
171 fBytesPerPixel = bpp;
172 fData = NULL;
173 fDirtyRect.setEmpty();
174 SkDEBUGCODE(fDirty = false;)
175 fTexture = texture;
joshualitt5bf99f12015-03-13 11:47:42 -0700176 }
177
178 BatchToken fLastUpload;
joshualittb4c507e2015-04-08 08:07:59 -0700179 BatchToken fLastUse;
joshualitt5bf99f12015-03-13 11:47:42 -0700180
181 uint32_t fIndex;
joshualitt8db6fdc2015-07-31 08:25:07 -0700182 uint64_t fGenID;
joshualitt5bf99f12015-03-13 11:47:42 -0700183 GrBatchAtlas::AtlasID fID;
184 unsigned char* fData;
joshualitt8db6fdc2015-07-31 08:25:07 -0700185 uint32_t fWidth;
186 uint32_t fHeight;
187 uint32_t fX;
188 uint32_t fY;
joshualitt5bf99f12015-03-13 11:47:42 -0700189 GrTexture* fTexture;
190 GrRectanizer* fRects;
191 GrBatchAtlas* fAtlas;
192 SkIPoint16 fOffset; // the offset of the plot in the backing texture
193 size_t fBytesPerPixel;
194 SkIRect fDirtyRect;
195 SkDEBUGCODE(bool fDirty;)
196
197 friend class GrBatchAtlas;
198
199 typedef SkRefCnt INHERITED;
200};
201
202////////////////////////////////////////////////////////////////////////////////
203
204class GrPlotUploader : public GrBatchTarget::Uploader {
205public:
206 GrPlotUploader(BatchPlot* plot)
207 : INHERITED(plot->lastUploadToken())
208 , fPlot(SkRef(plot)) {
209 SkASSERT(plot);
210 }
211
mtklein36352bf2015-03-25 18:17:31 -0700212 void upload(GrBatchTarget::TextureUploader uploader) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700213 fPlot->uploadToTexture(uploader);
214 }
215
216private:
217 SkAutoTUnref<BatchPlot> fPlot;
218
219 typedef GrBatchTarget::Uploader INHERITED;
220};
221
222///////////////////////////////////////////////////////////////////////////////
223
224GrBatchAtlas::GrBatchAtlas(GrTexture* texture, int numPlotsX, int numPlotsY)
225 : fTexture(texture)
226 , fNumPlotsX(numPlotsX)
227 , fNumPlotsY(numPlotsY)
228 , fPlotWidth(texture->width() / numPlotsX)
joshualitt7c3a2f82015-03-31 13:32:05 -0700229 , fPlotHeight(texture->height() / numPlotsY)
230 , fAtlasGeneration(kInvalidAtlasGeneration + 1) {
joshualittb4c507e2015-04-08 08:07:59 -0700231 SkASSERT(fNumPlotsX * fNumPlotsY <= BulkUseTokenUpdater::kMaxPlots);
joshualitt8db6fdc2015-07-31 08:25:07 -0700232 SkASSERT(fPlotWidth * fNumPlotsX == static_cast<uint32_t>(texture->width()));
233 SkASSERT(fPlotHeight * fNumPlotsY == static_cast<uint32_t>(texture->height()));
joshualitt5bf99f12015-03-13 11:47:42 -0700234
235 // We currently do not support compressed atlases...
236 SkASSERT(!GrPixelConfigIsCompressed(texture->desc().fConfig));
237
238 // set up allocated plots
239 fBPP = GrBytesPerPixel(texture->desc().fConfig);
240 fPlotArray = SkNEW_ARRAY(SkAutoTUnref<BatchPlot>, (fNumPlotsX * fNumPlotsY));
241
242 SkAutoTUnref<BatchPlot>* currPlot = fPlotArray;
243 for (int y = fNumPlotsY - 1, r = 0; y >= 0; --y, ++r) {
244 for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) {
joshualitt8db6fdc2015-07-31 08:25:07 -0700245 uint32_t id = r * fNumPlotsX + c;
joshualitt5bf99f12015-03-13 11:47:42 -0700246 currPlot->reset(SkNEW(BatchPlot));
joshualitt7c3a2f82015-03-31 13:32:05 -0700247 (*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP);
joshualitt5bf99f12015-03-13 11:47:42 -0700248
249 // build LRU list
250 fPlotList.addToHead(currPlot->get());
251 ++currPlot;
252 }
253 }
254}
255
256GrBatchAtlas::~GrBatchAtlas() {
257 SkSafeUnref(fTexture);
258 SkDELETE_ARRAY(fPlotArray);
joshualitt5bf99f12015-03-13 11:47:42 -0700259}
260
261void GrBatchAtlas::processEviction(AtlasID id) {
262 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
263 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
264 }
265}
266
267void GrBatchAtlas::makeMRU(BatchPlot* plot) {
268 if (fPlotList.head() == plot) {
269 return;
270 }
271
272 fPlotList.remove(plot);
273 fPlotList.addToHead(plot);
274}
275
276inline void GrBatchAtlas::updatePlot(GrBatchTarget* batchTarget, AtlasID* id, BatchPlot* plot) {
277 this->makeMRU(plot);
278
279 // If our most recent upload has already occurred then we have to insert a new
280 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
281 // This new update will piggy back on that previously scheduled update.
282 if (batchTarget->isIssued(plot->lastUploadToken())) {
283 plot->setLastUploadToken(batchTarget->asapToken());
284 SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (plot)));
285 batchTarget->upload(uploader);
286 }
287 *id = plot->id();
288}
289
290bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
291 int width, int height, const void* image, SkIPoint16* loc) {
292 // We should already have a texture, TODO clean this up
joshualitt8db6fdc2015-07-31 08:25:07 -0700293 SkASSERT(fTexture &&
294 static_cast<uint32_t>(width) <= fPlotWidth &&
295 static_cast<uint32_t>(height) <= fPlotHeight);
joshualitt5bf99f12015-03-13 11:47:42 -0700296
297 // now look through all allocated plots for one we can share, in Most Recently Refed order
298 GrBatchPlotList::Iter plotIter;
299 plotIter.init(fPlotList, GrBatchPlotList::Iter::kHead_IterStart);
300 BatchPlot* plot;
301 while ((plot = plotIter.get())) {
302 if (plot->addSubImage(width, height, image, loc, fBPP * width)) {
303 this->updatePlot(batchTarget, id, plot);
304 return true;
305 }
306 plotIter.next();
307 }
308
309 // If the above fails, then see if the least recently refed plot has already been flushed to the
310 // gpu
311 plotIter.init(fPlotList, GrBatchPlotList::Iter::kTail_IterStart);
312 plot = plotIter.get();
313 SkASSERT(plot);
joshualittb4c507e2015-04-08 08:07:59 -0700314 if (batchTarget->isIssued(plot->lastUseToken())) {
joshualitt5bf99f12015-03-13 11:47:42 -0700315 this->processEviction(plot->id());
316 plot->resetRects();
317 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc, fBPP * width);
318 SkASSERT(verify);
319 this->updatePlot(batchTarget, id, plot);
joshualitt7c3a2f82015-03-31 13:32:05 -0700320 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700321 return true;
322 }
323
324 // The least recently refed plot hasn't been flushed to the gpu yet, however, if we have flushed
325 // it to the batch target than we can reuse it. Our last ref token is guaranteed to be less
326 // than or equal to the current token. If its 'less than' the current token, than we can spin
327 // off the plot(ie let the batch target manage it) and create a new plot in its place in our
328 // array. If it is equal to the currentToken, then the caller has to flush draws to the batch
329 // target so we can spin off the plot
joshualittb4c507e2015-04-08 08:07:59 -0700330 if (plot->lastUseToken() == batchTarget->currentToken()) {
joshualitt5bf99f12015-03-13 11:47:42 -0700331 return false;
332 }
333
334 // We take an extra ref here so our plot isn't deleted when we reset its index in the array.
335 plot->ref();
336 int index = plot->index();
337 int x = plot->x();
338 int y = plot->y();
joshualitt8db6fdc2015-07-31 08:25:07 -0700339 uint64_t generation = plot->genID();
joshualitt5bf99f12015-03-13 11:47:42 -0700340
341 this->processEviction(plot->id());
342 fPlotList.remove(plot);
343 SkAutoTUnref<BatchPlot>& newPlot = fPlotArray[plot->index()];
344 newPlot.reset(SkNEW(BatchPlot));
345 newPlot->init(this, fTexture, index, ++generation, x, y, fPlotWidth, fPlotHeight, fBPP);
346
347 fPlotList.addToHead(newPlot.get());
348 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc, fBPP * width);
349 SkASSERT(verify);
350 newPlot->setLastUploadToken(batchTarget->currentToken());
351 SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (newPlot)));
352 batchTarget->upload(uploader);
353 *id = newPlot->id();
354 plot->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -0700355 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700356 return true;
357}
358
359bool GrBatchAtlas::hasID(AtlasID id) {
joshualitt8db6fdc2015-07-31 08:25:07 -0700360 uint32_t index = GetIndexFromID(id);
joshualitt5bf99f12015-03-13 11:47:42 -0700361 SkASSERT(index < fNumPlotsX * fNumPlotsY);
joshualittb4c507e2015-04-08 08:07:59 -0700362 return fPlotArray[index]->genID() == GetGenerationFromID(id);
joshualitt5bf99f12015-03-13 11:47:42 -0700363}
364
joshualittb4c507e2015-04-08 08:07:59 -0700365void GrBatchAtlas::setLastUseToken(AtlasID id, BatchToken batchToken) {
joshualitt5bf99f12015-03-13 11:47:42 -0700366 SkASSERT(this->hasID(id));
joshualitt8db6fdc2015-07-31 08:25:07 -0700367 uint32_t index = GetIndexFromID(id);
joshualittb4c507e2015-04-08 08:07:59 -0700368 SkASSERT(index < fNumPlotsX * fNumPlotsY);
joshualitt5bf99f12015-03-13 11:47:42 -0700369 this->makeMRU(fPlotArray[index]);
joshualittb4c507e2015-04-08 08:07:59 -0700370 fPlotArray[index]->setLastUseToken(batchToken);
371}
372
373void GrBatchAtlas::setLastUseTokenBulk(const BulkUseTokenUpdater& updater, BatchToken batchToken) {
joshualitt4314e082015-04-23 08:03:35 -0700374 int count = updater.fPlotsToUpdate.count();
375 for (int i = 0; i < count; i++) {
joshualittb4c507e2015-04-08 08:07:59 -0700376 BatchPlot* plot = fPlotArray[updater.fPlotsToUpdate[i]];
377 this->makeMRU(plot);
378 plot->setLastUseToken(batchToken);
379 }
joshualitt5bf99f12015-03-13 11:47:42 -0700380}