• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 #include "src/gpu/GrDynamicAtlas.h"
9 
10 #include "src/core/SkIPoint16.h"
11 #include "src/gpu/GrOnFlushResourceProvider.h"
12 #include "src/gpu/GrProxyProvider.h"
13 #include "src/gpu/GrRectanizerPow2.h"
14 #include "src/gpu/GrRectanizerSkyline.h"
15 #include "src/gpu/GrRenderTarget.h"
16 #include "src/gpu/GrSurfaceDrawContext.h"
17 
18 // Each Node covers a sub-rectangle of the final atlas. When a GrDynamicAtlas runs out of room, we
19 // create a new Node the same size as all combined nodes in the atlas as-is, and then place the new
20 // Node immediately below or beside the others (thereby doubling the size of the GyDynamicAtlas).
21 class GrDynamicAtlas::Node {
22 public:
Node(Node * previous,GrRectanizer * rectanizer,int x,int y)23     Node(Node* previous, GrRectanizer* rectanizer, int x, int y)
24             : fPrevious(previous), fRectanizer(rectanizer), fX(x), fY(y) {}
25 
previous() const26     Node* previous() const { return fPrevious; }
27 
addRect(int w,int h,SkIPoint16 * loc)28     bool addRect(int w, int h, SkIPoint16* loc) {
29         // Pad all paths except those that are expected to take up an entire physical texture.
30         if (w < fRectanizer->width()) {
31             w = std::min(w + kPadding, fRectanizer->width());
32         }
33         if (h < fRectanizer->height()) {
34             h = std::min(h + kPadding, fRectanizer->height());
35         }
36         if (!fRectanizer->addRect(w, h, loc)) {
37             return false;
38         }
39         loc->fX += fX;
40         loc->fY += fY;
41         return true;
42     }
43 
44 private:
45     Node* const fPrevious;
46     GrRectanizer* const fRectanizer;
47     const int fX, fY;
48 };
49 
MakeLazyAtlasProxy(LazyInstantiateAtlasCallback && callback,GrColorType colorType,InternalMultisample internalMultisample,const GrCaps & caps,GrSurfaceProxy::UseAllocator useAllocator)50 sk_sp<GrTextureProxy> GrDynamicAtlas::MakeLazyAtlasProxy(
51         LazyInstantiateAtlasCallback&& callback,
52         GrColorType colorType,
53         InternalMultisample internalMultisample,
54         const GrCaps& caps,
55         GrSurfaceProxy::UseAllocator useAllocator) {
56     GrBackendFormat format = caps.getDefaultBackendFormat(colorType, GrRenderable::kYes);
57 
58     int sampleCount = 1;
59     if (InternalMultisample::kYes == internalMultisample) {
60         sampleCount = caps.internalMultisampleCount(format);
61     }
62 
63     sk_sp<GrTextureProxy> proxy =
64             GrProxyProvider::MakeFullyLazyProxy(std::move(callback), format, GrRenderable::kYes,
65                                                 sampleCount, GrProtected::kNo, caps, useAllocator);
66 
67     return proxy;
68 }
69 
GrDynamicAtlas(GrColorType colorType,InternalMultisample internalMultisample,SkISize initialSize,int maxAtlasSize,const GrCaps & caps,RectanizerAlgorithm algorithm)70 GrDynamicAtlas::GrDynamicAtlas(GrColorType colorType, InternalMultisample internalMultisample,
71                                SkISize initialSize, int maxAtlasSize, const GrCaps& caps,
72                                RectanizerAlgorithm algorithm)
73         : fColorType(colorType)
74         , fInternalMultisample(internalMultisample)
75         , fMaxAtlasSize(maxAtlasSize)
76         , fRectanizerAlgorithm(algorithm) {
77     SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
78     this->reset(initialSize, caps);
79 }
80 
~GrDynamicAtlas()81 GrDynamicAtlas::~GrDynamicAtlas() {
82 }
83 
reset(SkISize initialSize,const GrCaps & caps)84 void GrDynamicAtlas::reset(SkISize initialSize, const GrCaps& caps) {
85     fNodeAllocator.reset();
86     fWidth = std::min(SkNextPow2(initialSize.width()), fMaxAtlasSize);
87     fHeight = std::min(SkNextPow2(initialSize.height()), fMaxAtlasSize);
88     fTopNode = nullptr;
89     fDrawBounds.setEmpty();
90     fTextureProxy = MakeLazyAtlasProxy(
91             [this](GrResourceProvider* resourceProvider, const LazyAtlasDesc& desc) {
92                 if (!fBackingTexture) {
93                     fBackingTexture = resourceProvider->createTexture(
94                             {fWidth, fHeight}, desc.fFormat, desc.fRenderable, desc.fSampleCnt,
95                             desc.fMipmapped, desc.fBudgeted, desc.fProtected);
96                 }
97                 return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
98             },
99             fColorType, fInternalMultisample, caps, GrSurfaceProxy::UseAllocator::kNo);
100     fBackingTexture = nullptr;
101 }
102 
makeNode(Node * previous,int l,int t,int r,int b)103 GrDynamicAtlas::Node* GrDynamicAtlas::makeNode(Node* previous, int l, int t, int r, int b) {
104     int width = r - l;
105     int height = b - t;
106     GrRectanizer* rectanizer = (fRectanizerAlgorithm == RectanizerAlgorithm::kSkyline)
107             ? (GrRectanizer*)fNodeAllocator.make<GrRectanizerSkyline>(width, height)
108             : fNodeAllocator.make<GrRectanizerPow2>(width, height);
109     return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
110 }
111 
addRect(int width,int height,SkIPoint16 * location)112 bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
113     // This can't be called anymore once instantiate() has been called.
114     SkASSERT(!this->isInstantiated());
115 
116     if (!this->internalPlaceRect(width, height, location)) {
117         return false;
118     }
119 
120     fDrawBounds.fWidth = std::max(fDrawBounds.width(), location->x() + width);
121     fDrawBounds.fHeight = std::max(fDrawBounds.height(), location->y() + height);
122     return true;
123 }
124 
internalPlaceRect(int w,int h,SkIPoint16 * loc)125 bool GrDynamicAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
126     if (std::max(h, w) > fMaxAtlasSize) {
127         return false;
128     }
129     if (std::min(h, w) <= 0) {
130         loc->set(0, 0);
131         return true;
132     }
133 
134     if (!fTopNode) {
135         if (w > fWidth) {
136             fWidth = std::min(SkNextPow2(w), fMaxAtlasSize);
137         }
138         if (h > fHeight) {
139             fHeight = std::min(SkNextPow2(h), fMaxAtlasSize);
140         }
141         fTopNode = this->makeNode(nullptr, 0, 0, fWidth, fHeight);
142     }
143 
144     for (Node* node = fTopNode; node; node = node->previous()) {
145         if (node->addRect(w, h, loc)) {
146             return true;
147         }
148     }
149 
150     // The rect didn't fit. Grow the atlas and try again.
151     do {
152         if (fWidth >= fMaxAtlasSize && fHeight >= fMaxAtlasSize) {
153             return false;
154         }
155         if (fHeight <= fWidth) {
156             int top = fHeight;
157             fHeight = std::min(fHeight * 2, fMaxAtlasSize);
158             fTopNode = this->makeNode(fTopNode, 0, top, fWidth, fHeight);
159         } else {
160             int left = fWidth;
161             fWidth = std::min(fWidth * 2, fMaxAtlasSize);
162             fTopNode = this->makeNode(fTopNode, left, 0, fWidth, fHeight);
163         }
164     } while (!fTopNode->addRect(w, h, loc));
165 
166     return true;
167 }
168 
instantiate(GrOnFlushResourceProvider * onFlushRP,sk_sp<GrTexture> backingTexture)169 std::unique_ptr<GrSurfaceDrawContext> GrDynamicAtlas::instantiate(
170         GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
171     SkASSERT(!this->isInstantiated());  // This method should only be called once.
172     // Caller should have cropped any paths to the destination render target instead of asking for
173     // an atlas larger than maxRenderTargetSize.
174     SkASSERT(std::max(fHeight, fWidth) <= fMaxAtlasSize);
175     SkASSERT(fMaxAtlasSize <= onFlushRP->caps()->maxRenderTargetSize());
176 
177     // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
178     // knows we only intend to write out a smaller sub-rectangle of the backing texture.
179     fTextureProxy->priv().setLazyDimensions(fDrawBounds);
180 
181     if (backingTexture) {
182 #ifdef SK_DEBUG
183         auto backingRT = backingTexture->asRenderTarget();
184         SkASSERT(backingRT);
185         SkASSERT(backingRT->backendFormat() == fTextureProxy->backendFormat());
186         SkASSERT(backingRT->numSamples() == fTextureProxy->asRenderTargetProxy()->numSamples());
187         SkASSERT(backingRT->width() == fWidth);
188         SkASSERT(backingRT->height() == fHeight);
189 #endif
190         fBackingTexture = std::move(backingTexture);
191     }
192     auto rtc = onFlushRP->makeRenderTargetContext(fTextureProxy, kTextureOrigin, fColorType,
193                                                   nullptr, SkSurfaceProps());
194     if (!rtc) {
195         onFlushRP->printWarningMessage(SkStringPrintf(
196                 "WARNING: failed to allocate a %ix%i atlas. Some masks will not be drawn.\n",
197                 fWidth, fHeight).c_str());
198         return nullptr;
199     }
200 
201     SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
202     rtc->clearAtLeast(clearRect, SK_PMColor4fTRANSPARENT);
203     return rtc;
204 }
205