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