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 GrBatchAtlas_DEFINED 9 #define GrBatchAtlas_DEFINED 10 11 #include "GrTexture.h" 12 #include "SkPoint.h" 13 #include "SkTDArray.h" 14 #include "SkTInternalLList.h" 15 16 #include "batches/GrDrawBatch.h" 17 18 class GrRectanizer; 19 20 struct GrBatchAtlasConfig { numPlotsXGrBatchAtlasConfig21 int numPlotsX() const { return fWidth / fPlotWidth; } numPlotsYGrBatchAtlasConfig22 int numPlotsY() const { return fHeight / fPlotWidth; } 23 int fWidth; 24 int fHeight; 25 int fLog2Width; 26 int fLog2Height; 27 int fPlotWidth; 28 int fPlotHeight; 29 }; 30 31 class GrBatchAtlas { 32 public: 33 // An AtlasID is an opaque handle which callers can use to determine if the atlas contains 34 // a specific piece of data 35 typedef uint64_t AtlasID; 36 static const uint32_t kInvalidAtlasID = 0; 37 static const uint64_t kInvalidAtlasGeneration = 0; 38 39 // A function pointer for use as a callback during eviction. Whenever GrBatchAtlas evicts a 40 // specific AtlasID, it will call all of the registered listeners so they can optionally process 41 // the eviction 42 typedef void (*EvictionFunc)(GrBatchAtlas::AtlasID, void*); 43 44 GrBatchAtlas(GrTexture*, int numPlotsX, int numPlotsY); 45 ~GrBatchAtlas(); 46 47 // Adds a width x height subimage to the atlas. Upon success it returns 48 // the containing GrPlot and absolute location in the backing texture. 49 // nullptr is returned if the subimage cannot fit in the atlas. 50 // If provided, the image data will be written to the CPU-side backing bitmap. 51 // NOTE: If the client intends to refer to the atlas, they should immediately call 'setUseToken' 52 // with the currentToken from the batch target, otherwise the next call to addToAtlas might 53 // cause an eviction 54 bool addToAtlas(AtlasID*, GrDrawBatch::Target*, int width, int height, const void* image, 55 SkIPoint16* loc); 56 getTexture()57 GrTexture* getTexture() const { return fTexture; } 58 atlasGeneration()59 uint64_t atlasGeneration() const { return fAtlasGeneration; } 60 hasID(AtlasID id)61 inline bool hasID(AtlasID id) { 62 uint32_t index = GetIndexFromID(id); 63 SkASSERT(index < fNumPlots); 64 return fPlotArray[index]->genID() == GetGenerationFromID(id); 65 } 66 67 // To ensure the atlas does not evict a given entry, the client must set the last use token setLastUseToken(AtlasID id,GrBatchDrawToken batchToken)68 inline void setLastUseToken(AtlasID id, GrBatchDrawToken batchToken) { 69 SkASSERT(this->hasID(id)); 70 uint32_t index = GetIndexFromID(id); 71 SkASSERT(index < fNumPlots); 72 this->makeMRU(fPlotArray[index]); 73 fPlotArray[index]->setLastUseToken(batchToken); 74 } 75 registerEvictionCallback(EvictionFunc func,void * userData)76 inline void registerEvictionCallback(EvictionFunc func, void* userData) { 77 EvictionData* data = fEvictionCallbacks.append(); 78 data->fFunc = func; 79 data->fData = userData; 80 } 81 82 /* 83 * A class which can be handed back to GrBatchAtlas for updating in bulk last use tokens. The 84 * current max number of plots the GrBatchAtlas can handle is 32, if in the future this is 85 * insufficient then we can move to a 64 bit int 86 */ 87 class BulkUseTokenUpdater { 88 public: BulkUseTokenUpdater()89 BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {} BulkUseTokenUpdater(const BulkUseTokenUpdater & that)90 BulkUseTokenUpdater(const BulkUseTokenUpdater& that) 91 : fPlotsToUpdate(that.fPlotsToUpdate) 92 , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) { 93 } 94 add(AtlasID id)95 void add(AtlasID id) { 96 int index = GrBatchAtlas::GetIndexFromID(id); 97 if (!this->find(index)) { 98 this->set(index); 99 } 100 } 101 reset()102 void reset() { 103 fPlotsToUpdate.reset(); 104 fPlotAlreadyUpdated = 0; 105 } 106 107 private: find(int index)108 bool find(int index) const { 109 SkASSERT(index < kMaxPlots); 110 return (fPlotAlreadyUpdated >> index) & 1; 111 } 112 set(int index)113 void set(int index) { 114 SkASSERT(!this->find(index)); 115 fPlotAlreadyUpdated = fPlotAlreadyUpdated | (1 << index); 116 fPlotsToUpdate.push_back(index); 117 } 118 119 static const int kMinItems = 4; 120 static const int kMaxPlots = 32; 121 SkSTArray<kMinItems, int, true> fPlotsToUpdate; 122 uint32_t fPlotAlreadyUpdated; 123 124 friend class GrBatchAtlas; 125 }; 126 setLastUseTokenBulk(const BulkUseTokenUpdater & updater,GrBatchDrawToken batchToken)127 void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrBatchDrawToken batchToken) { 128 int count = updater.fPlotsToUpdate.count(); 129 for (int i = 0; i < count; i++) { 130 BatchPlot* plot = fPlotArray[updater.fPlotsToUpdate[i]]; 131 this->makeMRU(plot); 132 plot->setLastUseToken(batchToken); 133 } 134 } 135 136 static const int kGlyphMaxDim = 256; GlyphTooLargeForAtlas(int width,int height)137 static bool GlyphTooLargeForAtlas(int width, int height) { 138 return width > kGlyphMaxDim || height > kGlyphMaxDim; 139 } 140 141 private: 142 // The backing GrTexture for a GrBatchAtlas is broken into a spatial grid of BatchPlots. 143 // The BatchPlots keep track of subimage placement via their GrRectanizer. A BatchPlot 144 // manages the lifetime of its data using two tokens, a last use token and a last upload token. 145 // Once a BatchPlot is "full" (i.e. there is no room for the new subimage according to the 146 // GrRectanizer), it can no longer be used unless the last use of the GrPlot has already been 147 // flushed through to the gpu. 148 class BatchPlot : public SkRefCnt { 149 SK_DECLARE_INTERNAL_LLIST_INTERFACE(BatchPlot); 150 151 public: 152 // index() is a unique id for the plot relative to the owning GrAtlas. genID() is a 153 // monotonically incremented number which is bumped every time this plot is 154 // evicted from the cache (i.e., there is continuity in genID() across atlas spills). index()155 uint32_t index() const { return fIndex; } genID()156 uint64_t genID() const { return fGenID; } id()157 GrBatchAtlas::AtlasID id() const { 158 SkASSERT(GrBatchAtlas::kInvalidAtlasID != fID); 159 return fID; 160 } 161 SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; }) 162 163 bool addSubImage(int width, int height, const void* image, SkIPoint16* loc); 164 165 // To manage the lifetime of a plot, we use two tokens. We use the last upload token to 166 // know when we can 'piggy back' uploads, ie if the last upload hasn't been flushed to gpu, 167 // we don't need to issue a new upload even if we update the cpu backing store. We use 168 // lastUse to determine when we can evict a plot from the cache, ie if the last use has 169 // already flushed through the gpu then we can reuse the plot. lastUploadToken()170 GrBatchDrawToken lastUploadToken() const { return fLastUpload; } lastUseToken()171 GrBatchDrawToken lastUseToken() const { return fLastUse; } setLastUploadToken(GrBatchDrawToken batchToken)172 void setLastUploadToken(GrBatchDrawToken batchToken) { fLastUpload = batchToken; } setLastUseToken(GrBatchDrawToken batchToken)173 void setLastUseToken(GrBatchDrawToken batchToken) { fLastUse = batchToken; } 174 175 void uploadToTexture(GrDrawBatch::WritePixelsFn&, GrTexture* texture); 176 void resetRects(); 177 178 private: 179 BatchPlot(int index, uint64_t genID, int offX, int offY, int width, int height, 180 GrPixelConfig config); 181 182 ~BatchPlot() override; 183 184 // Create a clone of this plot. The cloned plot will take the place of the 185 // current plot in the atlas. clone()186 BatchPlot* clone() const { 187 return new BatchPlot(fIndex, fGenID+1, fX, fY, fWidth, fHeight, fConfig); 188 } 189 CreateId(uint32_t index,uint64_t generation)190 static GrBatchAtlas::AtlasID CreateId(uint32_t index, uint64_t generation) { 191 SkASSERT(index < (1 << 16)); 192 SkASSERT(generation < ((uint64_t)1 << 48)); 193 return generation << 16 | index; 194 } 195 196 GrBatchDrawToken fLastUpload; 197 GrBatchDrawToken fLastUse; 198 199 const uint32_t fIndex; 200 uint64_t fGenID; 201 GrBatchAtlas::AtlasID fID; 202 unsigned char* fData; 203 const int fWidth; 204 const int fHeight; 205 const int fX; 206 const int fY; 207 GrRectanizer* fRects; 208 const SkIPoint16 fOffset; // the offset of the plot in the backing texture 209 const GrPixelConfig fConfig; 210 const size_t fBytesPerPixel; 211 SkIRect fDirtyRect; 212 SkDEBUGCODE(bool fDirty;) 213 214 friend class GrBatchAtlas; 215 216 typedef SkRefCnt INHERITED; 217 }; 218 219 typedef SkTInternalLList<BatchPlot> GrBatchPlotList; 220 GetIndexFromID(AtlasID id)221 static uint32_t GetIndexFromID(AtlasID id) { 222 return id & 0xffff; 223 } 224 225 // top 48 bits are reserved for the generation ID GetGenerationFromID(AtlasID id)226 static uint64_t GetGenerationFromID(AtlasID id) { 227 return (id >> 16) & 0xffffffffffff; 228 } 229 230 inline void updatePlot(GrDrawBatch::Target*, AtlasID*, BatchPlot*); 231 makeMRU(BatchPlot * plot)232 inline void makeMRU(BatchPlot* plot) { 233 if (fPlotList.head() == plot) { 234 return; 235 } 236 237 fPlotList.remove(plot); 238 fPlotList.addToHead(plot); 239 } 240 241 inline void processEviction(AtlasID); 242 243 GrTexture* fTexture; 244 int fPlotWidth; 245 int fPlotHeight; 246 SkDEBUGCODE(uint32_t fNumPlots;) 247 248 uint64_t fAtlasGeneration; 249 250 struct EvictionData { 251 EvictionFunc fFunc; 252 void* fData; 253 }; 254 255 SkTDArray<EvictionData> fEvictionCallbacks; 256 // allocated array of GrBatchPlots 257 SkAutoTUnref<BatchPlot>* fPlotArray; 258 // LRU list of GrPlots (MRU at head - LRU at tail) 259 GrBatchPlotList fPlotList; 260 }; 261 262 #endif 263