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