blob: 214264730a5f612e0128ded8d22f21ef6d138c30 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/GrDrawOpAtlas.h"
Robert Phillips32f28182017-02-28 16:20:03 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/gpu/GrContext.h"
11#include "include/gpu/GrTexture.h"
12#include "src/gpu/GrContextPriv.h"
13#include "src/gpu/GrOnFlushResourceProvider.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProxyProvider.h"
16#include "src/gpu/GrRectanizer.h"
17#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrSurfaceProxyPriv.h"
19#include "src/gpu/GrTracing.h"
joshualitt5bf99f12015-03-13 11:47:42 -070020
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,
Greg Daniel4065d452018-11-16 15:43:41 -050037 const GrBackendFormat& format,
Robert Phillips42dda082019-05-14 13:29:45 -040038 GrColorType colorType, int width,
Jim Van Verthf6206f92018-12-14 08:22:24 -050039 int height, int plotWidth, int plotHeight,
Brian Salomon9f545bc2017-11-06 10:36:57 -050040 AllowMultitexturing allowMultitexturing,
41 GrDrawOpAtlas::EvictionFunc func, void* data) {
Robert Phillips42dda082019-05-14 13:29:45 -040042 std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, format, colorType, width,
Jim Van Verthf6206f92018-12-14 08:22:24 -050043 height, plotWidth, plotHeight,
Robert Phillips4bc70112018-03-01 10:24:02 -050044 allowMultitexturing));
Jim Van Vertha950b632017-09-12 11:54:11 -040045 if (!atlas->getProxies()[0]) {
Jim Van Verthd74f3f22017-08-31 16:44:08 -040046 return nullptr;
47 }
48
Robert Phillips256c37b2017-03-01 14:32:46 -050049 atlas->registerEvictionCallback(func, data);
50 return atlas;
51}
52
Jim Van Verthc3269ae2017-09-28 15:04:00 -040053#ifdef DUMP_ATLAS_DATA
54static bool gDumpAtlasData = false;
55#endif
Robert Phillips256c37b2017-03-01 14:32:46 -050056
joshualitt5df175e2015-11-18 13:37:54 -080057////////////////////////////////////////////////////////////////////////////////
Jim Van Vertha950b632017-09-12 11:54:11 -040058GrDrawOpAtlas::Plot::Plot(int pageIndex, int plotIndex, uint64_t genID, int offX, int offY,
Robert Phillips42dda082019-05-14 13:29:45 -040059 int width, int height, GrColorType colorType)
Brian Salomon943ed792017-10-30 09:37:55 -040060 : fLastUpload(GrDeferredUploadToken::AlreadyFlushedToken())
61 , fLastUse(GrDeferredUploadToken::AlreadyFlushedToken())
Jim Van Verth106b5c42017-09-26 12:45:29 -040062 , fFlushesSinceLastUse(0)
Jim Van Vertha950b632017-09-12 11:54:11 -040063 , fPageIndex(pageIndex)
64 , fPlotIndex(plotIndex)
Brian Salomon2ee084e2016-12-16 18:59:19 -050065 , fGenID(genID)
Jim Van Vertha950b632017-09-12 11:54:11 -040066 , fID(CreateId(fPageIndex, fPlotIndex, fGenID))
Brian Salomon2ee084e2016-12-16 18:59:19 -050067 , fData(nullptr)
68 , fWidth(width)
69 , fHeight(height)
70 , fX(offX)
71 , fY(offY)
72 , fRects(nullptr)
73 , fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
Robert Phillips42dda082019-05-14 13:29:45 -040074 , fColorType(colorType)
75 , fBytesPerPixel(GrColorTypeBytesPerPixel(colorType))
joshualitt5df175e2015-11-18 13:37:54 -080076#ifdef SK_DEBUG
Brian Salomon2ee084e2016-12-16 18:59:19 -050077 , fDirty(false)
joshualitt5df175e2015-11-18 13:37:54 -080078#endif
79{
Jim Van Vertha8c55fa2018-02-20 15:38:08 -050080 // We expect the allocated dimensions to be a multiple of 4 bytes
81 SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
82 // The padding for faster uploads only works for 1, 2 and 4 byte texels
83 SkASSERT(fBytesPerPixel != 3 && fBytesPerPixel <= 4);
joshualitt5df175e2015-11-18 13:37:54 -080084 fDirtyRect.setEmpty();
85}
joshualitt5bf99f12015-03-13 11:47:42 -070086
Brian Salomon2ee084e2016-12-16 18:59:19 -050087GrDrawOpAtlas::Plot::~Plot() {
jvanverthc3d706f2016-04-20 10:33:27 -070088 sk_free(fData);
joshualitt5df175e2015-11-18 13:37:54 -080089 delete fRects;
90}
joshualitt5bf99f12015-03-13 11:47:42 -070091
Brian Salomon2ee084e2016-12-16 18:59:19 -050092bool GrDrawOpAtlas::Plot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
joshualitt5df175e2015-11-18 13:37:54 -080093 SkASSERT(width <= fWidth && height <= fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070094
joshualitt5df175e2015-11-18 13:37:54 -080095 if (!fRects) {
96 fRects = GrRectanizer::Factory(fWidth, fHeight);
joshualitt5bf99f12015-03-13 11:47:42 -070097 }
98
joshualitt5df175e2015-11-18 13:37:54 -080099 if (!fRects->addRect(width, height, loc)) {
100 return false;
joshualittb4c507e2015-04-08 08:07:59 -0700101 }
joshualitt5bf99f12015-03-13 11:47:42 -0700102
jvanverthc3d706f2016-04-20 10:33:27 -0700103 if (!fData) {
104 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
105 fHeight));
joshualitt5df175e2015-11-18 13:37:54 -0800106 }
107 size_t rowBytes = width * fBytesPerPixel;
108 const unsigned char* imagePtr = (const unsigned char*)image;
109 // point ourselves at the right starting spot
jvanverthc3d706f2016-04-20 10:33:27 -0700110 unsigned char* dataPtr = fData;
joshualitt5df175e2015-11-18 13:37:54 -0800111 dataPtr += fBytesPerPixel * fWidth * loc->fY;
112 dataPtr += fBytesPerPixel * loc->fX;
Brian Osmancce3e582016-10-14 11:42:20 -0400113 // copy into the data buffer, swizzling as we go if this is ARGB data
114 if (4 == fBytesPerPixel && kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
115 for (int i = 0; i < height; ++i) {
Mike Klein6e78ae52018-09-19 13:37:16 -0400116 SkOpts::RGBA_to_BGRA((uint32_t*)dataPtr, (const uint32_t*)imagePtr, width);
Brian Osmancce3e582016-10-14 11:42:20 -0400117 dataPtr += fBytesPerPixel * fWidth;
118 imagePtr += rowBytes;
119 }
120 } else {
121 for (int i = 0; i < height; ++i) {
122 memcpy(dataPtr, imagePtr, rowBytes);
123 dataPtr += fBytesPerPixel * fWidth;
124 imagePtr += rowBytes;
125 }
joshualitt5bf99f12015-03-13 11:47:42 -0700126 }
127
joshualitt5df175e2015-11-18 13:37:54 -0800128 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
robertphillips2b0536f2015-11-06 14:10:42 -0800129
joshualitt5df175e2015-11-18 13:37:54 -0800130 loc->fX += fOffset.fX;
131 loc->fY += fOffset.fY;
132 SkDEBUGCODE(fDirty = true;)
joshualitt5bf99f12015-03-13 11:47:42 -0700133
joshualitt5df175e2015-11-18 13:37:54 -0800134 return true;
135}
joshualitt5bf99f12015-03-13 11:47:42 -0700136
Brian Salomon943ed792017-10-30 09:37:55 -0400137void GrDrawOpAtlas::Plot::uploadToTexture(GrDeferredTextureUploadWritePixelsFn& writePixels,
Robert Phillipsacaa6072017-07-28 10:54:53 -0400138 GrTextureProxy* proxy) {
joshualitt5df175e2015-11-18 13:37:54 -0800139 // We should only be issuing uploads if we are in fact dirty
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400140 SkASSERT(fDirty && fData && proxy && proxy->peekTexture());
Brian Osman39c08ac2017-07-26 09:36:09 -0400141 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
joshualitt5df175e2015-11-18 13:37:54 -0800142 size_t rowBytes = fBytesPerPixel * fWidth;
jvanverthc3d706f2016-04-20 10:33:27 -0700143 const unsigned char* dataPtr = fData;
Jim Van Vertha8c55fa2018-02-20 15:38:08 -0500144 // Clamp to 4-byte aligned boundaries
145 unsigned int clearBits = 0x3 / fBytesPerPixel;
146 fDirtyRect.fLeft &= ~clearBits;
147 fDirtyRect.fRight += clearBits;
148 fDirtyRect.fRight &= ~clearBits;
149 SkASSERT(fDirtyRect.fRight <= fWidth);
150 // Set up dataPtr
jvanverthc3d706f2016-04-20 10:33:27 -0700151 dataPtr += rowBytes * fDirtyRect.fTop;
152 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
Robert Phillips42dda082019-05-14 13:29:45 -0400153
Robert Phillipsacaa6072017-07-28 10:54:53 -0400154 writePixels(proxy, fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
Robert Phillips42dda082019-05-14 13:29:45 -0400155 fDirtyRect.width(), fDirtyRect.height(), fColorType, 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
Greg Daniel4065d452018-11-16 15:43:41 -0500181GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider, const GrBackendFormat& format,
Robert Phillips42dda082019-05-14 13:29:45 -0400182 GrColorType colorType, int width, int height,
Jim Van Verthf6206f92018-12-14 08:22:24 -0500183 int plotWidth, int plotHeight, AllowMultitexturing allowMultitexturing)
Greg Daniel4065d452018-11-16 15:43:41 -0500184 : fFormat(format)
Robert Phillips42dda082019-05-14 13:29:45 -0400185 , fColorType(colorType)
Jim Van Verthd74f3f22017-08-31 16:44:08 -0400186 , fTextureWidth(width)
187 , fTextureHeight(height)
Jim Van Verthf6206f92018-12-14 08:22:24 -0500188 , fPlotWidth(plotWidth)
189 , fPlotHeight(plotHeight)
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400190 , fAtlasGeneration(kInvalidAtlasGeneration + 1)
Brian Salomon943ed792017-10-30 09:37:55 -0400191 , fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500192 , fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ? kMaxMultitexturePages : 1)
Robert Phillips4bc70112018-03-01 10:24:02 -0500193 , fNumActivePages(0) {
Jim Van Verthf6206f92018-12-14 08:22:24 -0500194 int numPlotsX = width/plotWidth;
195 int numPlotsY = height/plotHeight;
Herb Derbybbf5fb52018-10-15 16:39:39 -0400196 SkASSERT(numPlotsX * numPlotsY <= GrDrawOpAtlas::kMaxPlots);
Jim Van Verthd74f3f22017-08-31 16:44:08 -0400197 SkASSERT(fPlotWidth * numPlotsX == fTextureWidth);
198 SkASSERT(fPlotHeight * numPlotsY == fTextureHeight);
robertphillips2b0536f2015-11-06 14:10:42 -0800199
Jim Van Verth06f593c2018-02-20 11:30:10 -0500200 fNumPlots = numPlotsX * numPlotsY;
joshualitt5bf99f12015-03-13 11:47:42 -0700201
Robert Phillips4bc70112018-03-01 10:24:02 -0500202 this->createPages(proxyProvider);
joshualitt5bf99f12015-03-13 11:47:42 -0700203}
204
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400205inline void GrDrawOpAtlas::processEviction(AtlasID id) {
joshualitt5bf99f12015-03-13 11:47:42 -0700206 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
207 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
208 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400209 ++fAtlasGeneration;
joshualitt5bf99f12015-03-13 11:47:42 -0700210}
211
Brian Salomon29b60c92017-10-31 14:42:10 -0400212inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* id, Plot* plot) {
Jim Van Vertha950b632017-09-12 11:54:11 -0400213 int pageIdx = GetPageIndexFromID(plot->id());
214 this->makeMRU(plot, pageIdx);
joshualitt5bf99f12015-03-13 11:47:42 -0700215
216 // If our most recent upload has already occurred then we have to insert a new
217 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
218 // This new update will piggy back on that previously scheduled update.
Robert Phillips40a29d72018-01-18 12:59:22 -0500219 if (plot->lastUploadToken() < target->tokenTracker()->nextTokenToFlush()) {
jvanverthc3d706f2016-04-20 10:33:27 -0700220 // With c+14 we could move sk_sp into lamba to only ref once.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500221 sk_sp<Plot> plotsp(SkRef(plot));
Robert Phillips256c37b2017-03-01 14:32:46 -0500222
Jim Van Vertha950b632017-09-12 11:54:11 -0400223 GrTextureProxy* proxy = fProxies[pageIdx].get();
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400224 SkASSERT(proxy->isInstantiated()); // This is occurring at flush time
Robert Phillips256c37b2017-03-01 14:32:46 -0500225
Brian Salomon29b60c92017-10-31 14:42:10 -0400226 GrDeferredUploadToken lastUploadToken = target->addASAPUpload(
Brian Salomon943ed792017-10-30 09:37:55 -0400227 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
228 plotsp->uploadToTexture(writePixels, proxy);
229 });
Robert Phillips256c37b2017-03-01 14:32:46 -0500230 plot->setLastUploadToken(lastUploadToken);
joshualitt5bf99f12015-03-13 11:47:42 -0700231 }
232 *id = plot->id();
Robert Phillips256c37b2017-03-01 14:32:46 -0500233 return true;
joshualitt5bf99f12015-03-13 11:47:42 -0700234}
235
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500236bool GrDrawOpAtlas::uploadToPage(unsigned int pageIdx, AtlasID* id, GrDeferredUploadTarget* target,
237 int width, int height, const void* image, SkIPoint16* loc) {
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400238 SkASSERT(fProxies[pageIdx] && fProxies[pageIdx]->isInstantiated());
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500239
240 // look through all allocated plots for one we can share, in Most Recently Refed order
241 PlotList::Iter plotIter;
242 plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
243
244 for (Plot* plot = plotIter.get(); plot; plot = plotIter.next()) {
245 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
246
247 if (plot->addSubImage(width, height, image, loc)) {
248 return this->updatePlot(target, id, plot);
249 }
250 }
251
252 return false;
253}
254
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400255// Number of atlas-related flushes beyond which we consider a plot to no longer be in use.
256//
257// This value is somewhat arbitrary -- the idea is to keep it low enough that
258// a page with unused plots will get removed reasonably quickly, but allow it
259// to hang around for a bit in case it's needed. The assumption is that flushes
260// are rare; i.e., we are not continually refreshing the frame.
Derek Sollenberger90196cc2017-10-09 15:00:33 -0400261static constexpr auto kRecentlyUsedCount = 256;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400262
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500263GrDrawOpAtlas::ErrorCode GrDrawOpAtlas::addToAtlas(GrResourceProvider* resourceProvider,
264 AtlasID* id, GrDeferredUploadTarget* target,
265 int width, int height,
266 const void* image, SkIPoint16* loc) {
bsalomon6d6b6ad2016-07-13 14:45:28 -0700267 if (width > fPlotWidth || height > fPlotHeight) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500268 return ErrorCode::kError;
bsalomon6d6b6ad2016-07-13 14:45:28 -0700269 }
joshualitt5bf99f12015-03-13 11:47:42 -0700270
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400271 // Look through each page to see if we can upload without having to flush
272 // We prioritize this upload to the first pages, not the most recently used, to make it easier
273 // to remove unused pages in reverse page order.
Robert Phillips4bc70112018-03-01 10:24:02 -0500274 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500275 if (this->uploadToPage(pageIdx, id, target, width, height, image, loc)) {
276 return ErrorCode::kSucceeded;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400277 }
Jim Van Verth712fe732017-09-25 16:53:49 -0400278 }
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400279
Jim Van Verth712fe732017-09-25 16:53:49 -0400280 // 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 -0400281 // flushed to the gpu if we're at max page allocation, or if the plot has aged out otherwise.
282 // We wait until we've grown to the full number of pages to begin evicting already flushed
283 // plots so that we can maximize the opportunity for reuse.
Jim Van Verth712fe732017-09-25 16:53:49 -0400284 // As before we prioritize this upload to the first pages, not the most recently used.
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500285 if (fNumActivePages == this->maxPages()) {
286 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
287 Plot* plot = fPages[pageIdx].fPlotList.tail();
288 SkASSERT(plot);
Jim Van Verthba98b7d2018-12-05 12:33:43 -0500289 if (plot->lastUseToken() < target->tokenTracker()->nextTokenToFlush()) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500290 this->processEvictionAndResetRects(plot);
291 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
292 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
293 SkASSERT(verify);
294 if (!this->updatePlot(target, id, plot)) {
295 return ErrorCode::kError;
296 }
297 return ErrorCode::kSucceeded;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400298 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500299 }
300 } else {
301 // If we haven't activated all the available pages, try to create a new one and add to it
302 if (!this->activateNewPage(resourceProvider)) {
303 return ErrorCode::kError;
304 }
305
306 if (this->uploadToPage(fNumActivePages-1, id, target, width, height, image, loc)) {
307 return ErrorCode::kSucceeded;
308 } else {
309 // If we fail to upload to a newly activated page then something has gone terribly
310 // wrong - return an error
311 return ErrorCode::kError;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400312 }
313 }
314
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500315 if (!fNumActivePages) {
316 return ErrorCode::kError;
joshualitt5bf99f12015-03-13 11:47:42 -0700317 }
318
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400319 // Try to find a plot that we can perform an inline upload to.
320 // We prioritize this upload in reverse order of pages to counterbalance the order above.
321 Plot* plot = nullptr;
Robert Phillips6250f292018-03-01 10:53:45 -0500322 for (int pageIdx = ((int)fNumActivePages)-1; pageIdx >= 0; --pageIdx) {
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400323 Plot* currentPlot = fPages[pageIdx].fPlotList.tail();
Robert Phillips40a29d72018-01-18 12:59:22 -0500324 if (currentPlot->lastUseToken() != target->tokenTracker()->nextDrawToken()) {
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400325 plot = currentPlot;
326 break;
Robert Phillips256c37b2017-03-01 14:32:46 -0500327 }
joshualitt5bf99f12015-03-13 11:47:42 -0700328 }
329
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400330 // If we can't find a plot that is not used in a draw currently being prepared by an op, then
331 // we have to fail. This gives the op a chance to enqueue the draw, and call back into this
332 // function. When that draw is enqueued, the draw token advances, and the subsequent call will
333 // continue past this branch and prepare an inline upload that will occur after the enqueued
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500334 // draw which references the plot's pre-upload content.
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400335 if (!plot) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500336 return ErrorCode::kTryAgain;
joshualitt5bf99f12015-03-13 11:47:42 -0700337 }
338
joshualitt5bf99f12015-03-13 11:47:42 -0700339 this->processEviction(plot->id());
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400340 int pageIdx = GetPageIndexFromID(plot->id());
Jim Van Vertha950b632017-09-12 11:54:11 -0400341 fPages[pageIdx].fPlotList.remove(plot);
342 sk_sp<Plot>& newPlot = fPages[pageIdx].fPlotArray[plot->index()];
robertphillips2b0536f2015-11-06 14:10:42 -0800343 newPlot.reset(plot->clone());
joshualitt5bf99f12015-03-13 11:47:42 -0700344
Jim Van Vertha950b632017-09-12 11:54:11 -0400345 fPages[pageIdx].fPlotList.addToHead(newPlot.get());
346 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == newPlot->bpp());
robertphillips2b0536f2015-11-06 14:10:42 -0800347 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
joshualitt5bf99f12015-03-13 11:47:42 -0700348 SkASSERT(verify);
robertphillips2b0536f2015-11-06 14:10:42 -0800349
robertphillips1f0e3502015-11-10 10:19:50 -0800350 // Note that this plot will be uploaded inline with the draws whereas the
Brian Salomon29b60c92017-10-31 14:42:10 -0400351 // one it displaced most likely was uploaded ASAP.
Brian Salomon2ee084e2016-12-16 18:59:19 -0500352 // With c+14 we could move sk_sp into lambda to only ref once.
353 sk_sp<Plot> plotsp(SkRef(newPlot.get()));
Robert Phillips4bc70112018-03-01 10:24:02 -0500354
Jim Van Vertha950b632017-09-12 11:54:11 -0400355 GrTextureProxy* proxy = fProxies[pageIdx].get();
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400356 SkASSERT(proxy->isInstantiated());
bsalomon342bfc22016-04-01 06:06:20 -0700357
Brian Salomon943ed792017-10-30 09:37:55 -0400358 GrDeferredUploadToken lastUploadToken = target->addInlineUpload(
359 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
360 plotsp->uploadToTexture(writePixels, proxy);
361 });
Robert Phillips256c37b2017-03-01 14:32:46 -0500362 newPlot->setLastUploadToken(lastUploadToken);
363
joshualitt5bf99f12015-03-13 11:47:42 -0700364 *id = newPlot->id();
robertphillips2b0536f2015-11-06 14:10:42 -0800365
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500366 return ErrorCode::kSucceeded;
joshualitt5bf99f12015-03-13 11:47:42 -0700367}
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400368
Brian Salomon943ed792017-10-30 09:37:55 -0400369void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500370 if (fNumActivePages <= 1) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400371 fPrevFlushToken = startTokenForNextFlush;
372 return;
373 }
374
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400375 // For all plots, reset number of flushes since used if used this frame.
Jim Van Verth106b5c42017-09-26 12:45:29 -0400376 PlotList::Iter plotIter;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400377 bool atlasUsedThisFlush = false;
Robert Phillips4bc70112018-03-01 10:24:02 -0500378 for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400379 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
380 while (Plot* plot = plotIter.get()) {
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400381 // Reset number of flushes since used
Jim Van Verth106b5c42017-09-26 12:45:29 -0400382 if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
383 plot->resetFlushesSinceLastUsed();
384 atlasUsedThisFlush = true;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400385 }
386
387 plotIter.next();
388 }
389 }
390
391 // We only try to compact if the atlas was used in the recently completed flush.
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400392 // This is to handle the case where a lot of text or path rendering has occurred but then just
393 // a blinking cursor is drawn.
Jim Van Verth106b5c42017-09-26 12:45:29 -0400394 // TODO: consider if we should also do this if it's been a long time since the last atlas use
395 if (atlasUsedThisFlush) {
Jim Van Verthcad0acf2018-02-16 18:41:41 -0500396 SkTArray<Plot*> availablePlots;
Robert Phillips4bc70112018-03-01 10:24:02 -0500397 uint32_t lastPageIndex = fNumActivePages - 1;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400398
399 // For all plots but the last one, update number of flushes since used, and check to see
400 // if there are any in the first pages that the last page can safely upload to.
401 for (uint32_t pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) {
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400402#ifdef DUMP_ATLAS_DATA
403 if (gDumpAtlasData) {
404 SkDebugf("page %d: ", pageIndex);
405 }
406#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400407 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
408 while (Plot* plot = plotIter.get()) {
409 // Update number of flushes since plot was last used
410 // We only increment the 'sinceLastUsed' count for flushes where the atlas was used
411 // to avoid deleting everything when we return to text drawing in the blinking
412 // cursor case
413 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
414 plot->incFlushesSinceLastUsed();
415 }
416
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400417#ifdef DUMP_ATLAS_DATA
418 if (gDumpAtlasData) {
419 SkDebugf("%d ", plot->flushesSinceLastUsed());
420 }
421#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400422 // Count plots we can potentially upload to in all pages except the last one
423 // (the potential compactee).
424 if (plot->flushesSinceLastUsed() > kRecentlyUsedCount) {
Jim Van Verthcad0acf2018-02-16 18:41:41 -0500425 availablePlots.push_back() = plot;
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400426 }
427
428 plotIter.next();
429 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400430#ifdef DUMP_ATLAS_DATA
431 if (gDumpAtlasData) {
432 SkDebugf("\n");
433 }
434#endif
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400435 }
436
Jim Van Verth06f593c2018-02-20 11:30:10 -0500437 // Count recently used plots in the last page and evict any that are no longer in use.
438 // Since we prioritize uploading to the first pages, this will eventually
Jim Van Verth106b5c42017-09-26 12:45:29 -0400439 // clear out usage of this page unless we have a large need.
440 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
Jim Van Verth06f593c2018-02-20 11:30:10 -0500441 unsigned int usedPlots = 0;
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400442#ifdef DUMP_ATLAS_DATA
443 if (gDumpAtlasData) {
444 SkDebugf("page %d: ", lastPageIndex);
445 }
446#endif
Jim Van Verth106b5c42017-09-26 12:45:29 -0400447 while (Plot* plot = plotIter.get()) {
Jim Van Verth62ea0cd2017-09-27 12:59:45 -0400448 // Update number of flushes since plot was last used
449 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
450 plot->incFlushesSinceLastUsed();
451 }
452
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400453#ifdef DUMP_ATLAS_DATA
454 if (gDumpAtlasData) {
455 SkDebugf("%d ", plot->flushesSinceLastUsed());
456 }
457#endif
Jim Van Verth106b5c42017-09-26 12:45:29 -0400458 // If this plot was used recently
459 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
460 usedPlots++;
Brian Salomon943ed792017-10-30 09:37:55 -0400461 } else if (plot->lastUseToken() != GrDeferredUploadToken::AlreadyFlushedToken()) {
Jim Van Verth106b5c42017-09-26 12:45:29 -0400462 // otherwise if aged out just evict it.
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400463 this->processEvictionAndResetRects(plot);
Jim Van Verth106b5c42017-09-26 12:45:29 -0400464 }
465 plotIter.next();
466 }
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400467#ifdef DUMP_ATLAS_DATA
468 if (gDumpAtlasData) {
469 SkDebugf("\n");
470 }
471#endif
Jim Van Verth06f593c2018-02-20 11:30:10 -0500472
473 // If recently used plots in the last page are using less than a quarter of the page, try
474 // to evict them if there's available space in earlier pages. Since we prioritize uploading
475 // to the first pages, this will eventually clear out usage of this page unless we have a
476 // large need.
477 if (availablePlots.count() && usedPlots && usedPlots <= fNumPlots / 4) {
478 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
479 while (Plot* plot = plotIter.get()) {
480 // If this plot was used recently
481 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
482 // See if there's room in an earlier page and if so evict.
483 // We need to be somewhat harsh here so that a handful of plots that are
484 // consistently in use don't end up locking the page in memory.
485 if (availablePlots.count() > 0) {
486 this->processEvictionAndResetRects(plot);
487 this->processEvictionAndResetRects(availablePlots.back());
488 availablePlots.pop_back();
489 --usedPlots;
490 }
491 if (!usedPlots || !availablePlots.count()) {
492 break;
493 }
494 }
495 plotIter.next();
496 }
497 }
498
Jim Van Verth106b5c42017-09-26 12:45:29 -0400499 // If none of the plots in the last page have been used recently, delete it.
500 if (!usedPlots) {
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400501#ifdef DUMP_ATLAS_DATA
502 if (gDumpAtlasData) {
503 SkDebugf("delete %d\n", fNumPages-1);
504 }
505#endif
Robert Phillips4bc70112018-03-01 10:24:02 -0500506 this->deactivateLastPage();
Jim Van Verth106b5c42017-09-26 12:45:29 -0400507 }
508 }
509
510 fPrevFlushToken = startTokenForNextFlush;
511}
512
Robert Phillips4bc70112018-03-01 10:24:02 -0500513bool GrDrawOpAtlas::createPages(GrProxyProvider* proxyProvider) {
514 SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
Robert Phillips0bd24dc2018-01-16 08:06:32 -0500515
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400516 GrSurfaceDesc desc;
Kevin Lubickf58e49f2019-04-08 12:14:21 -0400517 if (proxyProvider->caps()->shouldInitializeTextures()) {
518 // The atlas isn't guaranteed to touch all its pixels so, for platforms that benefit
519 // from complete initialization, clear everything.
520 desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
521 } else {
522 desc.fFlags = kNone_GrSurfaceFlags;
523 }
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400524 desc.fWidth = fTextureWidth;
525 desc.fHeight = fTextureHeight;
Greg Daniele877dce2019-07-11 10:52:43 -0400526 desc.fConfig = GrColorTypeToPixelConfig(fColorType);
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400527
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400528 int numPlotsX = fTextureWidth/fPlotWidth;
529 int numPlotsY = fTextureHeight/fPlotHeight;
530
Robert Phillips4bc70112018-03-01 10:24:02 -0500531 for (uint32_t i = 0; i < this->maxPages(); ++i) {
Greg Daniel4065d452018-11-16 15:43:41 -0500532 fProxies[i] = proxyProvider->createProxy(fFormat, desc, kTopLeft_GrSurfaceOrigin,
Robert Phillips10d17212019-04-24 14:09:10 -0400533 SkBackingFit::kExact, SkBudgeted::kYes);
Robert Phillips4bc70112018-03-01 10:24:02 -0500534 if (!fProxies[i]) {
535 return false;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400536 }
Robert Phillips4bc70112018-03-01 10:24:02 -0500537
Robert Phillips5f78adf2019-04-22 12:41:39 -0400538 fProxies[i]->priv().setIgnoredByResourceAllocator();
539
Robert Phillips4bc70112018-03-01 10:24:02 -0500540 // set up allocated plots
541 fPages[i].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
542
543 sk_sp<Plot>* currPlot = fPages[i].fPlotArray.get();
544 for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
545 for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
546 uint32_t plotIndex = r * numPlotsX + c;
547 currPlot->reset(new Plot(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
Robert Phillips42dda082019-05-14 13:29:45 -0400548 fColorType));
Robert Phillips4bc70112018-03-01 10:24:02 -0500549
550 // build LRU list
551 fPages[i].fPlotList.addToHead(currPlot->get());
552 ++currPlot;
553 }
554 }
555
556 }
557
558 return true;
559}
560
561
562bool GrDrawOpAtlas::activateNewPage(GrResourceProvider* resourceProvider) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500563 SkASSERT(fNumActivePages < this->maxPages());
Robert Phillips4bc70112018-03-01 10:24:02 -0500564
565 if (!fProxies[fNumActivePages]->instantiate(resourceProvider)) {
566 return false;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400567 }
568
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400569#ifdef DUMP_ATLAS_DATA
570 if (gDumpAtlasData) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500571 SkDebugf("activated page#: %d\n", fNumActivePages);
Jim Van Verthc3269ae2017-09-28 15:04:00 -0400572 }
573#endif
Robert Phillips4bc70112018-03-01 10:24:02 -0500574
575 ++fNumActivePages;
Jim Van Vertheafa64b2017-09-18 10:05:00 -0400576 return true;
577}
Jim Van Verth106b5c42017-09-26 12:45:29 -0400578
Robert Phillips4bc70112018-03-01 10:24:02 -0500579
580inline void GrDrawOpAtlas::deactivateLastPage() {
581 SkASSERT(fNumActivePages);
582
583 uint32_t lastPageIndex = fNumActivePages - 1;
584
585 int numPlotsX = fTextureWidth/fPlotWidth;
586 int numPlotsY = fTextureHeight/fPlotHeight;
587
Jim Van Verth106b5c42017-09-26 12:45:29 -0400588 fPages[lastPageIndex].fPlotList.reset();
Robert Phillips6250f292018-03-01 10:53:45 -0500589 for (int r = 0; r < numPlotsY; ++r) {
590 for (int c = 0; c < numPlotsX; ++c) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500591 uint32_t plotIndex = r * numPlotsX + c;
592
593 Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
594 currPlot->resetRects();
595 currPlot->resetFlushesSinceLastUsed();
596
597 // rebuild the LRU list
598 SkDEBUGCODE(currPlot->fPrev = currPlot->fNext = nullptr);
599 SkDEBUGCODE(currPlot->fList = nullptr);
600 fPages[lastPageIndex].fPlotList.addToHead(currPlot);
601 }
602 }
603
604 // remove ref to the backing texture
Brian Salomon967df202018-12-07 11:15:53 -0500605 fProxies[lastPageIndex]->deinstantiate();
Robert Phillips4bc70112018-03-01 10:24:02 -0500606 --fNumActivePages;
Jim Van Verth106b5c42017-09-26 12:45:29 -0400607}
Herb Derby15d9ef22018-10-18 13:41:32 -0400608
Jim Van Verthf6206f92018-12-14 08:22:24 -0500609GrDrawOpAtlasConfig::GrDrawOpAtlasConfig(int maxTextureSize, size_t maxBytes) {
610 static const SkISize kARGBDimensions[] = {
611 {256, 256}, // maxBytes < 2^19
612 {512, 256}, // 2^19 <= maxBytes < 2^20
613 {512, 512}, // 2^20 <= maxBytes < 2^21
614 {1024, 512}, // 2^21 <= maxBytes < 2^22
615 {1024, 1024}, // 2^22 <= maxBytes < 2^23
616 {2048, 1024}, // 2^23 <= maxBytes
617 };
Herb Derby15d9ef22018-10-18 13:41:32 -0400618
Jim Van Verthf6206f92018-12-14 08:22:24 -0500619 // Index 0 corresponds to maxBytes of 2^18, so start by dividing it by that
620 maxBytes >>= 18;
621 // Take the floor of the log to get the index
622 int index = maxBytes > 0
623 ? SkTPin<int>(SkPrevLog2(maxBytes), 0, SK_ARRAY_COUNT(kARGBDimensions) - 1)
624 : 0;
Herb Derby15d9ef22018-10-18 13:41:32 -0400625
Jim Van Verthf6206f92018-12-14 08:22:24 -0500626 SkASSERT(kARGBDimensions[index].width() <= kMaxAtlasDim);
627 SkASSERT(kARGBDimensions[index].height() <= kMaxAtlasDim);
628 fARGBDimensions.set(SkTMin<int>(kARGBDimensions[index].width(), maxTextureSize),
629 SkTMin<int>(kARGBDimensions[index].height(), maxTextureSize));
630 fMaxTextureSize = SkTMin<int>(maxTextureSize, kMaxAtlasDim);
Herb Derby15d9ef22018-10-18 13:41:32 -0400631}
632
633SkISize GrDrawOpAtlasConfig::atlasDimensions(GrMaskFormat type) const {
Jim Van Verthf6206f92018-12-14 08:22:24 -0500634 if (kA8_GrMaskFormat == type) {
635 // A8 is always 2x the ARGB dimensions, clamped to the max allowed texture size
636 return { SkTMin<int>(2 * fARGBDimensions.width(), fMaxTextureSize),
637 SkTMin<int>(2 * fARGBDimensions.height(), fMaxTextureSize) };
638 } else {
639 return fARGBDimensions;
640 }
Herb Derby15d9ef22018-10-18 13:41:32 -0400641}
642
Jim Van Verthf6206f92018-12-14 08:22:24 -0500643SkISize GrDrawOpAtlasConfig::plotDimensions(GrMaskFormat type) const {
644 if (kA8_GrMaskFormat == type) {
645 SkISize atlasDimensions = this->atlasDimensions(type);
646 // For A8 we want to grow the plots at larger texture sizes to accept more of the
647 // larger SDF glyphs. Since the largest SDF glyph can be 170x170 with padding, this
648 // allows us to pack 3 in a 512x256 plot, or 9 in a 512x512 plot.
Herb Derby15d9ef22018-10-18 13:41:32 -0400649
Jim Van Verth578b0892018-12-20 20:48:55 +0000650 // This will give us 512x256 plots for 2048x1024, 512x512 plots for 2048x2048,
651 // and 256x256 plots otherwise.
Jim Van Verthf6206f92018-12-14 08:22:24 -0500652 int plotWidth = atlasDimensions.width() >= 2048 ? 512 : 256;
Jim Van Verth578b0892018-12-20 20:48:55 +0000653 int plotHeight = atlasDimensions.height() >= 2048 ? 512 : 256;
Herb Derby15d9ef22018-10-18 13:41:32 -0400654
Jim Van Verthf6206f92018-12-14 08:22:24 -0500655 return { plotWidth, plotHeight };
656 } else {
657 // ARGB and LCD always use 256x256 plots -- this has been shown to be faster
658 return { 256, 256 };
659 }
Herb Derby15d9ef22018-10-18 13:41:32 -0400660}
661
Jim Van Verthf6206f92018-12-14 08:22:24 -0500662constexpr int GrDrawOpAtlasConfig::kMaxAtlasDim;