1 /* 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 #ifndef GrDrawOpAtlas_DEFINED 9 #define GrDrawOpAtlas_DEFINED 10 11 #include <cmath> 12 #include <vector> 13 14 #include "include/gpu/GrBackendSurface.h" 15 #include "include/private/SkTArray.h" 16 #include "src/core/SkIPoint16.h" 17 #include "src/core/SkTInternalLList.h" 18 #include "src/gpu/GrDeferredUpload.h" 19 #include "src/gpu/GrRectanizerSkyline.h" 20 #include "src/gpu/GrSurfaceProxyView.h" 21 #include "src/gpu/geometry/GrRect.h" 22 23 class GrOnFlushResourceProvider; 24 class GrProxyProvider; 25 class GrResourceProvider; 26 class GrTextureProxy; 27 28 /** 29 * This class manages one or more atlas textures on behalf of GrDrawOps. The draw ops that use the 30 * atlas perform texture uploads when preparing their draws during flush. The class provides 31 * facilities for using GrDrawOpUploadToken to detect data hazards. Op's uploads are performed in 32 * "ASAP" mode until it is impossible to add data without overwriting texels read by draws that 33 * have not yet executed on the gpu. At that point, the atlas will attempt to allocate a new 34 * atlas texture (or "page") of the same size, up to a maximum number of textures, and upload 35 * to that texture. If that's not possible, the uploads are performed "inline" between draws. If a 36 * single draw would use enough subimage space to overflow the atlas texture then the atlas will 37 * fail to add a subimage. This gives the op the chance to end the draw and begin a new one. 38 * Additional uploads will then succeed in inline mode. 39 * 40 * When the atlas has multiple pages, new uploads are prioritized to the lower index pages, i.e., 41 * it will try to upload to page 0 before page 1 or 2. To keep the atlas from continually using 42 * excess space, periodic garbage collection is needed to shift data from the higher index pages to 43 * the lower ones, and then eventually remove any pages that are no longer in use. "In use" is 44 * determined by using the GrDrawUploadToken system: After a flush each subarea of the page 45 * is checked to see whether it was used in that flush; if it is not, a counter is incremented. 46 * Once that counter reaches a threshold that subarea is considered to be no longer in use. 47 * 48 * Garbage collection is initiated by the GrDrawOpAtlas's client via the compact() method. One 49 * solution is to make the client a subclass of GrOnFlushCallbackObject, register it with the 50 * GrContext via addOnFlushCallbackObject(), and the client's postFlush() method calls compact() 51 * and passes in the given GrDrawUploadToken. 52 */ 53 class GrDrawOpAtlas { 54 public: 55 /** Is the atlas allowed to use more than one texture? */ 56 enum class AllowMultitexturing : bool { kNo, kYes }; 57 58 // These are both restricted by the space they occupy in the PlotLocator. 59 // maxPages is also limited by being crammed into the glyph uvs. 60 // maxPlots is also limited by the fPlotAlreadyUpdated bitfield in BulkUseTokenUpdater 61 #ifdef SK_ENABLE_SMALL_PAGE 62 inline static constexpr auto kMaxMultitexturePages = 16; 63 #else 64 inline static constexpr auto kMaxMultitexturePages = 4; 65 #endif 66 inline static constexpr int kMaxPlots = 32; 67 68 /** 69 * A PlotLocator specifies the plot and is analogous to a directory path: 70 * page/plot/plotGeneration 71 * 72 * In fact PlotLocator is a portion of a glyph image location in the atlas fully specified by: 73 * format/atlasGeneration/page/plot/plotGeneration/rect 74 * 75 * TODO: Remove the small path renderer's use of the PlotLocator for eviction. 76 */ 77 class PlotLocator { 78 public: PlotLocator(uint32_t pageIdx,uint32_t plotIdx,uint64_t generation)79 PlotLocator(uint32_t pageIdx, uint32_t plotIdx, uint64_t generation) 80 : fGenID(generation) 81 , fPlotIndex(plotIdx) 82 , fPageIndex(pageIdx) { 83 SkASSERT(pageIdx < kMaxMultitexturePages); 84 SkASSERT(plotIdx < kMaxPlots); 85 SkASSERT(generation < ((uint64_t)1 << 48)); 86 } 87 PlotLocator()88 PlotLocator() : fGenID(0), fPlotIndex(0), fPageIndex(0) {} 89 isValid()90 bool isValid() const { 91 return fGenID != 0 || fPlotIndex != 0 || fPageIndex != 0; 92 } 93 makeInvalid()94 void makeInvalid() { 95 fGenID = 0; 96 fPlotIndex = 0; 97 fPageIndex = 0; 98 } 99 100 bool operator==(const PlotLocator& other) const { 101 return fGenID == other.fGenID && 102 fPlotIndex == other.fPlotIndex && 103 fPageIndex == other.fPageIndex; } 104 pageIndex()105 uint32_t pageIndex() const { return fPageIndex; } plotIndex()106 uint32_t plotIndex() const { return fPlotIndex; } genID()107 uint64_t genID() const { return fGenID; } 108 109 private: 110 uint64_t fGenID:48; 111 uint64_t fPlotIndex:8; 112 uint64_t fPageIndex:8; 113 }; 114 115 static const uint64_t kInvalidAtlasGeneration = 0; 116 117 118 // AtlasLocator handles atlas position information. It keeps a left-top, right-bottom pair of 119 // encoded UV coordinates. The bits 13 & 14 of the U coordinates hold the atlas page index. 120 // This information is handed directly as is from fUVs. This encoding has the nice property 121 // that width = fUVs[2] - fUVs[0]; the page encoding in the top bits subtracts to zero. 122 class AtlasLocator { 123 public: getUVs()124 std::array<uint16_t, 4> getUVs() const { 125 return fUVs; 126 } 127 invalidatePlotLocator()128 void invalidatePlotLocator() { fPlotLocator.makeInvalid(); } 129 130 // TODO: Remove the small path renderer's use of this for eviction plotLocator()131 PlotLocator plotLocator() const { return fPlotLocator; } 132 pageIndex()133 uint32_t pageIndex() const { return fPlotLocator.pageIndex(); } 134 plotIndex()135 uint32_t plotIndex() const { return fPlotLocator.plotIndex(); } 136 genID()137 uint64_t genID() const { return fPlotLocator.genID(); } 138 topLeft()139 SkIPoint topLeft() const { 140 #ifdef SK_ENABLE_SMALL_PAGE 141 return {fUVs[0] & 0x3FFF, fUVs[1] & 0x3FFF}; 142 #else 143 return {fUVs[0] & 0x1FFF, fUVs[1]}; 144 #endif 145 } 146 width()147 uint16_t width() const { 148 return fUVs[2] - fUVs[0]; 149 } 150 height()151 uint16_t height() const { 152 return fUVs[3] - fUVs[1]; 153 } 154 insetSrc(int padding)155 void insetSrc(int padding) { 156 SkASSERT(2 * padding <= this->width()); 157 SkASSERT(2 * padding <= this->height()); 158 159 fUVs[0] += padding; 160 fUVs[1] += padding; 161 fUVs[2] -= padding; 162 fUVs[3] -= padding; 163 } 164 updatePlotLocator(PlotLocator p)165 void updatePlotLocator(PlotLocator p) { 166 fPlotLocator = p; 167 #ifdef SK_ENABLE_SMALL_PAGE 168 SkASSERT(fPlotLocator.pageIndex() <= 15); 169 // package pageIndex(max value is 16, 4bit) 170 // high 2bit save in x-axis(fUVs[0],fUVs[2]),low 2bit save in y-axis(fUVs[1],fUVs[3]) 171 uint16_t page = fPlotLocator.pageIndex() << 12; 172 fUVs[0] = (fUVs[0] & 0x3FFF) | (page & 0xC000); 173 fUVs[1] = (fUVs[1] & 0x3FFF) | ((page << 2) & 0xC000); 174 fUVs[2] = (fUVs[2] & 0x3FFF) | (page & 0xC000); 175 fUVs[3] = (fUVs[3] & 0x3FFF) | ((page << 2) & 0xC000); 176 #else 177 SkASSERT(fPlotLocator.pageIndex() <= 3); 178 uint16_t page = fPlotLocator.pageIndex() << 13; 179 fUVs[0] = (fUVs[0] & 0x1FFF) | page; 180 fUVs[2] = (fUVs[2] & 0x1FFF) | page; 181 #endif 182 } 183 updateRect(GrIRect16 rect)184 void updateRect(GrIRect16 rect) { 185 #ifdef SK_ENABLE_SMALL_PAGE 186 SkASSERT(rect.fLeft <= rect.fRight); 187 SkASSERT(rect.fRight <= 0x3FFF); 188 SkASSERT(rect.fTop <= rect.fBottom); 189 SkASSERT(rect.fBottom <= 0x3FFF); 190 fUVs[0] = (fUVs[0] & 0xC000) | rect.fLeft; 191 fUVs[1] = (fUVs[1] & 0xC000) | rect.fTop; 192 fUVs[2] = (fUVs[2] & 0xC000) | rect.fRight; 193 fUVs[3] = (fUVs[3] & 0xC000) | rect.fBottom; 194 #else 195 SkASSERT(rect.fLeft <= rect.fRight); 196 SkASSERT(rect.fRight <= 0x1FFF); 197 fUVs[0] = (fUVs[0] & 0xE000) | rect.fLeft; 198 fUVs[1] = rect.fTop; 199 fUVs[2] = (fUVs[2] & 0xE000) | rect.fRight; 200 fUVs[3] = rect.fBottom; 201 #endif 202 } 203 204 private: 205 PlotLocator fPlotLocator{0, 0, 0}; 206 207 // The inset padded bounds in the atlas in the lower 13 bits, and page index in bits 13 & 208 // 14 of the Us. 209 std::array<uint16_t, 4> fUVs{0, 0, 0, 0}; 210 }; 211 212 /** 213 * An interface for eviction callbacks. Whenever GrDrawOpAtlas evicts a 214 * specific PlotLocator, it will call all of the registered listeners so they can process the 215 * eviction. 216 */ 217 class EvictionCallback { 218 public: 219 virtual ~EvictionCallback() = default; 220 virtual void evict(PlotLocator) = 0; 221 }; 222 223 /** 224 * Keep track of generation number for Atlases and Plots. 225 */ 226 class GenerationCounter { 227 public: 228 inline static constexpr uint64_t kInvalidGeneration = 0; next()229 uint64_t next() { 230 return fGeneration++; 231 } 232 233 private: 234 uint64_t fGeneration{1}; 235 }; 236 237 /** 238 * Returns a GrDrawOpAtlas. This function can be called anywhere, but the returned atlas 239 * should only be used inside of GrMeshDrawOp::onPrepareDraws. 240 * @param GrColorType The colorType which this atlas will store 241 * @param width width in pixels of the atlas 242 * @param height height in pixels of the atlas 243 * @param numPlotsX The number of plots the atlas should be broken up into in the X 244 * direction 245 * @param numPlotsY The number of plots the atlas should be broken up into in the Y 246 * direction 247 * @param atlasGeneration a pointer to the context's generation counter. 248 * @param allowMultitexturing Can the atlas use more than one texture. 249 * @param evictor A pointer to an eviction callback class. 250 * 251 * @return An initialized GrDrawOpAtlas, or nullptr if creation fails 252 */ 253 static std::unique_ptr<GrDrawOpAtlas> Make(GrProxyProvider*, 254 const GrBackendFormat& format, 255 GrColorType, 256 int width, int height, 257 int plotWidth, int plotHeight, 258 GenerationCounter* generationCounter, 259 AllowMultitexturing allowMultitexturing, 260 #ifdef SK_ENABLE_SMALL_PAGE 261 int atlasPageNum, 262 #endif 263 EvictionCallback* evictor); 264 265 /** 266 * Adds a width x height subimage to the atlas. Upon success it returns 'kSucceeded' and returns 267 * the ID and the subimage's coordinates in the backing texture. 'kTryAgain' is returned if 268 * the subimage cannot fit in the atlas without overwriting texels that will be read in the 269 * current draw. This indicates that the op should end its current draw and begin another 270 * before adding more data. Upon success, an upload of the provided image data will have 271 * been added to the GrDrawOp::Target, in "asap" mode if possible, otherwise in "inline" mode. 272 * Successive uploads in either mode may be consolidated. 273 * 'kError' will be returned when some unrecoverable error was encountered while trying to 274 * add the subimage. In this case the op being created should be discarded. 275 * 276 * NOTE: When the GrDrawOp prepares a draw that reads from the atlas, it must immediately call 277 * 'setUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to 278 * addToAtlas might cause the previous data to be overwritten before it has been read. 279 */ 280 281 enum class ErrorCode { 282 kError, 283 kSucceeded, 284 kTryAgain 285 }; 286 287 ErrorCode addToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, 288 int width, int height, const void* image, AtlasLocator*); 289 getViews()290 const GrSurfaceProxyView* getViews() const { return fViews; } 291 atlasGeneration()292 uint64_t atlasGeneration() const { return fAtlasGeneration; } 293 hasID(const PlotLocator & plotLocator)294 bool hasID(const PlotLocator& plotLocator) { 295 if (!plotLocator.isValid()) { 296 return false; 297 } 298 299 uint32_t plot = plotLocator.plotIndex(); 300 uint32_t page = plotLocator.pageIndex(); 301 uint64_t plotGeneration = fPages[page].fPlotArray[plot]->genID(); 302 uint64_t locatorGeneration = plotLocator.genID(); 303 return plot < fNumPlots && page < fNumActivePages && plotGeneration == locatorGeneration; 304 } 305 306 /** To ensure the atlas does not evict a given entry, the client must set the last use token. */ setLastUseToken(const AtlasLocator & atlasLocator,GrDeferredUploadToken token)307 void setLastUseToken(const AtlasLocator& atlasLocator, GrDeferredUploadToken token) { 308 SkASSERT(this->hasID(atlasLocator.plotLocator())); 309 uint32_t plotIdx = atlasLocator.plotIndex(); 310 SkASSERT(plotIdx < fNumPlots); 311 uint32_t pageIdx = atlasLocator.pageIndex(); 312 SkASSERT(pageIdx < fNumActivePages); 313 Plot* plot = fPages[pageIdx].fPlotArray[plotIdx].get(); 314 this->makeMRU(plot, pageIdx); 315 plot->setLastUseToken(token); 316 } 317 numActivePages()318 uint32_t numActivePages() { return fNumActivePages; } 319 320 /** 321 * A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk. The 322 * current max number of plots per page the GrDrawOpAtlas can handle is 32. If in the future 323 * this is insufficient then we can move to a 64 bit int. 324 */ 325 class BulkUseTokenUpdater { 326 public: BulkUseTokenUpdater()327 BulkUseTokenUpdater() { 328 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated)); 329 } BulkUseTokenUpdater(const BulkUseTokenUpdater & that)330 BulkUseTokenUpdater(const BulkUseTokenUpdater& that) 331 : fPlotsToUpdate(that.fPlotsToUpdate) { 332 memcpy(fPlotAlreadyUpdated, that.fPlotAlreadyUpdated, sizeof(fPlotAlreadyUpdated)); 333 } 334 add(const AtlasLocator & atlasLocator)335 bool add(const AtlasLocator& atlasLocator) { 336 int plotIdx = atlasLocator.plotIndex(); 337 int pageIdx = atlasLocator.pageIndex(); 338 if (this->find(pageIdx, plotIdx)) { 339 return false; 340 } 341 this->set(pageIdx, plotIdx); 342 return true; 343 } 344 reset()345 void reset() { 346 fPlotsToUpdate.reset(); 347 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated)); 348 } 349 350 struct PlotData { PlotDataPlotData351 PlotData(int pageIdx, int plotIdx) : fPageIndex(pageIdx), fPlotIndex(plotIdx) {} 352 uint32_t fPageIndex; 353 uint32_t fPlotIndex; 354 }; 355 356 private: find(int pageIdx,int index)357 bool find(int pageIdx, int index) const { 358 SkASSERT(index < kMaxPlots); 359 return (fPlotAlreadyUpdated[pageIdx] >> index) & 1; 360 } 361 set(int pageIdx,int index)362 void set(int pageIdx, int index) { 363 SkASSERT(!this->find(pageIdx, index)); 364 fPlotAlreadyUpdated[pageIdx] |= (1 << index); 365 fPlotsToUpdate.push_back(PlotData(pageIdx, index)); 366 } 367 368 inline static constexpr int kMinItems = 4; 369 SkSTArray<kMinItems, PlotData, true> fPlotsToUpdate; 370 uint32_t fPlotAlreadyUpdated[kMaxMultitexturePages]; // TODO: increase this to uint64_t 371 // to allow more plots per page 372 373 friend class GrDrawOpAtlas; 374 }; 375 setLastUseTokenBulk(const BulkUseTokenUpdater & updater,GrDeferredUploadToken token)376 void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrDeferredUploadToken token) { 377 int count = updater.fPlotsToUpdate.count(); 378 for (int i = 0; i < count; i++) { 379 const BulkUseTokenUpdater::PlotData& pd = updater.fPlotsToUpdate[i]; 380 // it's possible we've added a plot to the updater and subsequently the plot's page 381 // was deleted -- so we check to prevent a crash 382 if (pd.fPageIndex < fNumActivePages) { 383 Plot* plot = fPages[pd.fPageIndex].fPlotArray[pd.fPlotIndex].get(); 384 this->makeMRU(plot, pd.fPageIndex); 385 plot->setLastUseToken(token); 386 } 387 } 388 } 389 390 #ifdef SK_ENABLE_SMALL_PAGE setRadicalsCompactFlag(bool isRadicals)391 void setRadicalsCompactFlag(bool isRadicals) { fUseRadicalsCompact = isRadicals; } 392 #endif 393 void compact(GrDeferredUploadToken startTokenForNextFlush); 394 395 void instantiate(GrOnFlushResourceProvider*); 396 maxPages()397 uint32_t maxPages() const { 398 return fMaxPages; 399 } 400 401 int numAllocated_TestingOnly() const; 402 void setMaxPages_TestingOnly(uint32_t maxPages); 403 404 private: 405 GrDrawOpAtlas(GrProxyProvider*, const GrBackendFormat& format, GrColorType, int width, 406 int height, int plotWidth, int plotHeight, GenerationCounter* generationCounter, 407 #ifdef SK_ENABLE_SMALL_PAGE 408 AllowMultitexturing allowMultitexturing, int atlasPageNum); 409 #else 410 AllowMultitexturing allowMultitexturing); 411 #endif 412 413 /** 414 * The backing GrTexture for a GrDrawOpAtlas is broken into a spatial grid of Plots. The Plots 415 * keep track of subimage placement via their GrRectanizer. A Plot manages the lifetime of its 416 * data using two tokens, a last use token and a last upload token. Once a Plot is "full" (i.e. 417 * there is no room for the new subimage according to the GrRectanizer), it can no longer be 418 * used unless the last use of the Plot has already been flushed through to the gpu. 419 */ 420 class Plot : public SkRefCnt { 421 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot); 422 423 public: pageIndex()424 uint32_t pageIndex() const { return fPageIndex; } 425 426 /** plotIndex() is a unique id for the plot relative to the owning GrAtlas and page. */ plotIndex()427 uint32_t plotIndex() const { return fPlotIndex; } 428 /** 429 * genID() is incremented when the plot is evicted due to a atlas spill. It is used to know 430 * if a particular subimage is still present in the atlas. 431 */ genID()432 uint64_t genID() const { return fGenID; } plotLocator()433 PlotLocator plotLocator() const { 434 SkASSERT(fPlotLocator.isValid()); 435 return fPlotLocator; 436 } 437 SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; }) 438 439 bool addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator); 440 441 /** 442 * To manage the lifetime of a plot, we use two tokens. We use the last upload token to 443 * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to 444 * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We 445 * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use 446 * has already flushed through the gpu then we can reuse the plot. 447 */ lastUploadToken()448 GrDeferredUploadToken lastUploadToken() const { return fLastUpload; } lastUseToken()449 GrDeferredUploadToken lastUseToken() const { return fLastUse; } setLastUploadToken(GrDeferredUploadToken token)450 void setLastUploadToken(GrDeferredUploadToken token) { fLastUpload = token; } setLastUseToken(GrDeferredUploadToken token)451 void setLastUseToken(GrDeferredUploadToken token) { fLastUse = token; } 452 453 void uploadToTexture(GrDeferredTextureUploadWritePixelsFn&, GrTextureProxy*); 454 void resetRects(); 455 flushesSinceLastUsed()456 int flushesSinceLastUsed() { return fFlushesSinceLastUse; } resetFlushesSinceLastUsed()457 void resetFlushesSinceLastUsed() { fFlushesSinceLastUse = 0; } incFlushesSinceLastUsed()458 void incFlushesSinceLastUsed() { fFlushesSinceLastUse++; } 459 460 private: 461 Plot(int pageIndex, int plotIndex, GenerationCounter* generationCounter, 462 int offX, int offY, int width, int height, GrColorType colorType); 463 464 ~Plot() override; 465 466 /** 467 * Create a clone of this plot. The cloned plot will take the place of the current plot in 468 * the atlas 469 */ clone()470 Plot* clone() const { 471 return new Plot( 472 fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType); 473 } 474 475 GrDeferredUploadToken fLastUpload; 476 GrDeferredUploadToken fLastUse; 477 // the number of flushes since this plot has been last used 478 int fFlushesSinceLastUse; 479 480 struct { 481 const uint32_t fPageIndex : 16; 482 const uint32_t fPlotIndex : 16; 483 }; 484 GenerationCounter* const fGenerationCounter; 485 uint64_t fGenID; 486 PlotLocator fPlotLocator; 487 unsigned char* fData; 488 const int fWidth; 489 const int fHeight; 490 const int fX; 491 const int fY; 492 GrRectanizerSkyline fRectanizer; 493 const SkIPoint16 fOffset; // the offset of the plot in the backing texture 494 const GrColorType fColorType; 495 const size_t fBytesPerPixel; 496 SkIRect fDirtyRect; 497 SkDEBUGCODE(bool fDirty); 498 499 friend class GrDrawOpAtlas; 500 501 using INHERITED = SkRefCnt; 502 }; 503 504 typedef SkTInternalLList<Plot> PlotList; 505 506 inline bool updatePlot(GrDeferredUploadTarget*, AtlasLocator*, Plot*); 507 makeMRU(Plot * plot,int pageIdx)508 inline void makeMRU(Plot* plot, int pageIdx) { 509 if (fPages[pageIdx].fPlotList.head() == plot) { 510 return; 511 } 512 513 fPages[pageIdx].fPlotList.remove(plot); 514 fPages[pageIdx].fPlotList.addToHead(plot); 515 516 // No MRU update for pages -- since we will always try to add from 517 // the front and remove from the back there is no need for MRU. 518 } 519 520 bool uploadToPage(unsigned int pageIdx, GrDeferredUploadTarget*, int width, int height, 521 const void* image, AtlasLocator*); 522 523 bool createPages(GrProxyProvider*, GenerationCounter*); 524 bool activateNewPage(GrResourceProvider*); 525 void deactivateLastPage(); 526 #ifdef SK_ENABLE_SMALL_PAGE 527 void compactRadicals(GrDeferredUploadToken startTokenForNextFlush); 528 #endif 529 530 void processEviction(PlotLocator); processEvictionAndResetRects(Plot * plot)531 inline void processEvictionAndResetRects(Plot* plot) { 532 this->processEviction(plot->plotLocator()); 533 plot->resetRects(); 534 } 535 536 GrBackendFormat fFormat; 537 GrColorType fColorType; 538 int fTextureWidth; 539 int fTextureHeight; 540 int fPlotWidth; 541 int fPlotHeight; 542 unsigned int fNumPlots; 543 544 GenerationCounter* const fGenerationCounter; 545 uint64_t fAtlasGeneration; 546 547 // nextTokenToFlush() value at the end of the previous flush 548 GrDeferredUploadToken fPrevFlushToken; 549 550 // the number of flushes since this atlas has been last used 551 int fFlushesSinceLastUse; 552 553 std::vector<EvictionCallback*> fEvictionCallbacks; 554 555 struct Page { 556 // allocated array of Plots 557 std::unique_ptr<sk_sp<Plot>[]> fPlotArray; 558 // LRU list of Plots (MRU at head - LRU at tail) 559 PlotList fPlotList; 560 }; 561 // proxies kept separate to make it easier to pass them up to client 562 GrSurfaceProxyView fViews[kMaxMultitexturePages]; 563 Page fPages[kMaxMultitexturePages]; 564 uint32_t fMaxPages; 565 566 uint32_t fNumActivePages; 567 568 #ifdef SK_ENABLE_SMALL_PAGE 569 bool fUseRadicalsCompact = false; 570 #endif 571 572 SkDEBUGCODE(void validate(const AtlasLocator& atlasLocator) const;) 573 }; 574 575 // There are three atlases (A8, 565, ARGB) that are kept in relation with one another. In 576 // general, the A8 dimensions are 2x the 565 and ARGB dimensions with the constraint that an atlas 577 // size will always contain at least one plot. Since the ARGB atlas takes the most space, its 578 // dimensions are used to size the other two atlases. 579 class GrDrawOpAtlasConfig { 580 public: 581 // The capabilities of the GPU define maxTextureSize. The client provides maxBytes, and this 582 // represents the largest they want a single atlas texture to be. Due to multitexturing, we 583 // may expand temporarily to use more space as needed. 584 GrDrawOpAtlasConfig(int maxTextureSize, size_t maxBytes); 585 586 // For testing only - make minimum sized atlases -- a single plot for ARGB, four for A8 GrDrawOpAtlasConfig()587 GrDrawOpAtlasConfig() : GrDrawOpAtlasConfig(kMaxAtlasDim, 0) {} 588 589 SkISize atlasDimensions(GrMaskFormat type) const; 590 SkISize plotDimensions(GrMaskFormat type) const; 591 #ifdef SK_ENABLE_SMALL_PAGE getARGBDimensions()592 SkISize getARGBDimensions(){ return fARGBDimensions; } 593 int resetAsSmallPage(); 594 #endif 595 596 private: 597 // On some systems texture coordinates are represented using half-precision floating point, 598 // which limits the largest atlas dimensions to 2048x2048. 599 // For simplicity we'll use this constraint for all of our atlas textures. 600 // This can be revisited later if we need larger atlases. 601 inline static constexpr int kMaxAtlasDim = 2048; 602 603 SkISize fARGBDimensions; 604 int fMaxTextureSize; 605 }; 606 607 #endif 608