aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrBatchAtlas.h
blob: 4e235409246e16f2495e686a055027f4b504bf14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrBatchAtlas_DEFINED
#define GrBatchAtlas_DEFINED

#include "GrTexture.h"
#include "batches/GrDrawBatch.h"
#include "SkPoint.h"
#include "SkTDArray.h"
#include "SkTInternalLList.h"

class BatchPlot;
class GrRectanizer;

typedef SkTInternalLList<BatchPlot> GrBatchPlotList;

struct GrBatchAtlasConfig {
    int numPlotsX() const { return fWidth / fPlotWidth; }
    int numPlotsY() const { return fHeight / fPlotWidth; }
    int fWidth;
    int fHeight;
    int fPlotWidth;
    int fPlotHeight;
};

class GrBatchAtlas {
public:
    // An AtlasID is an opaque handle which callers can use to determine if the atlas contains
    // a specific piece of data
    typedef uint64_t AtlasID;
    static const uint32_t kInvalidAtlasID = 0;
    static const uint64_t kInvalidAtlasGeneration = 0;

    // A function pointer for use as a callback during eviction.  Whenever GrBatchAtlas evicts a
    // specific AtlasID, it will call all of the registered listeners so they can optionally process
    // the eviction
    typedef void (*EvictionFunc)(GrBatchAtlas::AtlasID, void*);

    GrBatchAtlas(GrTexture*, int numPlotsX, int numPlotsY);
    ~GrBatchAtlas();

    // Adds a width x height subimage to the atlas. Upon success it returns
    // the containing GrPlot and absolute location in the backing texture.
    // NULL is returned if the subimage cannot fit in the atlas.
    // If provided, the image data will be written to the CPU-side backing bitmap.
    // NOTE: If the client intends to refer to the atlas, they should immediately call 'setUseToken'
    // with the currentToken from the batch target, otherwise the next call to addToAtlas might
    // cause an eviction
    bool addToAtlas(AtlasID*, GrDrawBatch::Target*, int width, int height, const void* image,
                    SkIPoint16* loc);

    GrTexture* getTexture() const { return fTexture; }

    uint64_t atlasGeneration() const { return fAtlasGeneration; }
    bool hasID(AtlasID id);

    // To ensure the atlas does not evict a given entry, the client must set the last use token
    void setLastUseToken(AtlasID id, GrBatchToken batchToken);
    void registerEvictionCallback(EvictionFunc func, void* userData) {
        EvictionData* data = fEvictionCallbacks.append();
        data->fFunc = func;
        data->fData = userData;
    }

    /*
     * A class which can be handed back to GrBatchAtlas for updating in bulk last use tokens.  The
     * current max number of plots the GrBatchAtlas can handle is 32, if in the future this is
     * insufficient then we can move to a 64 bit int
     */
    class BulkUseTokenUpdater {
    public:
        BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
        BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
            : fPlotsToUpdate(that.fPlotsToUpdate)
            , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
        }

        void add(AtlasID id) {
            int index = GrBatchAtlas::GetIndexFromID(id);
            if (!this->find(index)) {
                this->set(index);
            }
        }

        void reset() {
            fPlotsToUpdate.reset();
            fPlotAlreadyUpdated = 0;
        }

    private:
        bool find(int index) const {
            SkASSERT(index < kMaxPlots);
            return (fPlotAlreadyUpdated >> index) & 1;
        }

        void set(int index) {
            SkASSERT(!this->find(index));
            fPlotAlreadyUpdated = fPlotAlreadyUpdated | (1 << index);
            fPlotsToUpdate.push_back(index);
        }

        static const int kMinItems = 4;
        static const int kMaxPlots = 32;
        SkSTArray<kMinItems, int, true> fPlotsToUpdate;
        uint32_t fPlotAlreadyUpdated;

        friend class GrBatchAtlas;
    };

    void setLastUseTokenBulk(const BulkUseTokenUpdater& reffer, GrBatchToken);

    static const int kGlyphMaxDim = 256;
    static bool GlyphTooLargeForAtlas(int width, int height) {
        return width > kGlyphMaxDim || height > kGlyphMaxDim;
    }

private:
    static uint32_t GetIndexFromID(AtlasID id) {
        return id & 0xffff;
    }

    // top 48 bits are reserved for the generation ID
    static uint64_t GetGenerationFromID(AtlasID id) {
        return (id >> 16) & 0xffffffffffff;
    }

    inline void updatePlot(GrDrawBatch::Target*, AtlasID*, BatchPlot*);

    inline void makeMRU(BatchPlot* plot);

    inline void processEviction(AtlasID);

    GrTexture* fTexture;
    uint32_t fNumPlotsX;
    uint32_t fNumPlotsY;
    uint32_t fPlotWidth;
    uint32_t fPlotHeight;
    size_t fBPP;
    uint64_t fAtlasGeneration;

    struct EvictionData {
        EvictionFunc fFunc;
        void* fData;
    };

    SkTDArray<EvictionData> fEvictionCallbacks;
    // allocated array of GrBatchPlots
    SkAutoTUnref<BatchPlot>* fPlotArray;
    // LRU list of GrPlots (MRU at head - LRU at tail)
    GrBatchPlotList fPlotList;
};

#endif