blob: 7a7da6c9be732e324ae3604f2017a18847eff936 [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
11#include "GrTexture.h"
12#include "SkPoint.h"
13#include "SkTDArray.h"
14#include "SkTInternalLList.h"
15
Brian Salomon89527432016-12-16 09:52:16 -050016#include "ops/GrDrawOp.h"
joshualittddd22d82016-02-16 06:47:52 -080017
joshualitt5bf99f12015-03-13 11:47:42 -070018class GrRectanizer;
19
Brian Salomon2ee084e2016-12-16 18:59:19 -050020struct GrDrawOpAtlasConfig {
joshualittda04e0e2015-08-19 08:16:43 -070021 int numPlotsX() const { return fWidth / fPlotWidth; }
22 int numPlotsY() const { return fHeight / fPlotWidth; }
23 int fWidth;
24 int fHeight;
jvanverth7023a002016-02-22 11:25:32 -080025 int fLog2Width;
26 int fLog2Height;
joshualittda04e0e2015-08-19 08:16:43 -070027 int fPlotWidth;
28 int fPlotHeight;
29};
30
Brian Salomon2ee084e2016-12-16 18:59:19 -050031/**
32 * This class manages an atlas texture on behalf of GrDrawOps. The draw ops that use the atlas
33 * perform texture uploads when preparing their draws during flush. The class provides facilities
34 * for using GrDrawOpUploadToken to detect data hazards. Op's uploads are performed in "asap" mode
35 * until it is impossible to add data without overwriting texels read by draws that have not yet
36 * executed on the gpu. At that point the uploads are performed "inline" between draws. If a single
37 * draw would use enough subimage space to overflow the atlas texture then the atlas will fail to
38 * add a subimage. This gives the op the chance to end the draw and begin a new one. Additional
39 * uploads will then succeed in inline mode.
40 */
41class GrDrawOpAtlas {
joshualitt5bf99f12015-03-13 11:47:42 -070042public:
Brian Salomon2ee084e2016-12-16 18:59:19 -050043 /**
44 * An AtlasID is an opaque handle which callers can use to determine if the atlas contains
45 * a specific piece of data.
46 */
joshualitt8db6fdc2015-07-31 08:25:07 -070047 typedef uint64_t AtlasID;
joshualitt7c3a2f82015-03-31 13:32:05 -070048 static const uint32_t kInvalidAtlasID = 0;
49 static const uint64_t kInvalidAtlasGeneration = 0;
joshualitt5bf99f12015-03-13 11:47:42 -070050
Brian Salomon2ee084e2016-12-16 18:59:19 -050051 /**
52 * A function pointer for use as a callback during eviction. Whenever GrDrawOpAtlas evicts a
53 * specific AtlasID, it will call all of the registered listeners so they can process the
54 * eviction.
55 */
56 typedef void (*EvictionFunc)(GrDrawOpAtlas::AtlasID, void*);
joshualitt5bf99f12015-03-13 11:47:42 -070057
Brian Salomon2ee084e2016-12-16 18:59:19 -050058 GrDrawOpAtlas(sk_sp<GrTexture>, int numPlotsX, int numPlotsY);
joshualitt5bf99f12015-03-13 11:47:42 -070059
Brian Salomon2ee084e2016-12-16 18:59:19 -050060 /**
61 * Adds a width x height subimage to the atlas. Upon success it returns an ID and the subimage's
62 * coordinates in the backing texture. False is returned if the subimage cannot fit in the
63 * atlas without overwriting texels that will be read in the current draw. This indicates that
64 * the op should end its current draw and begin another before adding more data. Upon success,
65 * an upload of the provided image data will have been added to the GrDrawOp::Target, in "asap"
66 * mode if possible, otherwise in "inline" mode. Successive uploads in either mode may be
67 * consolidated.
68 * NOTE: When the GrDrawOp prepares a draw that reads from the atlas, it must immediately call
69 * 'setUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to
70 * addToAtlas might cause the previous data to be overwritten before it has been read.
71 */
Brian Salomon9afd3712016-12-01 10:59:09 -050072 bool addToAtlas(AtlasID*, GrDrawOp::Target*, int width, int height, const void* image,
joshualitt5bf99f12015-03-13 11:47:42 -070073 SkIPoint16* loc);
74
Ben Wagner594f9ed2016-11-08 14:13:39 -050075 GrTexture* getTexture() const { return fTexture.get(); }
joshualitt5bf99f12015-03-13 11:47:42 -070076
joshualitt7c3a2f82015-03-31 13:32:05 -070077 uint64_t atlasGeneration() const { return fAtlasGeneration; }
joshualitt5df175e2015-11-18 13:37:54 -080078
79 inline bool hasID(AtlasID id) {
80 uint32_t index = GetIndexFromID(id);
81 SkASSERT(index < fNumPlots);
82 return fPlotArray[index]->genID() == GetGenerationFromID(id);
83 }
joshualittb4c507e2015-04-08 08:07:59 -070084
Brian Salomon2ee084e2016-12-16 18:59:19 -050085 /** To ensure the atlas does not evict a given entry, the client must set the last use token. */
86 inline void setLastUseToken(AtlasID id, GrDrawOpUploadToken token) {
joshualitt5df175e2015-11-18 13:37:54 -080087 SkASSERT(this->hasID(id));
88 uint32_t index = GetIndexFromID(id);
89 SkASSERT(index < fNumPlots);
Hal Canary144caf52016-11-07 17:57:18 -050090 this->makeMRU(fPlotArray[index].get());
Brian Salomon2ee084e2016-12-16 18:59:19 -050091 fPlotArray[index]->setLastUseToken(token);
joshualitt5df175e2015-11-18 13:37:54 -080092 }
93
94 inline void registerEvictionCallback(EvictionFunc func, void* userData) {
joshualitt5bf99f12015-03-13 11:47:42 -070095 EvictionData* data = fEvictionCallbacks.append();
96 data->fFunc = func;
97 data->fData = userData;
98 }
99
Brian Salomon2ee084e2016-12-16 18:59:19 -0500100 /**
101 * A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk. The
102 * current max number of plots the GrDrawOpAtlas can handle is 32. If in the future this is
103 * insufficient then we can move to a 64 bit int.
joshualittb4c507e2015-04-08 08:07:59 -0700104 */
105 class BulkUseTokenUpdater {
106 public:
joshualitt4314e082015-04-23 08:03:35 -0700107 BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
joshualitt7e97b0b2015-07-31 15:18:08 -0700108 BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
109 : fPlotsToUpdate(that.fPlotsToUpdate)
110 , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
111 }
112
joshualittb4c507e2015-04-08 08:07:59 -0700113 void add(AtlasID id) {
Brian Salomon2ee084e2016-12-16 18:59:19 -0500114 int index = GrDrawOpAtlas::GetIndexFromID(id);
joshualittb4c507e2015-04-08 08:07:59 -0700115 if (!this->find(index)) {
116 this->set(index);
117 }
118 }
119
120 void reset() {
joshualitt4314e082015-04-23 08:03:35 -0700121 fPlotsToUpdate.reset();
joshualittb4c507e2015-04-08 08:07:59 -0700122 fPlotAlreadyUpdated = 0;
123 }
124
125 private:
126 bool find(int index) const {
127 SkASSERT(index < kMaxPlots);
128 return (fPlotAlreadyUpdated >> index) & 1;
129 }
130
131 void set(int index) {
132 SkASSERT(!this->find(index));
133 fPlotAlreadyUpdated = fPlotAlreadyUpdated | (1 << index);
joshualitt97202d22015-04-22 13:47:02 -0700134 fPlotsToUpdate.push_back(index);
joshualittb4c507e2015-04-08 08:07:59 -0700135 }
136
137 static const int kMinItems = 4;
138 static const int kMaxPlots = 32;
joshualitt97202d22015-04-22 13:47:02 -0700139 SkSTArray<kMinItems, int, true> fPlotsToUpdate;
joshualitt8672f4d2015-04-21 08:03:04 -0700140 uint32_t fPlotAlreadyUpdated;
joshualittb4c507e2015-04-08 08:07:59 -0700141
Brian Salomon2ee084e2016-12-16 18:59:19 -0500142 friend class GrDrawOpAtlas;
joshualittb4c507e2015-04-08 08:07:59 -0700143 };
144
Brian Salomon2ee084e2016-12-16 18:59:19 -0500145 void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrDrawOpUploadToken token) {
joshualitt5df175e2015-11-18 13:37:54 -0800146 int count = updater.fPlotsToUpdate.count();
147 for (int i = 0; i < count; i++) {
Brian Salomon2ee084e2016-12-16 18:59:19 -0500148 Plot* plot = fPlotArray[updater.fPlotsToUpdate[i]].get();
joshualitt5df175e2015-11-18 13:37:54 -0800149 this->makeMRU(plot);
Brian Salomon2ee084e2016-12-16 18:59:19 -0500150 plot->setLastUseToken(token);
joshualitt5df175e2015-11-18 13:37:54 -0800151 }
152 }
joshualittb4c507e2015-04-08 08:07:59 -0700153
joshualitt010db532015-04-21 10:07:26 -0700154 static const int kGlyphMaxDim = 256;
155 static bool GlyphTooLargeForAtlas(int width, int height) {
156 return width > kGlyphMaxDim || height > kGlyphMaxDim;
157 }
158
joshualitt5bf99f12015-03-13 11:47:42 -0700159private:
Brian Salomon2ee084e2016-12-16 18:59:19 -0500160 /**
161 * The backing GrTexture for a GrDrawOpAtlas is broken into a spatial grid of Plots. The Plots
162 * keep track of subimage placement via their GrRectanizer. A Plot manages the lifetime of its
163 * data using two tokens, a last use token and a last upload token. Once a Plot is "full" (i.e.
164 * there is no room for the new subimage according to the GrRectanizer), it can no longer be
165 * used unless the last use of the Plot has already been flushed through to the gpu.
166 */
167 class Plot : public SkRefCnt {
168 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot);
joshualitt5df175e2015-11-18 13:37:54 -0800169
170 public:
Brian Salomon2ee084e2016-12-16 18:59:19 -0500171 /** index() is a unique id for the plot relative to the owning GrAtlas. */
joshualitt5df175e2015-11-18 13:37:54 -0800172 uint32_t index() const { return fIndex; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500173 /**
174 * genID() is incremented when the plot is evicted due to a atlas spill. It is used to know
175 * if a particular subimage is still present in the atlas.
176 */
joshualitt5df175e2015-11-18 13:37:54 -0800177 uint64_t genID() const { return fGenID; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500178 GrDrawOpAtlas::AtlasID id() const {
179 SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != fID);
joshualitt5df175e2015-11-18 13:37:54 -0800180 return fID;
181 }
182 SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
183
184 bool addSubImage(int width, int height, const void* image, SkIPoint16* loc);
185
Brian Salomon2ee084e2016-12-16 18:59:19 -0500186 /**
187 * To manage the lifetime of a plot, we use two tokens. We use the last upload token to
188 * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to
189 * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We
190 * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use
191 * has already flushed through the gpu then we can reuse the plot.
192 */
Brian Salomon9afd3712016-12-01 10:59:09 -0500193 GrDrawOpUploadToken lastUploadToken() const { return fLastUpload; }
194 GrDrawOpUploadToken lastUseToken() const { return fLastUse; }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500195 void setLastUploadToken(GrDrawOpUploadToken token) { fLastUpload = token; }
196 void setLastUseToken(GrDrawOpUploadToken token) { fLastUse = token; }
joshualitt5df175e2015-11-18 13:37:54 -0800197
Brian Salomon9afd3712016-12-01 10:59:09 -0500198 void uploadToTexture(GrDrawOp::WritePixelsFn&, GrTexture* texture);
joshualitt5df175e2015-11-18 13:37:54 -0800199 void resetRects();
200
201 private:
Brian Salomon2ee084e2016-12-16 18:59:19 -0500202 Plot(int index, uint64_t genID, int offX, int offY, int width, int height,
203 GrPixelConfig config);
joshualitt5df175e2015-11-18 13:37:54 -0800204
Brian Salomon2ee084e2016-12-16 18:59:19 -0500205 ~Plot() override;
joshualitt5df175e2015-11-18 13:37:54 -0800206
Brian Salomon2ee084e2016-12-16 18:59:19 -0500207 /**
208 * Create a clone of this plot. The cloned plot will take the place of the current plot in
209 * the atlas
210 */
211 Plot* clone() const {
212 return new Plot(fIndex, fGenID + 1, fX, fY, fWidth, fHeight, fConfig);
joshualitt5df175e2015-11-18 13:37:54 -0800213 }
214
Brian Salomon2ee084e2016-12-16 18:59:19 -0500215 static GrDrawOpAtlas::AtlasID CreateId(uint32_t index, uint64_t generation) {
joshualitt5df175e2015-11-18 13:37:54 -0800216 SkASSERT(index < (1 << 16));
217 SkASSERT(generation < ((uint64_t)1 << 48));
218 return generation << 16 | index;
219 }
220
Brian Salomon9afd3712016-12-01 10:59:09 -0500221 GrDrawOpUploadToken fLastUpload;
222 GrDrawOpUploadToken fLastUse;
joshualitt5df175e2015-11-18 13:37:54 -0800223
Brian Salomon2ee084e2016-12-16 18:59:19 -0500224 const uint32_t fIndex;
225 uint64_t fGenID;
226 GrDrawOpAtlas::AtlasID fID;
227 unsigned char* fData;
228 const int fWidth;
229 const int fHeight;
230 const int fX;
231 const int fY;
232 GrRectanizer* fRects;
233 const SkIPoint16 fOffset; // the offset of the plot in the backing texture
234 const GrPixelConfig fConfig;
235 const size_t fBytesPerPixel;
236 SkIRect fDirtyRect;
237 SkDEBUGCODE(bool fDirty);
joshualitt5df175e2015-11-18 13:37:54 -0800238
Brian Salomon2ee084e2016-12-16 18:59:19 -0500239 friend class GrDrawOpAtlas;
joshualitt5df175e2015-11-18 13:37:54 -0800240
241 typedef SkRefCnt INHERITED;
242 };
243
Brian Salomon2ee084e2016-12-16 18:59:19 -0500244 typedef SkTInternalLList<Plot> PlotList;
robertphillips2b0536f2015-11-06 14:10:42 -0800245
joshualitt8db6fdc2015-07-31 08:25:07 -0700246 static uint32_t GetIndexFromID(AtlasID id) {
joshualitt5bf99f12015-03-13 11:47:42 -0700247 return id & 0xffff;
248 }
249
joshualitt8db6fdc2015-07-31 08:25:07 -0700250 // top 48 bits are reserved for the generation ID
251 static uint64_t GetGenerationFromID(AtlasID id) {
252 return (id >> 16) & 0xffffffffffff;
joshualitt5bf99f12015-03-13 11:47:42 -0700253 }
254
Brian Salomon2ee084e2016-12-16 18:59:19 -0500255 inline void updatePlot(GrDrawOp::Target*, AtlasID*, Plot*);
joshualitt5bf99f12015-03-13 11:47:42 -0700256
Brian Salomon2ee084e2016-12-16 18:59:19 -0500257 inline void makeMRU(Plot* plot) {
joshualitt5df175e2015-11-18 13:37:54 -0800258 if (fPlotList.head() == plot) {
259 return;
260 }
261
262 fPlotList.remove(plot);
263 fPlotList.addToHead(plot);
264 }
joshualitt5bf99f12015-03-13 11:47:42 -0700265
266 inline void processEviction(AtlasID);
267
Ben Wagner594f9ed2016-11-08 14:13:39 -0500268 sk_sp<GrTexture> fTexture;
269 int fPlotWidth;
270 int fPlotHeight;
robertphillips2b0536f2015-11-06 14:10:42 -0800271 SkDEBUGCODE(uint32_t fNumPlots;)
272
joshualitt7c3a2f82015-03-31 13:32:05 -0700273 uint64_t fAtlasGeneration;
joshualitt5bf99f12015-03-13 11:47:42 -0700274
275 struct EvictionData {
276 EvictionFunc fFunc;
277 void* fData;
278 };
279
280 SkTDArray<EvictionData> fEvictionCallbacks;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500281 // allocated array of Plots
282 std::unique_ptr<sk_sp<Plot>[]> fPlotArray;
283 // LRU list of Plots (MRU at head - LRU at tail)
284 PlotList fPlotList;
joshualitt5bf99f12015-03-13 11:47:42 -0700285};
286
287#endif