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