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