2 * Copyright 2022 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #ifndef skgpu_AtlasTypes_DEFINED
9 #define skgpu_AtlasTypes_DEFINED
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/SkTArray.h"
18 #include "include/private/SkTo.h"
19 #include "src/core/SkIPoint16.h"
20 #include "src/core/SkTInternalLList.h"
21 #include "src/gpu/RectanizerSkyline.h"
24 class TestingUploadTarget;
27 * This file includes internal types that are used by all of our gpu backends for atlases.
33 int16_t fLeft, fTop, fRight, fBottom;
35 static IRect16 SK_WARN_UNUSED_RESULT MakeEmpty() {
41 static IRect16 SK_WARN_UNUSED_RESULT MakeWH(int16_t w, int16_t h) {
47 static IRect16 SK_WARN_UNUSED_RESULT MakeXYWH(int16_t x, int16_t y, int16_t w, int16_t h) {
49 r.set(x, y, x + w, y + h);
53 static IRect16 SK_WARN_UNUSED_RESULT Make(const SkIRect& ir) {
59 int width() const { return fRight - fLeft; }
60 int height() const { return fBottom - fTop; }
61 int area() const { return this->width() * this->height(); }
62 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
64 void setEmpty() { memset(this, 0, sizeof(*this)); }
66 void set(int16_t left, int16_t top, int16_t right, int16_t bottom) {
73 void set(const SkIRect& r) {
74 fLeft = SkToS16(r.fLeft);
75 fTop = SkToS16(r.fTop);
76 fRight = SkToS16(r.fRight);
77 fBottom = SkToS16(r.fBottom);
80 void offset(int16_t dx, int16_t dy) {
89 * Formats for masks, used by the font cache. Important that these are 0-based.
91 enum class MaskFormat : int {
92 kA8, //!< 1-byte per pixel
93 kA565, //!< 2-bytes per pixel, RGB represent 3-channel LCD coverage
94 kARGB, //!< 4-bytes per pixel, color format
98 static const int kMaskFormatCount = static_cast<int>(MaskFormat::kLast) + 1;
101 * Return the number of bytes-per-pixel for the specified mask format.
103 inline constexpr int MaskFormatBytesPerPixel(MaskFormat format) {
104 SkASSERT(static_cast<int>(format) < kMaskFormatCount);
108 static_assert(static_cast<int>(MaskFormat::kA8) == 0, "enum_order_dependency");
109 static_assert(static_cast<int>(MaskFormat::kA565) == 1, "enum_order_dependency");
110 static_assert(static_cast<int>(MaskFormat::kARGB) == 2, "enum_order_dependency");
112 return SkTo<int>(1u << static_cast<int>(format));
115 static constexpr SkColorType MaskFormatToColorType(MaskFormat format) {
117 case MaskFormat::kA8:
118 return kAlpha_8_SkColorType;
119 case MaskFormat::kA565:
120 return kRGB_565_SkColorType;
121 case MaskFormat::kARGB:
122 return kRGBA_8888_SkColorType;
128 * Keep track of generation number for atlases and Plots.
130 class AtlasGenerationCounter {
132 inline static constexpr uint64_t kInvalidGeneration = 0;
134 return fGeneration++;
138 uint64_t fGeneration{1};
142 * DrawToken is used to sequence uploads relative to each other and to batches of draws.
146 static DrawToken AlreadyFlushedToken() { return DrawToken(0); }
148 DrawToken(const DrawToken&) = default;
149 DrawToken& operator=(const DrawToken&) = default;
151 bool operator==(const DrawToken& that) const {
152 return fSequenceNumber == that.fSequenceNumber;
154 bool operator!=(const DrawToken& that) const { return !(*this == that); }
155 bool operator<(const DrawToken that) const {
156 return fSequenceNumber < that.fSequenceNumber;
158 bool operator<=(const DrawToken that) const {
159 return fSequenceNumber <= that.fSequenceNumber;
161 bool operator>(const DrawToken that) const {
162 return fSequenceNumber > that.fSequenceNumber;
164 bool operator>=(const DrawToken that) const {
165 return fSequenceNumber >= that.fSequenceNumber;
168 DrawToken& operator++() {
172 DrawToken operator++(int) {
173 auto old = fSequenceNumber;
175 return DrawToken(old);
178 DrawToken next() const { return DrawToken(fSequenceNumber + 1); }
180 /** Is this token in the [start, end] inclusive interval? */
181 bool inInterval(const DrawToken& start, const DrawToken& end) {
182 return *this >= start && *this <= end;
186 DrawToken() = delete;
187 explicit DrawToken(uint64_t sequenceNumber) : fSequenceNumber(sequenceNumber) {}
188 uint64_t fSequenceNumber;
192 * The TokenTracker encapsulates the incrementing and distribution of tokens.
196 /** Gets the token one beyond the last token that has been flushed,
197 either in GrDrawingManager::flush() or Device::flushPendingWorkToRecorder() */
198 DrawToken nextTokenToFlush() const { return fLastFlushedToken.next(); }
200 /** Gets the next draw token. This can be used to record that the next draw
201 issued will use a resource (e.g. texture) while preparing that draw. */
202 DrawToken nextDrawToken() const { return fLastIssuedToken.next(); }
205 // Only these classes get to increment the token counters
206 friend class ::GrOpFlushState;
207 friend class ::TestingUploadTarget;
209 /** Issues the next token for a draw. */
210 DrawToken issueDrawToken() { return ++fLastIssuedToken; }
212 /** Advances the last flushed token by one. */
213 DrawToken flushToken() { return ++fLastFlushedToken; }
215 DrawToken fLastIssuedToken = DrawToken::AlreadyFlushedToken();
216 DrawToken fLastFlushedToken = DrawToken::AlreadyFlushedToken();
220 * A PlotLocator specifies the plot and is analogous to a directory path:
221 * page/plot/plotGeneration
223 * In fact PlotLocator is a portion of a glyph image location in the atlas fully specified by:
224 * format/atlasGeneration/page/plot/plotGeneration/rect
226 * TODO: Remove the small path renderer's use of the PlotLocator for eviction.
230 // These are both restricted by the space they occupy in the PlotLocator.
231 // maxPages is also limited by being crammed into the glyph uvs.
232 // maxPlots is also limited by the fPlotAlreadyUpdated bitfield in
233 // GrDrawOpAtlas::BulkUseTokenUpdater.
234 inline static constexpr auto kMaxMultitexturePages = 4;
235 inline static constexpr int kMaxPlots = 32;
237 PlotLocator(uint32_t pageIdx, uint32_t plotIdx, uint64_t generation)
239 , fPlotIndex(plotIdx)
240 , fPageIndex(pageIdx) {
241 SkASSERT(pageIdx < kMaxMultitexturePages);
242 SkASSERT(plotIdx < kMaxPlots);
243 SkASSERT(generation < ((uint64_t)1 << 48));
247 : fGenID(AtlasGenerationCounter::kInvalidGeneration)
251 bool isValid() const {
252 return fGenID != 0 || fPlotIndex != 0 || fPageIndex != 0;
261 bool operator==(const PlotLocator& other) const {
262 return fGenID == other.fGenID &&
263 fPlotIndex == other.fPlotIndex &&
264 fPageIndex == other.fPageIndex; }
266 uint32_t pageIndex() const { return fPageIndex; }
267 uint32_t plotIndex() const { return fPlotIndex; }
268 uint64_t genID() const { return fGenID; }
272 uint64_t fPlotIndex:8;
273 uint64_t fPageIndex:8;
276 // AtlasLocator handles atlas position information. It keeps a left-top, right-bottom pair of
277 // encoded UV coordinates. The bits 13 & 14 of the U coordinates hold the atlas page index.
278 // This information is handed directly as is from fUVs. This encoding has the nice property
279 // that width = fUVs[2] - fUVs[0]; the page encoding in the top bits subtracts to zero.
282 std::array<uint16_t, 4> getUVs() const {
286 void invalidatePlotLocator() { fPlotLocator.makeInvalid(); }
288 // TODO: Remove the small path renderer's use of this for eviction
289 PlotLocator plotLocator() const { return fPlotLocator; }
291 uint32_t pageIndex() const { return fPlotLocator.pageIndex(); }
293 uint32_t plotIndex() const { return fPlotLocator.plotIndex(); }
295 uint64_t genID() const { return fPlotLocator.genID(); }
297 SkIPoint topLeft() const {
298 return {fUVs[0] & 0x1FFF, fUVs[1]};
301 uint16_t width() const {
302 return fUVs[2] - fUVs[0];
305 uint16_t height() const {
306 return fUVs[3] - fUVs[1];
309 void insetSrc(int padding) {
310 SkASSERT(2 * padding <= this->width());
311 SkASSERT(2 * padding <= this->height());
319 void updatePlotLocator(PlotLocator p) {
321 SkASSERT(fPlotLocator.pageIndex() <= 3);
322 uint16_t page = fPlotLocator.pageIndex() << 13;
323 fUVs[0] = (fUVs[0] & 0x1FFF) | page;
324 fUVs[2] = (fUVs[2] & 0x1FFF) | page;
327 void updateRect(skgpu::IRect16 rect) {
328 SkASSERT(rect.fLeft <= rect.fRight);
329 SkASSERT(rect.fRight <= 0x1FFF);
330 fUVs[0] = (fUVs[0] & 0xE000) | rect.fLeft;
332 fUVs[2] = (fUVs[2] & 0xE000) | rect.fRight;
333 fUVs[3] = rect.fBottom;
337 PlotLocator fPlotLocator{0, 0, 0};
339 // The inset padded bounds in the atlas in the lower 13 bits, and page index in bits 13 &
341 std::array<uint16_t, 4> fUVs{0, 0, 0, 0};
345 * An interface for eviction callbacks. Whenever an atlas evicts a specific PlotLocator,
346 * it will call all of the registered listeners so they can process the eviction.
348 class PlotEvictionCallback {
350 virtual ~PlotEvictionCallback() = default;
351 virtual void evict(PlotLocator) = 0;
355 * A class which can be handed back to an atlas for updating plots in bulk. The
356 * current max number of plots per page an atlas can handle is 32. If in the future
357 * this is insufficient then we can move to a 64 bit int.
359 class BulkUsePlotUpdater {
361 BulkUsePlotUpdater() {
362 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
364 BulkUsePlotUpdater(const BulkUsePlotUpdater& that)
365 : fPlotsToUpdate(that.fPlotsToUpdate) {
366 memcpy(fPlotAlreadyUpdated, that.fPlotAlreadyUpdated, sizeof(fPlotAlreadyUpdated));
369 bool add(const skgpu::AtlasLocator& atlasLocator) {
370 int plotIdx = atlasLocator.plotIndex();
371 int pageIdx = atlasLocator.pageIndex();
372 if (this->find(pageIdx, plotIdx)) {
375 this->set(pageIdx, plotIdx);
380 fPlotsToUpdate.reset();
381 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
385 PlotData(int pageIdx, int plotIdx) : fPageIndex(pageIdx), fPlotIndex(plotIdx) {}
390 int count() const { return fPlotsToUpdate.count(); }
392 const PlotData& plotData(int index) const { return fPlotsToUpdate[index]; }
395 bool find(int pageIdx, int index) const {
396 SkASSERT(index < skgpu::PlotLocator::kMaxPlots);
397 return (fPlotAlreadyUpdated[pageIdx] >> index) & 1;
400 void set(int pageIdx, int index) {
401 SkASSERT(!this->find(pageIdx, index));
402 fPlotAlreadyUpdated[pageIdx] |= (1 << index);
403 fPlotsToUpdate.push_back(PlotData(pageIdx, index));
406 inline static constexpr int kMinItems = 4;
407 SkSTArray<kMinItems, PlotData, true> fPlotsToUpdate;
408 // TODO: increase this to uint64_t to allow more plots per page
409 uint32_t fPlotAlreadyUpdated[skgpu::PlotLocator::kMaxMultitexturePages];
413 * The backing texture for an atlas is broken into a spatial grid of Plots. The Plots
414 * keep track of subimage placement via their Rectanizer. A Plot may be subclassed if
415 * the atlas class needs to track additional information.
417 class Plot : public SkRefCnt {
418 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot);
421 Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
422 int offX, int offY, int width, int height, SkColorType colorType, size_t bpp);
424 uint32_t pageIndex() const { return fPageIndex; }
426 /** plotIndex() is a unique id for the plot relative to the owning GrAtlas and page. */
427 uint32_t plotIndex() const { return fPlotIndex; }
429 * genID() is incremented when the plot is evicted due to a atlas spill. It is used to know
430 * if a particular subimage is still present in the atlas.
432 uint64_t genID() const { return fGenID; }
433 PlotLocator plotLocator() const {
434 SkASSERT(fPlotLocator.isValid());
437 SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
439 bool addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator);
442 * To manage the lifetime of a plot, we use two tokens. We use the last upload token to
443 * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to
444 * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We
445 * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use
446 * has already flushed through the gpu then we can reuse the plot.
448 skgpu::DrawToken lastUploadToken() const { return fLastUpload; }
449 skgpu::DrawToken lastUseToken() const { return fLastUse; }
450 void setLastUploadToken(skgpu::DrawToken token) { fLastUpload = token; }
451 void setLastUseToken(skgpu::DrawToken token) { fLastUse = token; }
453 int flushesSinceLastUsed() { return fFlushesSinceLastUse; }
454 void resetFlushesSinceLastUsed() { fFlushesSinceLastUse = 0; }
455 void incFlushesSinceLastUsed() { fFlushesSinceLastUse++; }
457 std::pair<const void*, SkIRect> prepareForUpload();
461 * Create a clone of this plot. The cloned plot will take the place of the current plot in
464 sk_sp<Plot> clone() const {
465 return sk_sp<Plot>(new Plot(
466 fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType,
471 void resetListPtrs() {
472 fPrev = fNext = nullptr;
480 skgpu::DrawToken fLastUpload;
481 skgpu::DrawToken fLastUse;
482 int fFlushesSinceLastUse;
485 const uint32_t fPageIndex : 16;
486 const uint32_t fPlotIndex : 16;
488 AtlasGenerationCounter* const fGenerationCounter;
490 PlotLocator fPlotLocator;
491 unsigned char* fData;
496 skgpu::RectanizerSkyline fRectanizer;
497 const SkIPoint16 fOffset; // the offset of the plot in the backing texture
498 const SkColorType fColorType;
499 const size_t fBytesPerPixel;
501 SkDEBUGCODE(bool fDirty);
504 typedef SkTInternalLList<Plot> PlotList;
508 #endif // skgpu_AtlasTypes_DEFINED