blob: 916a3381e274d3b1702bafd1742f0c03c831272a [file] [log] [blame]
Chris Daltona550cf22020-02-07 13:35:31 -07001/*
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
Chris Daltone5a141d2020-05-19 11:57:53 -060010#include "src/core/SkIPoint16.h"
Chris Daltona550cf22020-02-07 13:35:31 -070011#include "src/gpu/GrOnFlushResourceProvider.h"
12#include "src/gpu/GrProxyProvider.h"
Chris Daltond2dc8dd2020-05-19 16:32:02 -060013#include "src/gpu/GrRectanizerPow2.h"
Chris Daltona550cf22020-02-07 13:35:31 -070014#include "src/gpu/GrRectanizerSkyline.h"
15#include "src/gpu/GrRenderTarget.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040016#include "src/gpu/GrResourceProvider.h"
Brian Salomoneebe7352020-12-09 16:37:04 -050017#include "src/gpu/GrSurfaceDrawContext.h"
Chris Daltona550cf22020-02-07 13:35:31 -070018
19// Each Node covers a sub-rectangle of the final atlas. When a GrDynamicAtlas runs out of room, we
20// create a new Node the same size as all combined nodes in the atlas as-is, and then place the new
21// Node immediately below or beside the others (thereby doubling the size of the GyDynamicAtlas).
22class GrDynamicAtlas::Node {
23public:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060024 Node(Node* previous, GrRectanizer* rectanizer, int x, int y)
25 : fPrevious(previous), fRectanizer(rectanizer), fX(x), fY(y) {}
Chris Daltona550cf22020-02-07 13:35:31 -070026
Chris Daltond2dc8dd2020-05-19 16:32:02 -060027 Node* previous() const { return fPrevious; }
Chris Daltona550cf22020-02-07 13:35:31 -070028
29 bool addRect(int w, int h, SkIPoint16* loc) {
30 // Pad all paths except those that are expected to take up an entire physical texture.
Chris Daltond2dc8dd2020-05-19 16:32:02 -060031 if (w < fRectanizer->width()) {
32 w = std::min(w + kPadding, fRectanizer->width());
Chris Daltona550cf22020-02-07 13:35:31 -070033 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060034 if (h < fRectanizer->height()) {
35 h = std::min(h + kPadding, fRectanizer->height());
Chris Daltona550cf22020-02-07 13:35:31 -070036 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060037 if (!fRectanizer->addRect(w, h, loc)) {
Chris Daltona550cf22020-02-07 13:35:31 -070038 return false;
39 }
40 loc->fX += fX;
41 loc->fY += fY;
42 return true;
43 }
44
45private:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060046 Node* const fPrevious;
47 GrRectanizer* const fRectanizer;
Chris Daltona550cf22020-02-07 13:35:31 -070048 const int fX, fY;
Chris Daltona550cf22020-02-07 13:35:31 -070049};
50
51sk_sp<GrTextureProxy> GrDynamicAtlas::MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040052 LazyInstantiateAtlasCallback&& callback,
53 GrColorType colorType,
54 InternalMultisample internalMultisample,
55 const GrCaps& caps,
Chris Daltona550cf22020-02-07 13:35:31 -070056 GrSurfaceProxy::UseAllocator useAllocator) {
57 GrBackendFormat format = caps.getDefaultBackendFormat(colorType, GrRenderable::kYes);
Chris Daltonf83d0342020-02-10 14:19:30 -070058
59 int sampleCount = 1;
Chris Dalton57ab06c2021-04-22 12:57:28 -060060 if (InternalMultisample::kYes == internalMultisample) {
Chris Daltonf83d0342020-02-10 14:19:30 -070061 sampleCount = caps.internalMultisampleCount(format);
62 }
Chris Daltona550cf22020-02-07 13:35:31 -070063
Brian Salomondf1bd6d2020-03-26 20:37:01 -040064 sk_sp<GrTextureProxy> proxy =
Brian Salomon63410e92020-03-23 18:32:50 -040065 GrProxyProvider::MakeFullyLazyProxy(std::move(callback), format, GrRenderable::kYes,
Brian Salomondf1bd6d2020-03-26 20:37:01 -040066 sampleCount, GrProtected::kNo, caps, useAllocator);
Chris Daltona550cf22020-02-07 13:35:31 -070067
68 return proxy;
69}
70
71GrDynamicAtlas::GrDynamicAtlas(GrColorType colorType, InternalMultisample internalMultisample,
Chris Daltond2dc8dd2020-05-19 16:32:02 -060072 SkISize initialSize, int maxAtlasSize, const GrCaps& caps,
73 RectanizerAlgorithm algorithm)
Chris Daltona550cf22020-02-07 13:35:31 -070074 : fColorType(colorType)
75 , fInternalMultisample(internalMultisample)
Chris Daltond2dc8dd2020-05-19 16:32:02 -060076 , fMaxAtlasSize(maxAtlasSize)
77 , fRectanizerAlgorithm(algorithm) {
Chris Daltona550cf22020-02-07 13:35:31 -070078 SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
79 this->reset(initialSize, caps);
80}
81
82GrDynamicAtlas::~GrDynamicAtlas() {
83}
84
85void GrDynamicAtlas::reset(SkISize initialSize, const GrCaps& caps) {
Chris Daltond2dc8dd2020-05-19 16:32:02 -060086 fNodeAllocator.reset();
Chris Daltona550cf22020-02-07 13:35:31 -070087 fWidth = std::min(SkNextPow2(initialSize.width()), fMaxAtlasSize);
88 fHeight = std::min(SkNextPow2(initialSize.height()), fMaxAtlasSize);
89 fTopNode = nullptr;
90 fDrawBounds.setEmpty();
91 fTextureProxy = MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040092 [this](GrResourceProvider* resourceProvider, const LazyAtlasDesc& desc) {
Chris Daltona550cf22020-02-07 13:35:31 -070093 if (!fBackingTexture) {
94 fBackingTexture = resourceProvider->createTexture(
Chris Dalton83420eb2021-06-23 18:47:09 -060095 fTextureProxy->backingStoreDimensions(), desc.fFormat, desc.fRenderable,
96 desc.fSampleCnt, desc.fMipmapped, desc.fBudgeted, desc.fProtected);
Chris Daltona550cf22020-02-07 13:35:31 -070097 }
98 return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
99 },
100 fColorType, fInternalMultisample, caps, GrSurfaceProxy::UseAllocator::kNo);
101 fBackingTexture = nullptr;
102}
103
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600104GrDynamicAtlas::Node* GrDynamicAtlas::makeNode(Node* previous, int l, int t, int r, int b) {
105 int width = r - l;
106 int height = b - t;
107 GrRectanizer* rectanizer = (fRectanizerAlgorithm == RectanizerAlgorithm::kSkyline)
108 ? (GrRectanizer*)fNodeAllocator.make<GrRectanizerSkyline>(width, height)
109 : fNodeAllocator.make<GrRectanizerPow2>(width, height);
110 return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
111}
112
Chris Dalton83420eb2021-06-23 18:47:09 -0600113GrSurfaceProxyView GrDynamicAtlas::readView(const GrCaps& caps) const {
Chris Daltonabed2672021-06-17 16:54:28 -0600114 return {fTextureProxy, kTextureOrigin,
115 caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
116}
117
Chris Dalton83420eb2021-06-23 18:47:09 -0600118GrSurfaceProxyView GrDynamicAtlas::writeView(const GrCaps& caps) const {
119 return {fTextureProxy, kTextureOrigin,
120 caps.getWriteSwizzle(fTextureProxy->backendFormat(), fColorType)};
121}
122
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600123bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
Chris Daltona550cf22020-02-07 13:35:31 -0700124 // This can't be called anymore once instantiate() has been called.
125 SkASSERT(!this->isInstantiated());
126
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600127 if (!this->internalPlaceRect(width, height, location)) {
Chris Daltona550cf22020-02-07 13:35:31 -0700128 return false;
129 }
Chris Daltona550cf22020-02-07 13:35:31 -0700130
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600131 fDrawBounds.fWidth = std::max(fDrawBounds.width(), location->x() + width);
132 fDrawBounds.fHeight = std::max(fDrawBounds.height(), location->y() + height);
Chris Daltona550cf22020-02-07 13:35:31 -0700133 return true;
134}
135
136bool GrDynamicAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
137 if (std::max(h, w) > fMaxAtlasSize) {
138 return false;
139 }
140 if (std::min(h, w) <= 0) {
141 loc->set(0, 0);
142 return true;
143 }
144
145 if (!fTopNode) {
146 if (w > fWidth) {
147 fWidth = std::min(SkNextPow2(w), fMaxAtlasSize);
148 }
149 if (h > fHeight) {
150 fHeight = std::min(SkNextPow2(h), fMaxAtlasSize);
151 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600152 fTopNode = this->makeNode(nullptr, 0, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700153 }
154
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600155 for (Node* node = fTopNode; node; node = node->previous()) {
Chris Daltona550cf22020-02-07 13:35:31 -0700156 if (node->addRect(w, h, loc)) {
157 return true;
158 }
159 }
160
161 // The rect didn't fit. Grow the atlas and try again.
162 do {
163 if (fWidth >= fMaxAtlasSize && fHeight >= fMaxAtlasSize) {
164 return false;
165 }
166 if (fHeight <= fWidth) {
167 int top = fHeight;
168 fHeight = std::min(fHeight * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600169 fTopNode = this->makeNode(fTopNode, 0, top, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700170 } else {
171 int left = fWidth;
172 fWidth = std::min(fWidth * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600173 fTopNode = this->makeNode(fTopNode, left, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700174 }
175 } while (!fTopNode->addRect(w, h, loc));
176
177 return true;
178}
179
Chris Dalton83420eb2021-06-23 18:47:09 -0600180void GrDynamicAtlas::instantiate(GrOnFlushResourceProvider* onFlushRP,
181 sk_sp<GrTexture> backingTexture) {
Chris Daltona550cf22020-02-07 13:35:31 -0700182 SkASSERT(!this->isInstantiated()); // This method should only be called once.
183 // Caller should have cropped any paths to the destination render target instead of asking for
184 // an atlas larger than maxRenderTargetSize.
185 SkASSERT(std::max(fHeight, fWidth) <= fMaxAtlasSize);
186 SkASSERT(fMaxAtlasSize <= onFlushRP->caps()->maxRenderTargetSize());
187
Chris Dalton83420eb2021-06-23 18:47:09 -0600188 if (fTextureProxy->isFullyLazy()) {
189 // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
190 // knows we only intend to write out a smaller sub-rectangle of the backing texture.
191 fTextureProxy->priv().setLazyDimensions(fDrawBounds);
192 }
193 SkASSERT(fTextureProxy->dimensions() == fDrawBounds);
Chris Daltona550cf22020-02-07 13:35:31 -0700194
195 if (backingTexture) {
196#ifdef SK_DEBUG
197 auto backingRT = backingTexture->asRenderTarget();
198 SkASSERT(backingRT);
199 SkASSERT(backingRT->backendFormat() == fTextureProxy->backendFormat());
200 SkASSERT(backingRT->numSamples() == fTextureProxy->asRenderTargetProxy()->numSamples());
Chris Dalton83420eb2021-06-23 18:47:09 -0600201 SkASSERT(backingRT->dimensions() == fTextureProxy->backingStoreDimensions());
Chris Daltona550cf22020-02-07 13:35:31 -0700202#endif
203 fBackingTexture = std::move(backingTexture);
204 }
Chris Dalton83420eb2021-06-23 18:47:09 -0600205 onFlushRP->instatiateProxy(fTextureProxy.get());
Chris Daltona550cf22020-02-07 13:35:31 -0700206}