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