blob: 6661b4855cc16512d03155d4c77d42c0d918dc33 [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
Brian Salomon903da792016-12-16 14:24:46 -05008#include "GrDrawOpAtlas.h"
Robert Phillips32f28182017-02-28 16:20:03 -05009
10#include "GrContext.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050011#include "GrOpFlushState.h"
joshualitt5bf99f12015-03-13 11:47:42 -070012#include "GrRectanizer.h"
13#include "GrTracing.h"
14
joshualitt5df175e2015-11-18 13:37:54 -080015////////////////////////////////////////////////////////////////////////////////
joshualitt5bf99f12015-03-13 11:47:42 -070016
Brian Salomon2ee084e2016-12-16 18:59:19 -050017GrDrawOpAtlas::Plot::Plot(int index, uint64_t genID, int offX, int offY, int width, int height,
18 GrPixelConfig config)
19 : fLastUpload(GrDrawOpUploadToken::AlreadyFlushedToken())
20 , fLastUse(GrDrawOpUploadToken::AlreadyFlushedToken())
21 , fIndex(index)
22 , fGenID(genID)
23 , fID(CreateId(fIndex, fGenID))
24 , fData(nullptr)
25 , fWidth(width)
26 , fHeight(height)
27 , fX(offX)
28 , fY(offY)
29 , fRects(nullptr)
30 , fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
31 , fConfig(config)
32 , fBytesPerPixel(GrBytesPerPixel(config))
joshualitt5df175e2015-11-18 13:37:54 -080033#ifdef SK_DEBUG
Brian Salomon2ee084e2016-12-16 18:59:19 -050034 , fDirty(false)
joshualitt5df175e2015-11-18 13:37:54 -080035#endif
36{
37 fDirtyRect.setEmpty();
38}
joshualitt5bf99f12015-03-13 11:47:42 -070039
Brian Salomon2ee084e2016-12-16 18:59:19 -050040GrDrawOpAtlas::Plot::~Plot() {
jvanverthc3d706f2016-04-20 10:33:27 -070041 sk_free(fData);
joshualitt5df175e2015-11-18 13:37:54 -080042 delete fRects;
43}
joshualitt5bf99f12015-03-13 11:47:42 -070044
Brian Salomon2ee084e2016-12-16 18:59:19 -050045bool GrDrawOpAtlas::Plot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
joshualitt5df175e2015-11-18 13:37:54 -080046 SkASSERT(width <= fWidth && height <= fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070047
joshualitt5df175e2015-11-18 13:37:54 -080048 if (!fRects) {
49 fRects = GrRectanizer::Factory(fWidth, fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070050 }
51
joshualitt5df175e2015-11-18 13:37:54 -080052 if (!fRects->addRect(width, height, loc)) {
53 return false;
joshualittb4c507e2015-04-08 08:07:59 -070054 }
joshualitt5bf99f12015-03-13 11:47:42 -070055
jvanverthc3d706f2016-04-20 10:33:27 -070056 if (!fData) {
57 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
58 fHeight));
joshualitt5df175e2015-11-18 13:37:54 -080059 }
60 size_t rowBytes = width * fBytesPerPixel;
61 const unsigned char* imagePtr = (const unsigned char*)image;
62 // point ourselves at the right starting spot
jvanverthc3d706f2016-04-20 10:33:27 -070063 unsigned char* dataPtr = fData;
joshualitt5df175e2015-11-18 13:37:54 -080064 dataPtr += fBytesPerPixel * fWidth * loc->fY;
65 dataPtr += fBytesPerPixel * loc->fX;
Brian Osmancce3e582016-10-14 11:42:20 -040066 // copy into the data buffer, swizzling as we go if this is ARGB data
67 if (4 == fBytesPerPixel && kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
68 for (int i = 0; i < height; ++i) {
69 SkOpts::RGBA_to_BGRA(reinterpret_cast<uint32_t*>(dataPtr), imagePtr, width);
70 dataPtr += fBytesPerPixel * fWidth;
71 imagePtr += rowBytes;
72 }
73 } else {
74 for (int i = 0; i < height; ++i) {
75 memcpy(dataPtr, imagePtr, rowBytes);
76 dataPtr += fBytesPerPixel * fWidth;
77 imagePtr += rowBytes;
78 }
joshualitt5bf99f12015-03-13 11:47:42 -070079 }
80
joshualitt5df175e2015-11-18 13:37:54 -080081 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
robertphillips2b0536f2015-11-06 14:10:42 -080082
joshualitt5df175e2015-11-18 13:37:54 -080083 loc->fX += fOffset.fX;
84 loc->fY += fOffset.fY;
85 SkDEBUGCODE(fDirty = true;)
joshualitt5bf99f12015-03-13 11:47:42 -070086
joshualitt5df175e2015-11-18 13:37:54 -080087 return true;
88}
joshualitt5bf99f12015-03-13 11:47:42 -070089
Brian Salomon2ee084e2016-12-16 18:59:19 -050090void GrDrawOpAtlas::Plot::uploadToTexture(GrDrawOp::WritePixelsFn& writePixels,
91 GrTexture* texture) {
joshualitt5df175e2015-11-18 13:37:54 -080092 // We should only be issuing uploads if we are in fact dirty
jvanverthc3d706f2016-04-20 10:33:27 -070093 SkASSERT(fDirty && fData && texture);
Brian Salomon2ee084e2016-12-16 18:59:19 -050094 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrDrawOpAtlas::Plot::uploadToTexture");
joshualitt5df175e2015-11-18 13:37:54 -080095 size_t rowBytes = fBytesPerPixel * fWidth;
jvanverthc3d706f2016-04-20 10:33:27 -070096 const unsigned char* dataPtr = fData;
97 dataPtr += rowBytes * fDirtyRect.fTop;
98 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
99 writePixels(texture, fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
100 fDirtyRect.width(), fDirtyRect.height(), fConfig, dataPtr, rowBytes);
joshualitt5df175e2015-11-18 13:37:54 -0800101 fDirtyRect.setEmpty();
102 SkDEBUGCODE(fDirty = false;)
103}
104
Brian Salomon2ee084e2016-12-16 18:59:19 -0500105void GrDrawOpAtlas::Plot::resetRects() {
joshualitt5df175e2015-11-18 13:37:54 -0800106 if (fRects) {
107 fRects->reset();
joshualitt5bf99f12015-03-13 11:47:42 -0700108 }
109
joshualitt5df175e2015-11-18 13:37:54 -0800110 fGenID++;
111 fID = CreateId(fIndex, fGenID);
112
113 // zero out the plot
jvanverthc3d706f2016-04-20 10:33:27 -0700114 if (fData) {
115 sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -0700116 }
117
joshualitt5df175e2015-11-18 13:37:54 -0800118 fDirtyRect.setEmpty();
119 SkDEBUGCODE(fDirty = false;)
120}
joshualitt5bf99f12015-03-13 11:47:42 -0700121
joshualitt5bf99f12015-03-13 11:47:42 -0700122///////////////////////////////////////////////////////////////////////////////
123
Robert Phillips32f28182017-02-28 16:20:03 -0500124GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, sk_sp<GrTextureProxy> proxy,
125 int numPlotsX, int numPlotsY)
126 : fContext(context)
127 , fProxy(std::move(proxy))
128 , fAtlasGeneration(kInvalidAtlasGeneration + 1) {
129 fPlotWidth = fProxy->width() / numPlotsX;
130 fPlotHeight = fProxy->height() / numPlotsY;
robertphillips2b0536f2015-11-06 14:10:42 -0800131 SkASSERT(numPlotsX * numPlotsY <= BulkUseTokenUpdater::kMaxPlots);
Robert Phillips32f28182017-02-28 16:20:03 -0500132 SkASSERT(fPlotWidth * numPlotsX == fProxy->width());
133 SkASSERT(fPlotHeight * numPlotsY == fProxy->height());
robertphillips2b0536f2015-11-06 14:10:42 -0800134
135 SkDEBUGCODE(fNumPlots = numPlotsX * numPlotsY;)
joshualitt5bf99f12015-03-13 11:47:42 -0700136
137 // We currently do not support compressed atlases...
Robert Phillips32f28182017-02-28 16:20:03 -0500138 SkASSERT(!GrPixelConfigIsCompressed(fProxy->desc().fConfig));
joshualitt5bf99f12015-03-13 11:47:42 -0700139
140 // set up allocated plots
Brian Salomon2ee084e2016-12-16 18:59:19 -0500141 fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
joshualitt5bf99f12015-03-13 11:47:42 -0700142
Brian Salomon2ee084e2016-12-16 18:59:19 -0500143 sk_sp<Plot>* currPlot = fPlotArray.get();
robertphillips2b0536f2015-11-06 14:10:42 -0800144 for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
145 for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
146 uint32_t index = r * numPlotsX + c;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500147 currPlot->reset(
Robert Phillips32f28182017-02-28 16:20:03 -0500148 new Plot(index, 1, x, y, fPlotWidth, fPlotHeight, fProxy->desc().fConfig));
joshualitt5bf99f12015-03-13 11:47:42 -0700149
150 // build LRU list
151 fPlotList.addToHead(currPlot->get());
152 ++currPlot;
153 }
154 }
155}
156
Brian Salomon2ee084e2016-12-16 18:59:19 -0500157void GrDrawOpAtlas::processEviction(AtlasID id) {
joshualitt5bf99f12015-03-13 11:47:42 -0700158 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
159 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
160 }
161}
162
Brian Salomon2ee084e2016-12-16 18:59:19 -0500163inline void GrDrawOpAtlas::updatePlot(GrDrawOp::Target* target, AtlasID* id, Plot* plot) {
joshualitt5bf99f12015-03-13 11:47:42 -0700164 this->makeMRU(plot);
165
166 // If our most recent upload has already occurred then we have to insert a new
167 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
168 // This new update will piggy back on that previously scheduled update.
bsalomon342bfc22016-04-01 06:06:20 -0700169 if (target->hasDrawBeenFlushed(plot->lastUploadToken())) {
jvanverthc3d706f2016-04-20 10:33:27 -0700170 // With c+14 we could move sk_sp into lamba to only ref once.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500171 sk_sp<Plot> plotsp(SkRef(plot));
Robert Phillips32f28182017-02-28 16:20:03 -0500172 // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
173 // Once it is deferred more care must be taken upon instantiation failure.
174 GrTexture* texture = fProxy->instantiate(fContext->textureProvider());
175 if (texture) {
176 GrDrawOpUploadToken lastUploadToken = target->addAsapUpload(
177 [plotsp, texture] (GrDrawOp::WritePixelsFn& writePixels) {
178 plotsp->uploadToTexture(writePixels, texture);
179 }
180 );
181 plot->setLastUploadToken(lastUploadToken);
182 }
joshualitt5bf99f12015-03-13 11:47:42 -0700183 }
184 *id = plot->id();
185}
186
Brian Salomon2ee084e2016-12-16 18:59:19 -0500187bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDrawOp::Target* target, int width, int height,
188 const void* image, SkIPoint16* loc) {
joshualitt5bf99f12015-03-13 11:47:42 -0700189 // We should already have a texture, TODO clean this up
Robert Phillips32f28182017-02-28 16:20:03 -0500190 SkASSERT(fProxy);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700191 if (width > fPlotWidth || height > fPlotHeight) {
192 return false;
193 }
joshualitt5bf99f12015-03-13 11:47:42 -0700194
195 // now look through all allocated plots for one we can share, in Most Recently Refed order
Brian Salomon2ee084e2016-12-16 18:59:19 -0500196 PlotList::Iter plotIter;
197 plotIter.init(fPlotList, PlotList::Iter::kHead_IterStart);
198 Plot* plot;
joshualitt5bf99f12015-03-13 11:47:42 -0700199 while ((plot = plotIter.get())) {
Robert Phillips32f28182017-02-28 16:20:03 -0500200 SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == plot->bpp());
robertphillips2b0536f2015-11-06 14:10:42 -0800201 if (plot->addSubImage(width, height, image, loc)) {
bsalomon342bfc22016-04-01 06:06:20 -0700202 this->updatePlot(target, id, plot);
joshualitt5bf99f12015-03-13 11:47:42 -0700203 return true;
204 }
205 plotIter.next();
206 }
207
208 // If the above fails, then see if the least recently refed plot has already been flushed to the
209 // gpu
robertphillips2b0536f2015-11-06 14:10:42 -0800210 plot = fPlotList.tail();
joshualitt5bf99f12015-03-13 11:47:42 -0700211 SkASSERT(plot);
bsalomon342bfc22016-04-01 06:06:20 -0700212 if (target->hasDrawBeenFlushed(plot->lastUseToken())) {
joshualitt5bf99f12015-03-13 11:47:42 -0700213 this->processEviction(plot->id());
214 plot->resetRects();
Robert Phillips32f28182017-02-28 16:20:03 -0500215 SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == plot->bpp());
robertphillips2b0536f2015-11-06 14:10:42 -0800216 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
joshualitt5bf99f12015-03-13 11:47:42 -0700217 SkASSERT(verify);
bsalomon342bfc22016-04-01 06:06:20 -0700218 this->updatePlot(target, id, plot);
joshualitt7c3a2f82015-03-31 13:32:05 -0700219 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700220 return true;
221 }
222
Brian Salomon2ee084e2016-12-16 18:59:19 -0500223 // If this plot has been used in a draw that is currently being prepared by an op, then we have
224 // to fail. This gives the op a chance to enqueue the draw, and call back into this function.
225 // When that draw is enqueued, the draw token advances, and the subsequent call will continue
226 // past this branch and prepare an inline upload that will occur after the enqueued draw which
227 // references the plot's pre-upload content.
bsalomon342bfc22016-04-01 06:06:20 -0700228 if (plot->lastUseToken() == target->nextDrawToken()) {
joshualitt5bf99f12015-03-13 11:47:42 -0700229 return false;
230 }
231
joshualitt5bf99f12015-03-13 11:47:42 -0700232 this->processEviction(plot->id());
233 fPlotList.remove(plot);
Brian Salomon2ee084e2016-12-16 18:59:19 -0500234 sk_sp<Plot>& newPlot = fPlotArray[plot->index()];
robertphillips2b0536f2015-11-06 14:10:42 -0800235 newPlot.reset(plot->clone());
joshualitt5bf99f12015-03-13 11:47:42 -0700236
237 fPlotList.addToHead(newPlot.get());
Robert Phillips32f28182017-02-28 16:20:03 -0500238 SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == newPlot->bpp());
robertphillips2b0536f2015-11-06 14:10:42 -0800239 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
joshualitt5bf99f12015-03-13 11:47:42 -0700240 SkASSERT(verify);
robertphillips2b0536f2015-11-06 14:10:42 -0800241
robertphillips1f0e3502015-11-10 10:19:50 -0800242 // Note that this plot will be uploaded inline with the draws whereas the
243 // one it displaced most likely was uploaded asap.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500244 // With c+14 we could move sk_sp into lambda to only ref once.
245 sk_sp<Plot> plotsp(SkRef(newPlot.get()));
Robert Phillips32f28182017-02-28 16:20:03 -0500246 // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
247 // Once it is deferred more care must be taken upon instantiation failure.
248 GrTexture* texture = fProxy->instantiate(fContext->textureProvider());
249 if (texture) {
250 GrDrawOpUploadToken lastUploadToken = target->addInlineUpload(
251 [plotsp, texture] (GrDrawOp::WritePixelsFn& writePixels) {
252 plotsp->uploadToTexture(writePixels, texture);
253 }
254 );
255 newPlot->setLastUploadToken(lastUploadToken);
256 }
bsalomon342bfc22016-04-01 06:06:20 -0700257
joshualitt5bf99f12015-03-13 11:47:42 -0700258 *id = newPlot->id();
robertphillips2b0536f2015-11-06 14:10:42 -0800259
joshualitt7c3a2f82015-03-31 13:32:05 -0700260 fAtlasGeneration++;
joshualitt5bf99f12015-03-13 11:47:42 -0700261 return true;
262}