Flutter Engine
The Flutter Engine
GrDrawOpAtlas.h
Go to the documentation of this file.
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 <cmath>
12#include <vector>
13
15#include "src/core/SkIPoint16.h"
16#include "src/gpu/AtlasTypes.h"
20
22class GrProxyProvider;
24class GrTextureProxy;
25
26/**
27 * This class manages one or more atlas textures on behalf of GrDrawOps. The draw ops that use the
28 * atlas perform texture uploads when preparing their draws during flush. The class provides
29 * facilities for using GrDrawOpUploadToken to detect data hazards. Op's uploads are performed in
30 * "ASAP" mode until it is impossible to add data without overwriting texels read by draws that
31 * have not yet executed on the gpu. At that point, the atlas will attempt to allocate a new
32 * atlas texture (or "page") of the same size, up to a maximum number of textures, and upload
33 * to that texture. If that's not possible, the uploads are performed "inline" between draws. If a
34 * single draw would use enough subimage space to overflow the atlas texture then the atlas will
35 * fail to add a subimage. This gives the op the chance to end the draw and begin a new one.
36 * Additional uploads will then succeed in inline mode.
37 *
38 * When the atlas has multiple pages, new uploads are prioritized to the lower index pages, i.e.,
39 * it will try to upload to page 0 before page 1 or 2. To keep the atlas from continually using
40 * excess space, periodic garbage collection is needed to shift data from the higher index pages to
41 * the lower ones, and then eventually remove any pages that are no longer in use. "In use" is
42 * determined by using the GrDrawUploadToken system: After a flush each subarea of the page
43 * is checked to see whether it was used in that flush. If less than a quarter of the plots have
44 * been used recently (within kPlotRecentlyUsedCount iterations) and there are available
45 * plots in lower index pages, the higher index page will be deactivated, and its glyphs will
46 * gradually migrate to other pages via the usual upload system.
47 *
48 * Garbage collection is initiated by the GrDrawOpAtlas's client via the compact() method. One
49 * solution is to make the client a subclass of GrOnFlushCallbackObject, register it with the
50 * GrContext via addOnFlushCallbackObject(), and the client's postFlush() method calls compact()
51 * and passes in the given GrDrawUploadToken.
52 */
54public:
55 /** Is the atlas allowed to use more than one texture? */
56 enum class AllowMultitexturing : bool { kNo, kYes };
57
58 /**
59 * Returns a GrDrawOpAtlas. This function can be called anywhere, but the returned atlas
60 * should only be used inside of GrMeshDrawOp::onPrepareDraws.
61 * @param proxyProvider Used to create the atlas's texture proxies.
62 * @param format Backend format for the atlas's textures.
63 * Should be compatible with ct.
64 * @param ct The colorType which this atlas will store.
65 * @param bpp Size in bytes of each pixel.
66 * @param width Width in pixels of the atlas.
67 * @param height Height in pixels of the atlas.
68 * @param plotWidth The width of each plot. width/plotWidth should be an integer.
69 * @param plotWidth The height of each plot. height/plotHeight should be an integer.
70 * @param generationCounter A pointer to the context's generation counter.
71 * @param allowMultitexturing Can the atlas use more than one texture.
72 * @param evictor A pointer to an eviction callback class.
73 * @param label A label for the atlas texture.
74 *
75 * @return An initialized DrawAtlas, or nullptr if creation fails.
76 */
77 static std::unique_ptr<GrDrawOpAtlas> Make(GrProxyProvider* proxyProvider,
79 SkColorType ct, size_t bpp,
80 int width, int height,
81 int plotWidth, int plotHeight,
82 skgpu::AtlasGenerationCounter* generationCounter,
83 AllowMultitexturing allowMultitexturing,
85 std::string_view label);
86
87 /**
88 * Adds a width x height subimage to the atlas. Upon success it returns 'kSucceeded' and returns
89 * the ID and the subimage's coordinates in the backing texture. 'kTryAgain' is returned if
90 * the subimage cannot fit in the atlas without overwriting texels that will be read in the
91 * current draw. This indicates that the op should end its current draw and begin another
92 * before adding more data. Upon success, an upload of the provided image data will have
93 * been added to the GrDrawOp::Target, in "asap" mode if possible, otherwise in "inline" mode.
94 * Successive uploads in either mode may be consolidated.
95 * 'kError' will be returned when some unrecoverable error was encountered while trying to
96 * add the subimage. In this case the op being created should be discarded.
97 *
98 * NOTE: When the GrDrawOp prepares a draw that reads from the atlas, it must immediately call
99 * 'setLastUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to
100 * addToAtlas might cause the previous data to be overwritten before it has been read.
101 */
102
103 enum class ErrorCode {
104 kError,
105 kSucceeded,
106 kTryAgain
107 };
108
110 int width, int height, const void* image, skgpu::AtlasLocator*);
111
112 const GrSurfaceProxyView* getViews() const { return fViews; }
113
114 uint64_t atlasGeneration() const { return fAtlasGeneration; }
115
116 bool hasID(const skgpu::PlotLocator& plotLocator) {
117 if (!plotLocator.isValid()) {
118 return false;
119 }
120
121 uint32_t plot = plotLocator.plotIndex();
122 uint32_t page = plotLocator.pageIndex();
123 uint64_t plotGeneration = fPages[page].fPlotArray[plot]->genID();
124 uint64_t locatorGeneration = plotLocator.genID();
125 return plot < fNumPlots && page < fNumActivePages && plotGeneration == locatorGeneration;
126 }
127
128 /** To ensure the atlas does not evict a given entry, the client must set the last use token. */
129 void setLastUseToken(const skgpu::AtlasLocator& atlasLocator, skgpu::AtlasToken token) {
130 SkASSERT(this->hasID(atlasLocator.plotLocator()));
131 uint32_t plotIdx = atlasLocator.plotIndex();
132 SkASSERT(plotIdx < fNumPlots);
133 uint32_t pageIdx = atlasLocator.pageIndex();
134 SkASSERT(pageIdx < fNumActivePages);
135 skgpu::Plot* plot = fPages[pageIdx].fPlotArray[plotIdx].get();
136 this->makeMRU(plot, pageIdx);
137 plot->setLastUseToken(token);
138 }
139
140 uint32_t numActivePages() { return fNumActivePages; }
141
143 skgpu::AtlasToken token) {
144 int count = updater.count();
145 for (int i = 0; i < count; i++) {
147 // it's possible we've added a plot to the updater and subsequently the plot's page
148 // was deleted -- so we check to prevent a crash
149 if (pd.fPageIndex < fNumActivePages) {
150 skgpu::Plot* plot = fPages[pd.fPageIndex].fPlotArray[pd.fPlotIndex].get();
151 this->makeMRU(plot, pd.fPageIndex);
152 plot->setLastUseToken(token);
153 }
154 }
155 }
156
157 void compact(skgpu::AtlasToken startTokenForNextFlush);
158
160
161 uint32_t maxPages() const {
162 return fMaxPages;
163 }
164
165private:
166 friend class GrDrawOpAtlasTools;
167
169 int width, int height, int plotWidth, int plotHeight,
170 skgpu::AtlasGenerationCounter* generationCounter,
171 AllowMultitexturing allowMultitexturing, std::string_view label);
172
173 inline bool updatePlot(GrDeferredUploadTarget*, skgpu::AtlasLocator*, skgpu::Plot*);
174
175 inline void makeMRU(skgpu::Plot* plot, uint32_t pageIdx) {
176 if (fPages[pageIdx].fPlotList.head() == plot) {
177 return;
178 }
179
180 fPages[pageIdx].fPlotList.remove(plot);
181 fPages[pageIdx].fPlotList.addToHead(plot);
182
183 // No MRU update for pages -- since we will always try to add from
184 // the front and remove from the back there is no need for MRU.
185 }
186
187 bool uploadToPage(unsigned int pageIdx, GrDeferredUploadTarget*, int width, int height,
188 const void* image, skgpu::AtlasLocator*);
189
190 void uploadPlotToTexture(GrDeferredTextureUploadWritePixelsFn& writePixels,
191 GrTextureProxy* proxy,
193
195 bool activateNewPage(GrResourceProvider*);
196 void deactivateLastPage();
197
198 void processEviction(skgpu::PlotLocator);
199 inline void processEvictionAndResetRects(skgpu::Plot* plot) {
200 this->processEviction(plot->plotLocator());
201 plot->resetRects();
202 }
203
204 GrBackendFormat fFormat;
206 size_t fBytesPerPixel;
207 int fTextureWidth;
208 int fTextureHeight;
209 int fPlotWidth;
210 int fPlotHeight;
211 unsigned int fNumPlots;
212 const std::string fLabel;
213
214 // A counter to track the atlas eviction state for Glyphs. Each Glyph has a PlotLocator
215 // which contains its current generation. When the atlas evicts a plot, it increases
216 // the generation counter. If a Glyph's generation is less than the atlas's
217 // generation, then it knows it's been evicted and is either free to be deleted or
218 // re-added to the atlas if necessary.
219 skgpu::AtlasGenerationCounter* const fGenerationCounter;
220 uint64_t fAtlasGeneration;
221
222 // nextFlushToken() value at the end of the previous flush
223 skgpu::AtlasToken fPrevFlushToken;
224
225 // the number of flushes since this atlas has been last used
226 int fFlushesSinceLastUse;
227
228 std::vector<skgpu::PlotEvictionCallback*> fEvictionCallbacks;
229
230 struct Page {
231 // allocated array of Plots
232 std::unique_ptr<sk_sp<skgpu::Plot>[]> fPlotArray;
233 // LRU list of Plots (MRU at head - LRU at tail)
234 skgpu::PlotList fPlotList;
235 };
236 // proxies kept separate to make it easier to pass them up to client
239 uint32_t fMaxPages;
240
241 uint32_t fNumActivePages;
242
243 SkDEBUGCODE(void validate(const skgpu::AtlasLocator& atlasLocator) const;)
244};
245
246// There are three atlases (A8, 565, ARGB) that are kept in relation with one another. In
247// general, because A8 is the most frequently used mask format its dimensions are 2x the 565 and
248// ARGB dimensions, with the constraint that an atlas size will always contain at least one plot.
249// Since the ARGB atlas takes the most space, its dimensions are used to size the other two atlases.
251public:
252 // The capabilities of the GPU define maxTextureSize. The client provides maxBytes, and this
253 // represents the largest they want a single atlas texture to be. Due to multitexturing, we
254 // may expand temporarily to use more space as needed.
255 GrDrawOpAtlasConfig(int maxTextureSize, size_t maxBytes);
256
257 // For testing only - make minimum sized atlases -- a single plot for ARGB, four for A8
259
262
263private:
264 // On some systems texture coordinates are represented using half-precision floating point
265 // with 11 significant bits, which limits the largest atlas dimensions to 2048x2048.
266 // For simplicity we'll use this constraint for all of our atlas textures.
267 // This can be revisited later if we need larger atlases.
268 inline static constexpr int kMaxAtlasDim = 2048;
269
270 SkISize fARGBDimensions;
271 int fMaxTextureSize;
272};
273
274#endif
int count
Definition: FontMgrTest.cpp:50
std::function< bool(GrTextureProxy *, SkIRect, GrColorType srcColorType, const void *, size_t rowBytes)> GrDeferredTextureUploadWritePixelsFn
SkColorType fColorType
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorType
Definition: SkColorType.h:19
@ kYes
Do pre-clip the geometry before applying the (perspective) matrix.
@ kNo
Don't pre-clip the geometry before applying the (perspective) matrix.
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
GLenum type
SkISize atlasDimensions(skgpu::MaskFormat type) const
SkISize plotDimensions(skgpu::MaskFormat type) const
void compact(skgpu::AtlasToken startTokenForNextFlush)
uint64_t atlasGeneration() const
uint32_t numActivePages()
static std::unique_ptr< GrDrawOpAtlas > Make(GrProxyProvider *proxyProvider, const GrBackendFormat &format, SkColorType ct, size_t bpp, int width, int height, int plotWidth, int plotHeight, skgpu::AtlasGenerationCounter *generationCounter, AllowMultitexturing allowMultitexturing, skgpu::PlotEvictionCallback *evictor, std::string_view label)
const GrSurfaceProxyView * getViews() const
void setLastUseToken(const skgpu::AtlasLocator &atlasLocator, skgpu::AtlasToken token)
bool hasID(const skgpu::PlotLocator &plotLocator)
void setLastUseTokenBulk(const skgpu::BulkUsePlotUpdater &updater, skgpu::AtlasToken token)
ErrorCode addToAtlas(GrResourceProvider *, GrDeferredUploadTarget *, int width, int height, const void *image, skgpu::AtlasLocator *)
void instantiate(GrOnFlushResourceProvider *)
uint32_t maxPages() const
uint32_t plotIndex() const
Definition: AtlasTypes.h:305
PlotLocator plotLocator() const
Definition: AtlasTypes.h:301
uint32_t pageIndex() const
Definition: AtlasTypes.h:303
const PlotData & plotData(int index) const
Definition: AtlasTypes.h:410
static constexpr auto kMaxMultitexturePages
Definition: AtlasTypes.h:245
uint64_t genID() const
Definition: AtlasTypes.h:280
uint32_t plotIndex() const
Definition: AtlasTypes.h:279
bool isValid() const
Definition: AtlasTypes.h:262
uint32_t pageIndex() const
Definition: AtlasTypes.h:278
uint32_t uint32_t * format
sk_sp< const SkImage > image
Definition: SkRecords.h:269
MaskFormat
Definition: AtlasTypes.h:98
static void plot(SkCanvas *canvas, const char *fn, float xMin, float xMax, float yMin, float yMax, const char *label=nullptr, bool requireES3=false)
int32_t height
int32_t width
Definition: SkSize.h:16