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