blob: 1119e3de18cf4798be7ab082f5b68f1abda75fb0 [file] [log] [blame]
joshualitt5bf99f12015-03-13 11:47:42 -07001/*
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
Brian Salomon2ee084e2016-12-16 18:59:19 -05008#ifndef GrDrawOpAtlas_DEFINED
9#define GrDrawOpAtlas_DEFINED
joshualitt5bf99f12015-03-13 11:47:42 -070010
joshualitt5bf99f12015-03-13 11:47:42 -070011#include "SkPoint.h"
12#include "SkTDArray.h"
13#include "SkTInternalLList.h"
14
Brian Salomon89527432016-12-16 09:52:16 -050015#include "ops/GrDrawOp.h"
joshualittddd22d82016-02-16 06:47:52 -080016
joshualitt5bf99f12015-03-13 11:47:42 -070017class GrRectanizer;
18
Brian Salomon2ee084e2016-12-16 18:59:19 -050019struct GrDrawOpAtlasConfig {
joshualittda04e0e2015-08-19 08:16:43 -070020 int numPlotsX() const { return fWidth / fPlotWidth; }
21 int numPlotsY() const { return fHeight / fPlotWidth; }
22 int fWidth;
23 int fHeight;
jvanverth7023a002016-02-22 11:25:32 -080024 int fLog2Width;
25 int fLog2Height;
joshualittda04e0e2015-08-19 08:16:43 -070026 int fPlotWidth;
27 int fPlotHeight;
28};
29
Brian Salomon2ee084e2016-12-16 18:59:19 -050030/**
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 */
40class GrDrawOpAtlas {
joshualitt5bf99f12015-03-13 11:47:42 -070041public:
Brian Salomon2ee084e2016-12-16 18:59:19 -050042 /**
43 * An AtlasID is an opaque handle which callers can use to determine if the atlas contains
44 * a specific piece of data.
45 */
joshualitt8db6fdc2015-07-31 08:25:07 -070046 typedef uint64_t AtlasID;
joshualitt7c3a2f82015-03-31 13:32:05 -070047 static const uint32_t kInvalidAtlasID = 0;
48 static const uint64_t kInvalidAtlasGeneration = 0;
joshualitt5bf99f12015-03-13 11:47:42 -070049
Brian Salomon2ee084e2016-12-16 18:59:19 -050050 /**
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*);
joshualitt5bf99f12015-03-13 11:47:42 -070056
Robert Phillips256c37b2017-03-01 14:32:46 -050057 /**
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);
joshualitt5bf99f12015-03-13 11:47:42 -070077
Brian Salomon2ee084e2016-12-16 18:59:19 -050078 /**
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 */
Brian Salomon9afd3712016-12-01 10:59:09 -050090 bool addToAtlas(AtlasID*, GrDrawOp::Target*, int width, int height, const void* image,
joshualitt5bf99f12015-03-13 11:47:42 -070091 SkIPoint16* loc);
92
Robert Phillips32f28182017-02-28 16:20:03 -050093 GrContext* context() const { return fContext; }
94 sk_sp<GrTextureProxy> getProxy() const { return fProxy; }
joshualitt5bf99f12015-03-13 11:47:42 -070095
joshualitt7c3a2f82015-03-31 13:32:05 -070096 uint64_t atlasGeneration() const { return fAtlasGeneration; }
joshualitt5df175e2015-11-18 13:37:54 -080097
98 inline bool hasID(AtlasID id) {
99 uint32_t index = GetIndexFromID(id);
100 SkASSERT(index < fNumPlots);
101 return fPlotArray[index]->genID() == GetGenerationFromID(id);
102 }
joshualittb4c507e2015-04-08 08:07:59 -0700103
Brian Salomon2ee084e2016-12-16 18:59:19 -0500104 /** 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) {
joshualitt5df175e2015-11-18 13:37:54 -0800106 SkASSERT(this->hasID(id));
107 uint32_t index = GetIndexFromID(id);
108 SkASSERT(index < fNumPlots);
Hal Canary144caf52016-11-07 17:57:18 -0500109 this->makeMRU(fPlotArray[index].get());
Brian Salomon2ee084e2016-12-16 18:59:19 -0500110 fPlotArray[index]->setLastUseToken(token);
joshualitt5df175e2015-11-18 13:37:54 -0800111 }
112
113 inline void registerEvictionCallback(EvictionFunc func, void* userData) {
joshualitt5bf99f12015-03-13 11:47:42 -0700114 EvictionData* data = fEvictionCallbacks.append();
115 data->fFunc = func;
116 data->fData = userData;
117 }
118
Brian Salomon2ee084e2016-12-16 18:59:19 -0500119 /**
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.
joshualittb4c507e2015-04-08 08:07:59 -0700123 */
124 class BulkUseTokenUpdater {
125 public:
joshualitt4314e082015-04-23 08:03:35 -0700126 BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
joshualitt7e97b0b2015-07-31 15:18:08 -0700127 BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
128 : fPlotsToUpdate(that.fPlotsToUpdate)
129 , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
130 }
131
joshualittb4c507e2015-04-08 08:07:59 -0700132 void add(AtlasID id) {
Brian Salomon2ee084e2016-12-16 18:59:19 -0500133 int index = GrDrawOpAtlas::GetIndexFromID(id);
joshualittb4c507e2015-04-08 08:07:59 -0700134 if (!this->find(index)) {
135 this->set(index);
136 }
137 }
138
139 void reset() {
joshualitt4314e082015-04-23 08:03:35 -0700140 fPlotsToUpdate.reset();
joshualittb4c507e2015-04-08 08:07:59 -0700141 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);
joshualitt97202d22015-04-22 13:47:02 -0700153 fPlotsToUpdate.push_back(index);
joshualittb4c507e2015-04-08 08:07:59 -0700154 }
155
156 static const int kMinItems = 4;
157 static const int kMaxPlots = 32;
joshualitt97202d22015-04-22 13:47:02 -0700158 SkSTArray<kMinItems, int, true> fPlotsToUpdate;
joshualitt8672f4d2015-04-21 08:03:04 -0700159 uint32_t fPlotAlreadyUpdated;
joshualittb4c507e2015-04-08 08:07:59 -0700160
Brian Salomon2ee084e2016-12-16 18:59:19 -0500161 friend class GrDrawOpAtlas;
joshualittb4c507e2015-04-08 08:07:59 -0700162 };
163
Brian Salomon2ee084e2016-12-16 18:59:19 -0500164 void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrDrawOpUploadToken token) {
joshualitt5df175e2015-11-18 13:37:54 -0800165 int count = updater.fPlotsToUpdate.count();
166 for (int i = 0; i < count; i++) {
Brian Salomon2ee084e2016-12-16 18:59:19 -0500167 Plot* plot = fPlotArray[updater.fPlotsToUpdate[i]].get();
joshualitt5df175e2015-11-18 13:37:54 -0800168 this->makeMRU(plot);
Brian Salomon2ee084e2016-12-16 18:59:19 -0500169 plot->setLastUseToken(token);
joshualitt5df175e2015-11-18 13:37:54 -0800170 }
171 }
joshualittb4c507e2015-04-08 08:07:59 -0700172
joshualitt010db532015-04-21 10:07:26 -0700173 static const int kGlyphMaxDim = 256;
174 static bool GlyphTooLargeForAtlas(int width, int height) {
175 return width > kGlyphMaxDim || height > kGlyphMaxDim;
176 }
177
joshualitt5bf99f12015-03-13 11:47:42 -0700178private:
Robert Phillips256c37b2017-03-01 14:32:46 -0500179 GrDrawOpAtlas(GrContext*, sk_sp<GrTextureProxy>, int numPlotsX, int numPlotsY);
180
Brian Salomon2ee084e2016-12-16 18:59:19 -0500181 /**
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);
joshualitt5df175e2015-11-18 13:37:54 -0800190
191 public:
Brian Salomon2ee084e2016-12-16 18:59:19 -0500192 /** index() is a unique id for the plot relative to the owning GrAtlas. */
joshualitt5df175e2015-11-18 13:37:54 -0800193 uint32_t index() const { return fIndex; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500194 /**
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 */
joshualitt5df175e2015-11-18 13:37:54 -0800198 uint64_t genID() const { return fGenID; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500199 GrDrawOpAtlas::AtlasID id() const {
200 SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != fID);
joshualitt5df175e2015-11-18 13:37:54 -0800201 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
Brian Salomon2ee084e2016-12-16 18:59:19 -0500207 /**
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 */
Brian Salomon9afd3712016-12-01 10:59:09 -0500214 GrDrawOpUploadToken lastUploadToken() const { return fLastUpload; }
215 GrDrawOpUploadToken lastUseToken() const { return fLastUse; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500216 void setLastUploadToken(GrDrawOpUploadToken token) { fLastUpload = token; }
217 void setLastUseToken(GrDrawOpUploadToken token) { fLastUse = token; }
joshualitt5df175e2015-11-18 13:37:54 -0800218
Brian Salomon9afd3712016-12-01 10:59:09 -0500219 void uploadToTexture(GrDrawOp::WritePixelsFn&, GrTexture* texture);
joshualitt5df175e2015-11-18 13:37:54 -0800220 void resetRects();
221
222 private:
Brian Salomon2ee084e2016-12-16 18:59:19 -0500223 Plot(int index, uint64_t genID, int offX, int offY, int width, int height,
224 GrPixelConfig config);
joshualitt5df175e2015-11-18 13:37:54 -0800225
Brian Salomon2ee084e2016-12-16 18:59:19 -0500226 ~Plot() override;
joshualitt5df175e2015-11-18 13:37:54 -0800227
Brian Salomon2ee084e2016-12-16 18:59:19 -0500228 /**
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);
joshualitt5df175e2015-11-18 13:37:54 -0800234 }
235
Brian Salomon2ee084e2016-12-16 18:59:19 -0500236 static GrDrawOpAtlas::AtlasID CreateId(uint32_t index, uint64_t generation) {
joshualitt5df175e2015-11-18 13:37:54 -0800237 SkASSERT(index < (1 << 16));
238 SkASSERT(generation < ((uint64_t)1 << 48));
239 return generation << 16 | index;
240 }
241
Brian Salomon9afd3712016-12-01 10:59:09 -0500242 GrDrawOpUploadToken fLastUpload;
243 GrDrawOpUploadToken fLastUse;
joshualitt5df175e2015-11-18 13:37:54 -0800244
Brian Salomon2ee084e2016-12-16 18:59:19 -0500245 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);
joshualitt5df175e2015-11-18 13:37:54 -0800259
Brian Salomon2ee084e2016-12-16 18:59:19 -0500260 friend class GrDrawOpAtlas;
joshualitt5df175e2015-11-18 13:37:54 -0800261
262 typedef SkRefCnt INHERITED;
263 };
264
Brian Salomon2ee084e2016-12-16 18:59:19 -0500265 typedef SkTInternalLList<Plot> PlotList;
robertphillips2b0536f2015-11-06 14:10:42 -0800266
joshualitt8db6fdc2015-07-31 08:25:07 -0700267 static uint32_t GetIndexFromID(AtlasID id) {
joshualitt5bf99f12015-03-13 11:47:42 -0700268 return id & 0xffff;
269 }
270
joshualitt8db6fdc2015-07-31 08:25:07 -0700271 // top 48 bits are reserved for the generation ID
272 static uint64_t GetGenerationFromID(AtlasID id) {
273 return (id >> 16) & 0xffffffffffff;
joshualitt5bf99f12015-03-13 11:47:42 -0700274 }
275
Robert Phillips256c37b2017-03-01 14:32:46 -0500276 inline bool updatePlot(GrDrawOp::Target*, AtlasID*, Plot*);
joshualitt5bf99f12015-03-13 11:47:42 -0700277
Brian Salomon2ee084e2016-12-16 18:59:19 -0500278 inline void makeMRU(Plot* plot) {
joshualitt5df175e2015-11-18 13:37:54 -0800279 if (fPlotList.head() == plot) {
280 return;
281 }
282
283 fPlotList.remove(plot);
284 fPlotList.addToHead(plot);
285 }
joshualitt5bf99f12015-03-13 11:47:42 -0700286
287 inline void processEviction(AtlasID);
288
Robert Phillips32f28182017-02-28 16:20:03 -0500289 GrContext* fContext;
290 sk_sp<GrTextureProxy> fProxy;
291 int fPlotWidth;
292 int fPlotHeight;
293 SkDEBUGCODE(uint32_t fNumPlots;)
robertphillips2b0536f2015-11-06 14:10:42 -0800294
Robert Phillips32f28182017-02-28 16:20:03 -0500295 uint64_t fAtlasGeneration;
joshualitt5bf99f12015-03-13 11:47:42 -0700296
297 struct EvictionData {
298 EvictionFunc fFunc;
299 void* fData;
300 };
301
302 SkTDArray<EvictionData> fEvictionCallbacks;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500303 // 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;
joshualitt5bf99f12015-03-13 11:47:42 -0700307};
308
309#endif