blob: d891c027b945e83c23fec56b3ba10bc614b4d4ab [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"
Robert Phillips0bd24dc2018-01-16 08:06:32 -050011#include "GrContextPriv.h"
Robert Phillipscd5099c2018-02-09 09:56:56 -050012#include "GrOnFlushResourceProvider.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050013#include "GrOpFlushState.h"
joshualitt5bf99f12015-03-13 11:47:42 -070014#include "GrRectanizer.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -050015#include "GrProxyProvider.h"
Robert Phillips256c37b2017-03-01 14:32:46 -050016#include "GrResourceProvider.h"
Robert Phillipsc4039ea2018-03-01 11:36:45 -050017#include "GrSurfaceProxyPriv.h"
Robert Phillips646e4292017-06-13 12:44:56 -040018#include "GrTexture.h"
joshualitt5bf99f12015-03-13 11:47:42 -070019#include "GrTracing.h"
20
Robert Phillipscd5099c2018-02-09 09:56:56 -050021// When proxy allocation is deferred until flush time the proxies acting as atlases require
22// special handling. This is because the usage that can be determined from the ops themselves
23// isn't sufficient. Independent of the ops there will be ASAP and inline uploads to the
24// atlases. Extending the usage interval of any op that uses an atlas to the start of the
25// flush (as is done for proxies that are used for sw-generated masks) also won't work because
26// the atlas persists even beyond the last use in an op - for a given flush. Given this, atlases
27// must explicitly manage the lifetime of their backing proxies via the onFlushCallback system
28// (which calls this method).
29void GrDrawOpAtlas::instantiate(GrOnFlushResourceProvider* onFlushResourceProvider) {
Robert Phillips4bc70112018-03-01 10:24:02 -050030 for (uint32_t i = 0; i < fNumActivePages; ++i) {
31 // All the atlas pages are now instantiated at flush time in the activeNewPage method.
Brian Salomonfd98c2c2018-07-31 17:25:29 -040032 SkASSERT(fProxies[i] && fProxies[i]->isInstantiated());
Robert Phillipscd5099c2018-02-09 09:56:56 -050033 }
34}
35
Robert Phillips4bc70112018-03-01 10:24:02 -050036std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvider,
37 GrPixelConfig config, int width,
Brian Salomon9f545bc2017-11-06 10:36:57 -050038 int height, int numPlotsX, int numPlotsY,
39 AllowMultitexturing allowMultitexturing,
40 GrDrawOpAtlas::EvictionFunc func, void* data) {
Robert Phillips4bc70112018-03-01 10:24:02 -050041 std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, config, width, height,
42 numPlotsX, numPlotsY,
43 allowMultitexturing));
Jim Van Vertha950b632017-09-12 11:54:11 -040044 if (!atlas->getProxies()[0]) {
Jim Van Verthd74f3f22017-08-31 16:44:08 -040045 return nullptr;
46 }
47
Robert Phillips256c37b2017-03-01 14:32:46 -050048 atlas->registerEvictionCallback(func, data);
49 return atlas;
50}
51
Jim Van Verthc3269ae2017-09-28 15:04:00 -040052#ifdef DUMP_ATLAS_DATA
53static bool gDumpAtlasData = false;
54#endif
Robert Phillips256c37b2017-03-01 14:32:46 -050055
joshualitt5df175e2015-11-18 13:37:54 -080056////////////////////////////////////////////////////////////////////////////////
Jim Van Vertha950b632017-09-12 11:54:11 -040057GrDrawOpAtlas::Plot::Plot(int pageIndex, int plotIndex, uint64_t genID, int offX, int offY,
58 int width, int height, GrPixelConfig config)
Brian Salomon943ed792017-10-30 09:37:55 -040059 : fLastUpload(GrDeferredUploadToken::AlreadyFlushedToken())
60 , fLastUse(GrDeferredUploadToken::AlreadyFlushedToken())
Jim Van Verth106b5c42017-09-26 12:45:29 -040061 , fFlushesSinceLastUse(0)
Jim Van Vertha950b632017-09-12 11:54:11 -040062 , fPageIndex(pageIndex)
63 , fPlotIndex(plotIndex)
Brian Salomon2ee084e2016-12-16 18:59:19 -050064 , fGenID(genID)
Jim Van Vertha950b632017-09-12 11:54:11 -040065 , fID(CreateId(fPageIndex, fPlotIndex, fGenID))
Brian Salomon2ee084e2016-12-16 18:59:19 -050066 , fData(nullptr)
67 , fWidth(width)
68 , fHeight(height)
69 , fX(offX)
70 , fY(offY)
71 , fRects(nullptr)
72 , fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
73 , fConfig(config)
74 , fBytesPerPixel(GrBytesPerPixel(config))
joshualitt5df175e2015-11-18 13:37:54 -080075#ifdef SK_DEBUG
Brian Salomon2ee084e2016-12-16 18:59:19 -050076 , fDirty(false)
joshualitt5df175e2015-11-18 13:37:54 -080077#endif
78{
Jim Van Vertha8c55fa2018-02-20 15:38:08 -050079 // We expect the allocated dimensions to be a multiple of 4 bytes
80 SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
81 // The padding for faster uploads only works for 1, 2 and 4 byte texels
82 SkASSERT(fBytesPerPixel != 3 && fBytesPerPixel <= 4);
joshualitt5df175e2015-11-18 13:37:54 -080083 fDirtyRect.setEmpty();
84}
joshualitt5bf99f12015-03-13 11:47:42 -070085
Brian Salomon2ee084e2016-12-16 18:59:19 -050086GrDrawOpAtlas::Plot::~Plot() {
jvanverthc3d706f2016-04-20 10:33:27 -070087 sk_free(fData);
joshualitt5df175e2015-11-18 13:37:54 -080088 delete fRects;
89}
joshualitt5bf99f12015-03-13 11:47:42 -070090
Brian Salomon2ee084e2016-12-16 18:59:19 -050091bool GrDrawOpAtlas::Plot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
joshualitt5df175e2015-11-18 13:37:54 -080092 SkASSERT(width <= fWidth && height <= fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070093
joshualitt5df175e2015-11-18 13:37:54 -080094 if (!fRects) {
95 fRects = GrRectanizer::Factory(fWidth, fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070096 }
97
joshualitt5df175e2015-11-18 13:37:54 -080098 if (!fRects->addRect(width, height, loc)) {
99 return false;
joshualittb4c507e2015-04-08 08:07:59 -0700100 }
joshualitt5bf99f12015-03-13 11:47:42 -0700101
jvanverthc3d706f2016-04-20 10:33:27 -0700102 if (!fData) {
103 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
104 fHeight));
joshualitt5df175e2015-11-18 13:37:54 -0800105 }
106 size_t rowBytes = width * fBytesPerPixel;
107 const unsigned char* imagePtr = (const unsigned char*)image;
108 // point ourselves at the right starting spot
jvanverthc3d706f2016-04-20 10:33:27 -0700109 unsigned char* dataPtr = fData;
joshualitt5df175e2015-11-18 13:37:54 -0800110 dataPtr += fBytesPerPixel * fWidth * loc->fY;
111 dataPtr += fBytesPerPixel * loc->fX;
Brian Osmancce3e582016-10-14 11:42:20 -0400112 // copy into the data buffer, swizzling as we go if this is ARGB data
113 if (4 == fBytesPerPixel && kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
114 for (int i = 0; i < height; ++i) {
115 SkOpts::RGBA_to_BGRA(reinterpret_cast<uint32_t*>(dataPtr), imagePtr, width);
116 dataPtr += fBytesPerPixel * fWidth;
117 imagePtr += rowBytes;
118 }
119 } else {
120 for (int i = 0; i < height; ++i) {
121 memcpy(dataPtr, imagePtr, rowBytes);
122 dataPtr += fBytesPerPixel * fWidth;
123 imagePtr += rowBytes;
124 }
joshualitt5bf99f12015-03-13 11:47:42 -0700125 }
126
joshualitt5df175e2015-11-18 13:37:54 -0800127 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
robertphillips2b0536f2015-11-06 14:10:42 -0800128
joshualitt5df175e2015-11-18 13:37:54 -0800129 loc->fX += fOffset.fX;
130 loc->fY += fOffset.fY;
131 SkDEBUGCODE(fDirty = true;)
joshualitt5bf99f12015-03-13 11:47:42 -0700132
joshualitt5df175e2015-11-18 13:37:54 -0800133 return true;
134}
joshualitt5bf99f12015-03-13 11:47:42 -0700135
Brian Salomon943ed792017-10-30 09:37:55 -0400136void GrDrawOpAtlas::Plot::uploadToTexture(GrDeferredTextureUploadWritePixelsFn& writePixels,
Robert Phillipsacaa6072017-07-28 10:54:53 -0400137 GrTextureProxy* proxy) {
joshualitt5df175e2015-11-18 13:37:54 -0800138 // We should only be issuing uploads if we are in fact dirty
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400139 SkASSERT(fDirty && fData && proxy && proxy->peekTexture());
Brian Osman39c08ac2017-07-26 09:36:09 -0400140 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
joshualitt5df175e2015-11-18 13:37:54 -0800141 size_t rowBytes = fBytesPerPixel * fWidth;
jvanverthc3d706f2016-04-20 10:33:27 -0700142 const unsigned char* dataPtr = fData;
Jim Van Vertha8c55fa2018-02-20 15:38:08 -0500143 // Clamp to 4-byte aligned boundaries
144 unsigned int clearBits = 0x3 / fBytesPerPixel;
145 fDirtyRect.fLeft &= ~clearBits;
146 fDirtyRect.fRight += clearBits;
147 fDirtyRect.fRight &= ~clearBits;
148 SkASSERT(fDirtyRect.fRight <= fWidth);
149 // Set up dataPtr
jvanverthc3d706f2016-04-20 10:33:27 -0700150 dataPtr += rowBytes * fDirtyRect.fTop;
151 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
Brian Salomonc320b152018-02-20 14:05:36 -0500152 // TODO: Make GrDrawOpAtlas store a GrColorType rather than GrPixelConfig.
153 auto colorType = GrPixelConfigToColorType(fConfig);
Robert Phillipsacaa6072017-07-28 10:54:53 -0400154 writePixels(proxy, fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
Brian Salomonc320b152018-02-20 14:05:36 -0500155 fDirtyRect.width(), fDirtyRect.height(), colorType, dataPtr, rowBytes);
joshualitt5df175e2015-11-18 13:37:54 -0800156 fDirtyRect.setEmpty();
157 SkDEBUGCODE(fDirty = false;)
158}
159
Brian Salomon2ee084e2016-12-16 18:59:19 -0500160void GrDrawOpAtlas::Plot::resetRects() {
joshualitt5df175e2015-11-18 13:37:54 -0800161 if (fRects) {
162 fRects->reset();
joshualitt5bf99f12015-03-13 11:47:42 -0700163 }
164
joshualitt5df175e2015-11-18 13:37:54 -0800165 fGenID++;
Jim Van Vertha950b632017-09-12 11:54:11 -0400166 fID = CreateId(fPageIndex, fPlotIndex, fGenID);
Brian Salomon943ed792017-10-30 09:37:55 -0400167 fLastUpload = GrDeferredUploadToken::AlreadyFlushedToken();
168 fLastUse = GrDeferredUploadToken::AlreadyFlushedToken();
joshualitt5df175e2015-11-18 13:37:54 -0800169
170 // zero out the plot
jvanverthc3d706f2016-04-20 10:33:27 -0700171 if (fData) {
172 sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -0700173 }
174
joshualitt5df175e2015-11-18 13:37:54 -0800175 fDirtyRect.setEmpty();
176 SkDEBUGCODE(fDirty = false;)
177}
joshualitt5bf99f12015-03-13 11:47:42 -0700178
joshualitt5bf99f12015-03-13 11:47:42 -0700179///////////////////////////////////////////////////////////////////////////////
180
Robert Phillips4bc70112018-03-01 10:24:02 -0500181GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider,
182 GrPixelConfig config, int width, int height,
Brian Salomon9f545bc2017-11-06 10:36:57 -0500183 int numPlotsX, int numPlotsY, AllowMultitexturing allowMultitexturing)
Robert Phillips4bc70112018-03-01 10:24:02 -0500184 : fPixelConfig(config)
Jim Van Verthd74f3f22017-08-31 16:44:08 -0400185 , fTextureWidth(width)
186 , fTextureHeight(height)
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400187 , fAtlasGeneration(kInvalidAtlasGeneration + 1)
Brian Salomon943ed792017-10-30 09:37:55 -0400188 , fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500189 , fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ? kMaxMultitexturePages : 1)
Robert Phillips4bc70112018-03-01 10:24:02 -0500190 , fNumActivePages(0) {
Jim Van Verthd74f3f22017-08-31 16:44:08 -0400191 fPlotWidth = fTextureWidth / numPlotsX;
192 fPlotHeight = fTextureHeight / numPlotsY;
robertphillips2b0536f2015-11-06 14:10:42 -0800193 SkASSERT(numPlotsX * numPlotsY <= BulkUseTokenUpdater::kMaxPlots);
Jim Van Verthd74f3f22017-08-31 16:44:08 -0400194 SkASSERT(fPlotWidth * numPlotsX == fTextureWidth);
195 SkASSERT(fPlotHeight * numPlotsY == fTextureHeight);
robertphillips2b0536f2015-11-06 14:10:42 -0800196
Jim Van Verth06f593c2018-02-20 11:30:10 -0500197 fNumPlots = numPlotsX * numPlotsY;
joshualitt5bf99f12015-03-13 11:47:42 -0700198
Robert Phillips4bc70112018-03-01 10:24:02 -0500199 this->createPages(proxyProvider);
joshualitt5bf99f12015-03-13 11:47:42 -0700200}
201
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400202inline void GrDrawOpAtlas::processEviction(AtlasID id) {
joshualitt5bf99f12015-03-13 11:47:42 -0700203 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
204 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
205 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400206 ++fAtlasGeneration;
joshualitt5bf99f12015-03-13 11:47:42 -0700207}
208
Brian Salomon29b60c92017-10-31 14:42:10 -0400209inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* id, Plot* plot) {
Jim Van Vertha950b632017-09-12 11:54:11 -0400210 int pageIdx = GetPageIndexFromID(plot->id());
211 this->makeMRU(plot, pageIdx);
joshualitt5bf99f12015-03-13 11:47:42 -0700212
213 // If our most recent upload has already occurred then we have to insert a new
214 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
215 // This new update will piggy back on that previously scheduled update.
Robert Phillips40a29d72018-01-18 12:59:22 -0500216 if (plot->lastUploadToken() < target->tokenTracker()->nextTokenToFlush()) {
jvanverthc3d706f2016-04-20 10:33:27 -0700217 // With c+14 we could move sk_sp into lamba to only ref once.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500218 sk_sp<Plot> plotsp(SkRef(plot));
Robert Phillips256c37b2017-03-01 14:32:46 -0500219
Jim Van Vertha950b632017-09-12 11:54:11 -0400220 GrTextureProxy* proxy = fProxies[pageIdx].get();
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400221 SkASSERT(proxy->isInstantiated()); // This is occurring at flush time
Robert Phillips256c37b2017-03-01 14:32:46 -0500222
Brian Salomon29b60c92017-10-31 14:42:10 -0400223 GrDeferredUploadToken lastUploadToken = target->addASAPUpload(
Brian Salomon943ed792017-10-30 09:37:55 -0400224 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
225 plotsp->uploadToTexture(writePixels, proxy);
226 });
Robert Phillips256c37b2017-03-01 14:32:46 -0500227 plot->setLastUploadToken(lastUploadToken);
joshualitt5bf99f12015-03-13 11:47:42 -0700228 }
229 *id = plot->id();
Robert Phillips256c37b2017-03-01 14:32:46 -0500230 return true;
joshualitt5bf99f12015-03-13 11:47:42 -0700231}
232
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500233bool GrDrawOpAtlas::uploadToPage(unsigned int pageIdx, AtlasID* id, GrDeferredUploadTarget* target,
234 int width, int height, const void* image, SkIPoint16* loc) {
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400235 SkASSERT(fProxies[pageIdx] && fProxies[pageIdx]->isInstantiated());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500236
237 // look through all allocated plots for one we can share, in Most Recently Refed order
238 PlotList::Iter plotIter;
239 plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
240
241 for (Plot* plot = plotIter.get(); plot; plot = plotIter.next()) {
242 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
243
244 if (plot->addSubImage(width, height, image, loc)) {
245 return this->updatePlot(target, id, plot);
246 }
247 }
248
249 return false;
250}
251
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400252// Number of atlas-related flushes beyond which we consider a plot to no longer be in use.
253//
254// This value is somewhat arbitrary -- the idea is to keep it low enough that
255// a page with unused plots will get removed reasonably quickly, but allow it
256// to hang around for a bit in case it's needed. The assumption is that flushes
257// are rare; i.e., we are not continually refreshing the frame.
Derek Sollenberger90196cc2017-10-09 15:00:33 -0400258static constexpr auto kRecentlyUsedCount = 256;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400259
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500260GrDrawOpAtlas::ErrorCode GrDrawOpAtlas::addToAtlas(GrResourceProvider* resourceProvider,
261 AtlasID* id, GrDeferredUploadTarget* target,
262 int width, int height,
263 const void* image, SkIPoint16* loc) {
bsalomon6d6b6ad2016-07-13 14:45:28 -0700264 if (width > fPlotWidth || height > fPlotHeight) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500265 return ErrorCode::kError;
bsalomon6d6b6ad2016-07-13 14:45:28 -0700266 }
joshualitt5bf99f12015-03-13 11:47:42 -0700267
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400268 // Look through each page to see if we can upload without having to flush
269 // We prioritize this upload to the first pages, not the most recently used, to make it easier
270 // to remove unused pages in reverse page order.
Robert Phillips4bc70112018-03-01 10:24:02 -0500271 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500272 if (this->uploadToPage(pageIdx, id, target, width, height, image, loc)) {
273 return ErrorCode::kSucceeded;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400274 }
Jim Van Verth712fe732017-09-25 16:53:49 -0400275 }
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400276
Jim Van Verth712fe732017-09-25 16:53:49 -0400277 // If the above fails, then see if the least recently used plot per page has already been
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400278 // flushed to the gpu if we're at max page allocation, or if the plot has aged out otherwise.
279 // We wait until we've grown to the full number of pages to begin evicting already flushed
280 // plots so that we can maximize the opportunity for reuse.
Jim Van Verth712fe732017-09-25 16:53:49 -0400281 // As before we prioritize this upload to the first pages, not the most recently used.
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500282 if (fNumActivePages == this->maxPages()) {
283 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
284 Plot* plot = fPages[pageIdx].fPlotList.tail();
285 SkASSERT(plot);
286 if (plot->lastUseToken() < target->tokenTracker()->nextTokenToFlush() ||
287 plot->flushesSinceLastUsed() >= kRecentlyUsedCount) {
288 this->processEvictionAndResetRects(plot);
289 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
290 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
291 SkASSERT(verify);
292 if (!this->updatePlot(target, id, plot)) {
293 return ErrorCode::kError;
294 }
295 return ErrorCode::kSucceeded;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400296 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500297 }
298 } else {
299 // If we haven't activated all the available pages, try to create a new one and add to it
300 if (!this->activateNewPage(resourceProvider)) {
301 return ErrorCode::kError;
302 }
303
304 if (this->uploadToPage(fNumActivePages-1, id, target, width, height, image, loc)) {
305 return ErrorCode::kSucceeded;
306 } else {
307 // If we fail to upload to a newly activated page then something has gone terribly
308 // wrong - return an error
309 return ErrorCode::kError;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400310 }
311 }
312
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500313 if (!fNumActivePages) {
314 return ErrorCode::kError;
joshualitt5bf99f12015-03-13 11:47:42 -0700315 }
316
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400317 // Try to find a plot that we can perform an inline upload to.
318 // We prioritize this upload in reverse order of pages to counterbalance the order above.
319 Plot* plot = nullptr;
Robert Phillips6250f292018-03-01 10:53:45 -0500320 for (int pageIdx = ((int)fNumActivePages)-1; pageIdx >= 0; --pageIdx) {
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400321 Plot* currentPlot = fPages[pageIdx].fPlotList.tail();
Robert Phillips40a29d72018-01-18 12:59:22 -0500322 if (currentPlot->lastUseToken() != target->tokenTracker()->nextDrawToken()) {
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400323 plot = currentPlot;
324 break;
Robert Phillips256c37b2017-03-01 14:32:46 -0500325 }
joshualitt5bf99f12015-03-13 11:47:42 -0700326 }
327
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400328 // If we can't find a plot that is not used in a draw currently being prepared by an op, then
329 // we have to fail. This gives the op a chance to enqueue the draw, and call back into this
330 // function. When that draw is enqueued, the draw token advances, and the subsequent call will
331 // continue past this branch and prepare an inline upload that will occur after the enqueued
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500332 // draw which references the plot's pre-upload content.
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400333 if (!plot) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500334 return ErrorCode::kTryAgain;
joshualitt5bf99f12015-03-13 11:47:42 -0700335 }
336
joshualitt5bf99f12015-03-13 11:47:42 -0700337 this->processEviction(plot->id());
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400338 int pageIdx = GetPageIndexFromID(plot->id());
Jim Van Vertha950b632017-09-12 11:54:11 -0400339 fPages[pageIdx].fPlotList.remove(plot);
340 sk_sp<Plot>& newPlot = fPages[pageIdx].fPlotArray[plot->index()];
robertphillips2b0536f2015-11-06 14:10:42 -0800341 newPlot.reset(plot->clone());
joshualitt5bf99f12015-03-13 11:47:42 -0700342
Jim Van Vertha950b632017-09-12 11:54:11 -0400343 fPages[pageIdx].fPlotList.addToHead(newPlot.get());
344 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == newPlot->bpp());
robertphillips2b0536f2015-11-06 14:10:42 -0800345 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
joshualitt5bf99f12015-03-13 11:47:42 -0700346 SkASSERT(verify);
robertphillips2b0536f2015-11-06 14:10:42 -0800347
robertphillips1f0e3502015-11-10 10:19:50 -0800348 // Note that this plot will be uploaded inline with the draws whereas the
Brian Salomon29b60c92017-10-31 14:42:10 -0400349 // one it displaced most likely was uploaded ASAP.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500350 // With c+14 we could move sk_sp into lambda to only ref once.
351 sk_sp<Plot> plotsp(SkRef(newPlot.get()));
Robert Phillips4bc70112018-03-01 10:24:02 -0500352
Jim Van Vertha950b632017-09-12 11:54:11 -0400353 GrTextureProxy* proxy = fProxies[pageIdx].get();
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400354 SkASSERT(proxy->isInstantiated());
bsalomon342bfc22016-04-01 06:06:20 -0700355
Brian Salomon943ed792017-10-30 09:37:55 -0400356 GrDeferredUploadToken lastUploadToken = target->addInlineUpload(
357 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
358 plotsp->uploadToTexture(writePixels, proxy);
359 });
Robert Phillips256c37b2017-03-01 14:32:46 -0500360 newPlot->setLastUploadToken(lastUploadToken);
361
joshualitt5bf99f12015-03-13 11:47:42 -0700362 *id = newPlot->id();
robertphillips2b0536f2015-11-06 14:10:42 -0800363
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500364 return ErrorCode::kSucceeded;
joshualitt5bf99f12015-03-13 11:47:42 -0700365}
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400366
Brian Salomon943ed792017-10-30 09:37:55 -0400367void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500368 if (fNumActivePages <= 1) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400369 fPrevFlushToken = startTokenForNextFlush;
370 return;
371 }
372
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400373 // For all plots, reset number of flushes since used if used this frame.
Jim Van Verth106b5c42017-09-26 12:45:29 -0400374 PlotList::Iter plotIter;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400375 bool atlasUsedThisFlush = false;
Robert Phillips4bc70112018-03-01 10:24:02 -0500376 for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400377 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
378 while (Plot* plot = plotIter.get()) {
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400379 // Reset number of flushes since used
Jim Van Verth106b5c42017-09-26 12:45:29 -0400380 if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
381 plot->resetFlushesSinceLastUsed();
382 atlasUsedThisFlush = true;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400383 }
384
385 plotIter.next();
386 }
387 }
388
389 // We only try to compact if the atlas was used in the recently completed flush.
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400390 // This is to handle the case where a lot of text or path rendering has occurred but then just
391 // a blinking cursor is drawn.
Jim Van Verth106b5c42017-09-26 12:45:29 -0400392 // TODO: consider if we should also do this if it's been a long time since the last atlas use
393 if (atlasUsedThisFlush) {
Jim Van Verthcad0acf2018-02-16 18:41:41 -0500394 SkTArray<Plot*> availablePlots;
Robert Phillips4bc70112018-03-01 10:24:02 -0500395 uint32_t lastPageIndex = fNumActivePages - 1;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400396
397 // For all plots but the last one, update number of flushes since used, and check to see
398 // if there are any in the first pages that the last page can safely upload to.
399 for (uint32_t pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) {
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400400#ifdef DUMP_ATLAS_DATA
401 if (gDumpAtlasData) {
402 SkDebugf("page %d: ", pageIndex);
403 }
404#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400405 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
406 while (Plot* plot = plotIter.get()) {
407 // Update number of flushes since plot was last used
408 // We only increment the 'sinceLastUsed' count for flushes where the atlas was used
409 // to avoid deleting everything when we return to text drawing in the blinking
410 // cursor case
411 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
412 plot->incFlushesSinceLastUsed();
413 }
414
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400415#ifdef DUMP_ATLAS_DATA
416 if (gDumpAtlasData) {
417 SkDebugf("%d ", plot->flushesSinceLastUsed());
418 }
419#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400420 // Count plots we can potentially upload to in all pages except the last one
421 // (the potential compactee).
422 if (plot->flushesSinceLastUsed() > kRecentlyUsedCount) {
Jim Van Verthcad0acf2018-02-16 18:41:41 -0500423 availablePlots.push_back() = plot;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400424 }
425
426 plotIter.next();
427 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400428#ifdef DUMP_ATLAS_DATA
429 if (gDumpAtlasData) {
430 SkDebugf("\n");
431 }
432#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400433 }
434
Jim Van Verth06f593c2018-02-20 11:30:10 -0500435 // Count recently used plots in the last page and evict any that are no longer in use.
436 // Since we prioritize uploading to the first pages, this will eventually
Jim Van Verth106b5c42017-09-26 12:45:29 -0400437 // clear out usage of this page unless we have a large need.
438 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
Jim Van Verth06f593c2018-02-20 11:30:10 -0500439 unsigned int usedPlots = 0;
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400440#ifdef DUMP_ATLAS_DATA
441 if (gDumpAtlasData) {
442 SkDebugf("page %d: ", lastPageIndex);
443 }
444#endif
Jim Van Verth106b5c42017-09-26 12:45:29 -0400445 while (Plot* plot = plotIter.get()) {
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400446 // Update number of flushes since plot was last used
447 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
448 plot->incFlushesSinceLastUsed();
449 }
450
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400451#ifdef DUMP_ATLAS_DATA
452 if (gDumpAtlasData) {
453 SkDebugf("%d ", plot->flushesSinceLastUsed());
454 }
455#endif
Jim Van Verth106b5c42017-09-26 12:45:29 -0400456 // If this plot was used recently
457 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
458 usedPlots++;
Brian Salomon943ed792017-10-30 09:37:55 -0400459 } else if (plot->lastUseToken() != GrDeferredUploadToken::AlreadyFlushedToken()) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400460 // otherwise if aged out just evict it.
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400461 this->processEvictionAndResetRects(plot);
Jim Van Verth106b5c42017-09-26 12:45:29 -0400462 }
463 plotIter.next();
464 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400465#ifdef DUMP_ATLAS_DATA
466 if (gDumpAtlasData) {
467 SkDebugf("\n");
468 }
469#endif
Jim Van Verth06f593c2018-02-20 11:30:10 -0500470
471 // If recently used plots in the last page are using less than a quarter of the page, try
472 // to evict them if there's available space in earlier pages. Since we prioritize uploading
473 // to the first pages, this will eventually clear out usage of this page unless we have a
474 // large need.
475 if (availablePlots.count() && usedPlots && usedPlots <= fNumPlots / 4) {
476 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
477 while (Plot* plot = plotIter.get()) {
478 // If this plot was used recently
479 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
480 // See if there's room in an earlier page and if so evict.
481 // We need to be somewhat harsh here so that a handful of plots that are
482 // consistently in use don't end up locking the page in memory.
483 if (availablePlots.count() > 0) {
484 this->processEvictionAndResetRects(plot);
485 this->processEvictionAndResetRects(availablePlots.back());
486 availablePlots.pop_back();
487 --usedPlots;
488 }
489 if (!usedPlots || !availablePlots.count()) {
490 break;
491 }
492 }
493 plotIter.next();
494 }
495 }
496
Jim Van Verth106b5c42017-09-26 12:45:29 -0400497 // If none of the plots in the last page have been used recently, delete it.
498 if (!usedPlots) {
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400499#ifdef DUMP_ATLAS_DATA
500 if (gDumpAtlasData) {
501 SkDebugf("delete %d\n", fNumPages-1);
502 }
503#endif
Robert Phillips4bc70112018-03-01 10:24:02 -0500504 this->deactivateLastPage();
Jim Van Verth106b5c42017-09-26 12:45:29 -0400505 }
506 }
507
508 fPrevFlushToken = startTokenForNextFlush;
509}
510
Robert Phillips4bc70112018-03-01 10:24:02 -0500511bool GrDrawOpAtlas::createPages(GrProxyProvider* proxyProvider) {
512 SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500513
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400514 GrSurfaceDesc desc;
515 desc.fFlags = kNone_GrSurfaceFlags;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400516 desc.fWidth = fTextureWidth;
517 desc.fHeight = fTextureHeight;
518 desc.fConfig = fPixelConfig;
519
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400520 int numPlotsX = fTextureWidth/fPlotWidth;
521 int numPlotsY = fTextureHeight/fPlotHeight;
522
Robert Phillips4bc70112018-03-01 10:24:02 -0500523 for (uint32_t i = 0; i < this->maxPages(); ++i) {
Brian Salomon2a4f9832018-03-03 22:43:43 -0500524 fProxies[i] = proxyProvider->createProxy(desc, kTopLeft_GrSurfaceOrigin,
Robert Phillipsfe0253f2018-03-16 16:47:25 -0400525 SkBackingFit::kExact, SkBudgeted::kYes, GrInternalSurfaceFlags::kNoPendingIO);
Robert Phillips4bc70112018-03-01 10:24:02 -0500526 if (!fProxies[i]) {
527 return false;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400528 }
Robert Phillips4bc70112018-03-01 10:24:02 -0500529
530 // set up allocated plots
531 fPages[i].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
532
533 sk_sp<Plot>* currPlot = fPages[i].fPlotArray.get();
534 for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
535 for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
536 uint32_t plotIndex = r * numPlotsX + c;
537 currPlot->reset(new Plot(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
538 fPixelConfig));
539
540 // build LRU list
541 fPages[i].fPlotList.addToHead(currPlot->get());
542 ++currPlot;
543 }
544 }
545
546 }
547
548 return true;
549}
550
551
552bool GrDrawOpAtlas::activateNewPage(GrResourceProvider* resourceProvider) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500553 SkASSERT(fNumActivePages < this->maxPages());
Robert Phillips4bc70112018-03-01 10:24:02 -0500554
555 if (!fProxies[fNumActivePages]->instantiate(resourceProvider)) {
556 return false;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400557 }
558
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400559#ifdef DUMP_ATLAS_DATA
560 if (gDumpAtlasData) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500561 SkDebugf("activated page#: %d\n", fNumActivePages);
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400562 }
563#endif
Robert Phillips4bc70112018-03-01 10:24:02 -0500564
565 ++fNumActivePages;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400566 return true;
567}
Jim Van Verth106b5c42017-09-26 12:45:29 -0400568
Robert Phillips4bc70112018-03-01 10:24:02 -0500569
570inline void GrDrawOpAtlas::deactivateLastPage() {
571 SkASSERT(fNumActivePages);
572
573 uint32_t lastPageIndex = fNumActivePages - 1;
574
575 int numPlotsX = fTextureWidth/fPlotWidth;
576 int numPlotsY = fTextureHeight/fPlotHeight;
577
Jim Van Verth106b5c42017-09-26 12:45:29 -0400578 fPages[lastPageIndex].fPlotList.reset();
Robert Phillips6250f292018-03-01 10:53:45 -0500579 for (int r = 0; r < numPlotsY; ++r) {
580 for (int c = 0; c < numPlotsX; ++c) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500581 uint32_t plotIndex = r * numPlotsX + c;
582
583 Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
584 currPlot->resetRects();
585 currPlot->resetFlushesSinceLastUsed();
586
587 // rebuild the LRU list
588 SkDEBUGCODE(currPlot->fPrev = currPlot->fNext = nullptr);
589 SkDEBUGCODE(currPlot->fList = nullptr);
590 fPages[lastPageIndex].fPlotList.addToHead(currPlot);
591 }
592 }
593
594 // remove ref to the backing texture
595 fProxies[lastPageIndex]->deInstantiate();
596 --fNumActivePages;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400597}