1 /*
2 * Copyright 2017 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 GrCCAtlas_DEFINED
9 #define GrCCAtlas_DEFINED
10
11 #include "GrAllocator.h"
12 #include "GrNonAtomicRef.h"
13 #include "GrResourceKey.h"
14 #include "GrTexture.h"
15 #include "SkRefCnt.h"
16 #include "SkSize.h"
17
18 class GrCCCachedAtlas;
19 class GrOnFlushResourceProvider;
20 class GrRenderTargetContext;
21 class GrTextureProxy;
22 struct SkIPoint16;
23 struct SkIRect;
24
25 /**
26 * This class implements a dynamic size GrRectanizer that grows until it reaches the implementation-
27 * dependent max texture size. When finalized, it also creates and stores a GrTextureProxy for the
28 * underlying atlas.
29 */
30 class GrCCAtlas {
31 public:
32 // As long as GrSurfaceOrigin exists, we just have to decide on one for the atlas texture.
33 static constexpr GrSurfaceOrigin kTextureOrigin = kTopLeft_GrSurfaceOrigin;
34 static constexpr int kPadding = 1; // Amount of padding below and to the right of each path.
35
36 // This struct encapsulates the minimum and desired requirements for an atlas, as well as an
37 // approximate number of pixels to help select a good initial size.
38 struct Specs {
39 int fMaxPreferredTextureSize = 0;
40 int fMinTextureSize = 0;
41 int fMinWidth = 0; // If there are 100 20x10 paths, this should be 20.
42 int fMinHeight = 0; // If there are 100 20x10 paths, this should be 10.
43 int fApproxNumPixels = 0;
44
45 // Add space for a rect in the desired atlas specs.
46 void accountForSpace(int width, int height);
47 };
48
49 enum class CoverageType : bool {
50 kFP16_CoverageCount,
51 kA8_LiteralCoverage
52 };
53
54 GrCCAtlas(CoverageType, const Specs&, const GrCaps&);
55 ~GrCCAtlas();
56
textureProxy()57 GrTextureProxy* textureProxy() const { return fTextureProxy.get(); }
currentWidth()58 int currentWidth() const { return fWidth; }
currentHeight()59 int currentHeight() const { return fHeight; }
60
61 // Attempts to add a rect to the atlas. If successful, returns the integer offset from
62 // device-space pixels where the path will be drawn, to atlas pixels where its mask resides.
63 bool addRect(const SkIRect& devIBounds, SkIVector* atlasOffset);
drawBounds()64 const SkISize& drawBounds() { return fDrawBounds; }
65
66 // This is an optional space for the caller to jot down which user-defined batches to use when
67 // they render the content of this atlas.
68 void setFillBatchID(int id);
getFillBatchID()69 int getFillBatchID() const { return fFillBatchID; }
70 void setStrokeBatchID(int id);
getStrokeBatchID()71 int getStrokeBatchID() const { return fStrokeBatchID; }
72
73 sk_sp<GrCCCachedAtlas> refOrMakeCachedAtlas(GrOnFlushResourceProvider*);
74
75 // Instantiates our texture proxy for the atlas and returns a pre-cleared GrRenderTargetContext
76 // that the caller may use to render the content. After this call, it is no longer valid to call
77 // addRect(), setUserBatchID(), or this method again.
78 //
79 // 'backingTexture', if provided, is a renderable texture with which to instantiate our proxy.
80 // If null then we will create a texture using the resource provider. The purpose of this param
81 // is to provide a guaranteed way to recycle a stashed atlas texture from a previous flush.
82 sk_sp<GrRenderTargetContext> makeRenderTargetContext(GrOnFlushResourceProvider*,
83 sk_sp<GrTexture> backingTexture = nullptr);
84
85 private:
86 class Node;
87
88 bool internalPlaceRect(int w, int h, SkIPoint16* loc);
89
90 const CoverageType fCoverageType;
91 const int fMaxTextureSize;
92 int fWidth, fHeight;
93 std::unique_ptr<Node> fTopNode;
94 SkISize fDrawBounds = {0, 0};
95
96 int fFillBatchID;
97 int fStrokeBatchID;
98
99 sk_sp<GrCCCachedAtlas> fCachedAtlas;
100 sk_sp<GrTextureProxy> fTextureProxy;
101 sk_sp<GrTexture> fBackingTexture;
102 };
103
104 /**
105 * This class implements an unbounded stack of atlases. When the current atlas reaches the
106 * implementation-dependent max texture size, a new one is pushed to the back and we continue on.
107 */
108 class GrCCAtlasStack {
109 public:
110 using CoverageType = GrCCAtlas::CoverageType;
111
GrCCAtlasStack(CoverageType coverageType,const GrCCAtlas::Specs & specs,const GrCaps * caps)112 GrCCAtlasStack(CoverageType coverageType, const GrCCAtlas::Specs& specs, const GrCaps* caps)
113 : fCoverageType(coverageType), fSpecs(specs), fCaps(caps) {}
114
empty()115 bool empty() const { return fAtlases.empty(); }
front()116 const GrCCAtlas& front() const { SkASSERT(!this->empty()); return fAtlases.front(); }
front()117 GrCCAtlas& front() { SkASSERT(!this->empty()); return fAtlases.front(); }
current()118 GrCCAtlas& current() { SkASSERT(!this->empty()); return fAtlases.back(); }
119
120 class Iter {
121 public:
Iter(GrCCAtlasStack & stack)122 Iter(GrCCAtlasStack& stack) : fImpl(&stack.fAtlases) {}
next()123 bool next() { return fImpl.next(); }
124 GrCCAtlas* operator->() const { return fImpl.get(); }
125 private:
126 typename GrTAllocator<GrCCAtlas>::Iter fImpl;
127 };
128
129 // Adds a rect to the current atlas and returns the offset from device space to atlas space.
130 // Call current() to get the atlas it was added to.
131 //
132 // If the return value is non-null, it means the given rect did not fit in the then-current
133 // atlas, so it was retired and a new one was added to the stack. The return value is the
134 // newly-retired atlas. The caller should call setUserBatchID() on the retired atlas before
135 // moving on.
136 GrCCAtlas* addRect(const SkIRect& devIBounds, SkIVector* devToAtlasOffset);
137
138 private:
139 const CoverageType fCoverageType;
140 const GrCCAtlas::Specs fSpecs;
141 const GrCaps* const fCaps;
142 GrSTAllocator<4, GrCCAtlas> fAtlases;
143 };
144
accountForSpace(int width,int height)145 inline void GrCCAtlas::Specs::accountForSpace(int width, int height) {
146 fMinWidth = SkTMax(width, fMinWidth);
147 fMinHeight = SkTMax(height, fMinHeight);
148 fApproxNumPixels += (width + kPadding) * (height + kPadding);
149 }
150
151 #endif
152