Home | History | Annotate | Download | only in gpu
      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 {
     20     int numPlotsX() const { return fWidth / fPlotWidth; }
     21     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 
     93     GrContext* context() const { return fContext; }
     94     sk_sp<GrTextureProxy> getProxy() const { return fProxy; }
     95 
     96     uint64_t atlasGeneration() const { return fAtlasGeneration; }
     97 
     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. */
    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 
    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:
    126         BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
    127         BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
    128             : fPlotsToUpdate(that.fPlotsToUpdate)
    129             , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
    130         }
    131 
    132         void add(AtlasID id) {
    133             int index = GrDrawOpAtlas::GetIndexFromID(id);
    134             if (!this->find(index)) {
    135                 this->set(index);
    136             }
    137         }
    138 
    139         void reset() {
    140             fPlotsToUpdate.reset();
    141             fPlotAlreadyUpdated = 0;
    142         }
    143 
    144     private:
    145         bool find(int index) const {
    146             SkASSERT(index < kMaxPlots);
    147             return (fPlotAlreadyUpdated >> index) & 1;
    148         }
    149 
    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 
    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;
    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. */
    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          */
    198         uint64_t genID() const { return fGenID; }
    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          */
    214         GrDrawOpUploadToken lastUploadToken() const { return fLastUpload; }
    215         GrDrawOpUploadToken lastUseToken() const { return fLastUse; }
    216         void setLastUploadToken(GrDrawOpUploadToken token) { fLastUpload = 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          */
    232         Plot* clone() const {
    233             return new Plot(fIndex, fGenID + 1, fX, fY, fWidth, fHeight, fConfig);
    234         }
    235 
    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 
    267     static uint32_t GetIndexFromID(AtlasID id) {
    268         return id & 0xffff;
    269     }
    270 
    271     // top 48 bits are reserved for the generation ID
    272     static uint64_t GetGenerationFromID(AtlasID id) {
    273         return (id >> 16) & 0xffffffffffff;
    274     }
    275 
    276     inline bool updatePlot(GrDrawOp::Target*, AtlasID*, Plot*);
    277 
    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