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