• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkPoint.h"
12 #include "SkTDArray.h"
13 #include "SkTInternalLList.h"
14 
15 #include "ops/GrDrawOp.h"
16 
17 class GrRectanizer;
18 
19 struct GrDrawOpAtlasConfig {
numPlotsXGrDrawOpAtlasConfig20     int numPlotsX() const { return fWidth / fPlotWidth; }
numPlotsYGrDrawOpAtlasConfig21     int numPlotsY() const { return fHeight / fPlotWidth; }
22     int fWidth;
23     int fHeight;
24     int fLog2Width;
25     int fLog2Height;
26     int fPlotWidth;
27     int fPlotHeight;
28 };
29 
30 /**
31  * This class manages an atlas texture on behalf of GrDrawOps. The draw ops that use the atlas
32  * perform texture uploads when preparing their draws during flush. The class provides facilities
33  * for using GrDrawOpUploadToken to detect data hazards. Op's uploads are performed in "asap" mode
34  * until it is impossible to add data without overwriting texels read by draws that have not yet
35  * executed on the gpu. At that point the uploads are performed "inline" between draws. If a single
36  * draw would use enough subimage space to overflow the atlas texture then the atlas will fail to
37  * add a subimage. This gives the op the chance to end the draw and begin a new one. Additional
38  * uploads will then succeed in inline mode.
39  */
40 class GrDrawOpAtlas {
41 public:
42     /**
43      * An AtlasID is an opaque handle which callers can use to determine if the atlas contains
44      * a specific piece of data.
45      */
46     typedef uint64_t AtlasID;
47     static const uint32_t kInvalidAtlasID = 0;
48     static const uint64_t kInvalidAtlasGeneration = 0;
49 
50     /**
51      * A function pointer for use as a callback during eviction. Whenever GrDrawOpAtlas evicts a
52      * specific AtlasID, it will call all of the registered listeners so they can process the
53      * eviction.
54      */
55     typedef void (*EvictionFunc)(GrDrawOpAtlas::AtlasID, void*);
56 
57     /**
58      * Returns a GrDrawOpAtlas. This function can be called anywhere, but the returned atlas
59      * should only be used inside of GrMeshDrawOp::onPrepareDraws.
60      *  @param GrPixelConfig    The pixel config which this atlas will store
61      *  @param width            width in pixels of the atlas
62      *  @param height           height in pixels of the atlas
63      *  @param numPlotsX        The number of plots the atlas should be broken up into in the X
64      *                          direction
65      *  @param numPlotsY        The number of plots the atlas should be broken up into in the Y
66      *                          direction
67      *  @param func             An eviction function which will be called whenever the atlas has to
68      *                          evict data
69      *  @param data             User supplied data which will be passed into func whenver an
70      *                          eviction occurs
71      *  @return                 An initialized GrDrawOpAtlas, or nullptr if creation fails
72      */
73     static std::unique_ptr<GrDrawOpAtlas> Make(GrContext*, GrPixelConfig,
74                                                int width, int height,
75                                                int numPlotsX, int numPlotsY,
76                                                GrDrawOpAtlas::EvictionFunc func, void* data);
77 
78     /**
79      * Adds a width x height subimage to the atlas. Upon success it returns an ID and the subimage's
80      * coordinates in the backing texture. False is returned if the subimage cannot fit in the
81      * atlas without overwriting texels that will be read in the current draw. This indicates that
82      * the op should end its current draw and begin another before adding more data. Upon success,
83      * an upload of the provided image data will have been added to the GrDrawOp::Target, in "asap"
84      * mode if possible, otherwise in "inline" mode. Successive uploads in either mode may be
85      * consolidated.
86      * NOTE: When the GrDrawOp prepares a draw that reads from the atlas, it must immediately call
87      * 'setUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to
88      * addToAtlas might cause the previous data to be overwritten before it has been read.
89      */
90     bool addToAtlas(AtlasID*, GrDrawOp::Target*, int width, int height, const void* image,
91                     SkIPoint16* loc);
92 
context()93     GrContext* context() const { return fContext; }
getProxy()94     sk_sp<GrTextureProxy> getProxy() const { return fProxy; }
95 
atlasGeneration()96     uint64_t atlasGeneration() const { return fAtlasGeneration; }
97 
hasID(AtlasID id)98     inline bool hasID(AtlasID id) {
99         uint32_t index = GetIndexFromID(id);
100         SkASSERT(index < fNumPlots);
101         return fPlotArray[index]->genID() == GetGenerationFromID(id);
102     }
103 
104     /** To ensure the atlas does not evict a given entry, the client must set the last use token. */
setLastUseToken(AtlasID id,GrDrawOpUploadToken token)105     inline void setLastUseToken(AtlasID id, GrDrawOpUploadToken token) {
106         SkASSERT(this->hasID(id));
107         uint32_t index = GetIndexFromID(id);
108         SkASSERT(index < fNumPlots);
109         this->makeMRU(fPlotArray[index].get());
110         fPlotArray[index]->setLastUseToken(token);
111     }
112 
registerEvictionCallback(EvictionFunc func,void * userData)113     inline void registerEvictionCallback(EvictionFunc func, void* userData) {
114         EvictionData* data = fEvictionCallbacks.append();
115         data->fFunc = func;
116         data->fData = userData;
117     }
118 
119     /**
120      * A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk.  The
121      * current max number of plots the GrDrawOpAtlas can handle is 32. If in the future this is
122      * insufficient then we can move to a 64 bit int.
123      */
124     class BulkUseTokenUpdater {
125     public:
BulkUseTokenUpdater()126         BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
BulkUseTokenUpdater(const BulkUseTokenUpdater & that)127         BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
128             : fPlotsToUpdate(that.fPlotsToUpdate)
129             , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
130         }
131 
add(AtlasID id)132         void add(AtlasID id) {
133             int index = GrDrawOpAtlas::GetIndexFromID(id);
134             if (!this->find(index)) {
135                 this->set(index);
136             }
137         }
138 
reset()139         void reset() {
140             fPlotsToUpdate.reset();
141             fPlotAlreadyUpdated = 0;
142         }
143 
144     private:
find(int index)145         bool find(int index) const {
146             SkASSERT(index < kMaxPlots);
147             return (fPlotAlreadyUpdated >> index) & 1;
148         }
149 
set(int index)150         void set(int index) {
151             SkASSERT(!this->find(index));
152             fPlotAlreadyUpdated = fPlotAlreadyUpdated | (1 << index);
153             fPlotsToUpdate.push_back(index);
154         }
155 
156         static const int kMinItems = 4;
157         static const int kMaxPlots = 32;
158         SkSTArray<kMinItems, int, true> fPlotsToUpdate;
159         uint32_t fPlotAlreadyUpdated;
160 
161         friend class GrDrawOpAtlas;
162     };
163 
setLastUseTokenBulk(const BulkUseTokenUpdater & updater,GrDrawOpUploadToken token)164     void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrDrawOpUploadToken token) {
165         int count = updater.fPlotsToUpdate.count();
166         for (int i = 0; i < count; i++) {
167             Plot* plot = fPlotArray[updater.fPlotsToUpdate[i]].get();
168             this->makeMRU(plot);
169             plot->setLastUseToken(token);
170         }
171     }
172 
173     static const int kGlyphMaxDim = 256;
GlyphTooLargeForAtlas(int width,int height)174     static bool GlyphTooLargeForAtlas(int width, int height) {
175         return width > kGlyphMaxDim || height > kGlyphMaxDim;
176     }
177 
178 private:
179     GrDrawOpAtlas(GrContext*, sk_sp<GrTextureProxy>, int numPlotsX, int numPlotsY);
180 
181     /**
182      * The backing GrTexture for a GrDrawOpAtlas is broken into a spatial grid of Plots. The Plots
183      * keep track of subimage placement via their GrRectanizer. A Plot manages the lifetime of its
184      * data using two tokens, a last use token and a last upload token. Once a Plot is "full" (i.e.
185      * there is no room for the new subimage according to the GrRectanizer), it can no longer be
186      * used unless the last use of the Plot has already been flushed through to the gpu.
187      */
188     class Plot : public SkRefCnt {
189         SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot);
190 
191     public:
192         /** index() is a unique id for the plot relative to the owning GrAtlas. */
index()193         uint32_t index() const { return fIndex; }
194         /**
195          * genID() is incremented when the plot is evicted due to a atlas spill. It is used to know
196          * if a particular subimage is still present in the atlas.
197          */
genID()198         uint64_t genID() const { return fGenID; }
id()199         GrDrawOpAtlas::AtlasID id() const {
200             SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != fID);
201             return fID;
202         }
203         SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
204 
205         bool addSubImage(int width, int height, const void* image, SkIPoint16* loc);
206 
207         /**
208          * To manage the lifetime of a plot, we use two tokens. We use the last upload token to
209          * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to
210          * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We
211          * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use
212          * has already flushed through the gpu then we can reuse the plot.
213          */
lastUploadToken()214         GrDrawOpUploadToken lastUploadToken() const { return fLastUpload; }
lastUseToken()215         GrDrawOpUploadToken lastUseToken() const { return fLastUse; }
setLastUploadToken(GrDrawOpUploadToken token)216         void setLastUploadToken(GrDrawOpUploadToken token) { fLastUpload = token; }
setLastUseToken(GrDrawOpUploadToken token)217         void setLastUseToken(GrDrawOpUploadToken token) { fLastUse = token; }
218 
219         void uploadToTexture(GrDrawOp::WritePixelsFn&, GrTexture* texture);
220         void resetRects();
221 
222     private:
223         Plot(int index, uint64_t genID, int offX, int offY, int width, int height,
224              GrPixelConfig config);
225 
226         ~Plot() override;
227 
228         /**
229          * Create a clone of this plot. The cloned plot will take the place of the current plot in
230          * the atlas
231          */
clone()232         Plot* clone() const {
233             return new Plot(fIndex, fGenID + 1, fX, fY, fWidth, fHeight, fConfig);
234         }
235 
CreateId(uint32_t index,uint64_t generation)236         static GrDrawOpAtlas::AtlasID CreateId(uint32_t index, uint64_t generation) {
237             SkASSERT(index < (1 << 16));
238             SkASSERT(generation < ((uint64_t)1 << 48));
239             return generation << 16 | index;
240         }
241 
242         GrDrawOpUploadToken   fLastUpload;
243         GrDrawOpUploadToken   fLastUse;
244 
245         const uint32_t fIndex;
246         uint64_t fGenID;
247         GrDrawOpAtlas::AtlasID fID;
248         unsigned char* fData;
249         const int fWidth;
250         const int fHeight;
251         const int fX;
252         const int fY;
253         GrRectanizer* fRects;
254         const SkIPoint16 fOffset;  // the offset of the plot in the backing texture
255         const GrPixelConfig fConfig;
256         const size_t fBytesPerPixel;
257         SkIRect fDirtyRect;
258         SkDEBUGCODE(bool fDirty);
259 
260         friend class GrDrawOpAtlas;
261 
262         typedef SkRefCnt INHERITED;
263     };
264 
265     typedef SkTInternalLList<Plot> PlotList;
266 
GetIndexFromID(AtlasID id)267     static uint32_t GetIndexFromID(AtlasID id) {
268         return id & 0xffff;
269     }
270 
271     // top 48 bits are reserved for the generation ID
GetGenerationFromID(AtlasID id)272     static uint64_t GetGenerationFromID(AtlasID id) {
273         return (id >> 16) & 0xffffffffffff;
274     }
275 
276     inline bool updatePlot(GrDrawOp::Target*, AtlasID*, Plot*);
277 
makeMRU(Plot * plot)278     inline void makeMRU(Plot* plot) {
279         if (fPlotList.head() == plot) {
280             return;
281         }
282 
283         fPlotList.remove(plot);
284         fPlotList.addToHead(plot);
285     }
286 
287     inline void processEviction(AtlasID);
288 
289     GrContext*            fContext;
290     sk_sp<GrTextureProxy> fProxy;
291     int                   fPlotWidth;
292     int                   fPlotHeight;
293     SkDEBUGCODE(uint32_t  fNumPlots;)
294 
295     uint64_t              fAtlasGeneration;
296 
297     struct EvictionData {
298         EvictionFunc fFunc;
299         void* fData;
300     };
301 
302     SkTDArray<EvictionData> fEvictionCallbacks;
303     // allocated array of Plots
304     std::unique_ptr<sk_sp<Plot>[]> fPlotArray;
305     // LRU list of Plots (MRU at head - LRU at tail)
306     PlotList fPlotList;
307 };
308 
309 #endif
310