blob: ea44c8c8b190a108d545522d925b1f209960eafb [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"
Brian Salomoneebe7352020-12-09 16:37:04 -050016#include "src/gpu/GrSurfaceDrawContext.h"
Chris Daltona550cf22020-02-07 13:35:31 -070017
18// Each Node covers a sub-rectangle of the final atlas. When a GrDynamicAtlas runs out of room, we
19// create a new Node the same size as all combined nodes in the atlas as-is, and then place the new
20// Node immediately below or beside the others (thereby doubling the size of the GyDynamicAtlas).
21class GrDynamicAtlas::Node {
22public:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060023 Node(Node* previous, GrRectanizer* rectanizer, int x, int y)
24 : fPrevious(previous), fRectanizer(rectanizer), fX(x), fY(y) {}
Chris Daltona550cf22020-02-07 13:35:31 -070025
Chris Daltond2dc8dd2020-05-19 16:32:02 -060026 Node* previous() const { return fPrevious; }
Chris Daltona550cf22020-02-07 13:35:31 -070027
28 bool addRect(int w, int h, SkIPoint16* loc) {
29 // Pad all paths except those that are expected to take up an entire physical texture.
Chris Daltond2dc8dd2020-05-19 16:32:02 -060030 if (w < fRectanizer->width()) {
31 w = std::min(w + kPadding, fRectanizer->width());
Chris Daltona550cf22020-02-07 13:35:31 -070032 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060033 if (h < fRectanizer->height()) {
34 h = std::min(h + kPadding, fRectanizer->height());
Chris Daltona550cf22020-02-07 13:35:31 -070035 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060036 if (!fRectanizer->addRect(w, h, loc)) {
Chris Daltona550cf22020-02-07 13:35:31 -070037 return false;
38 }
39 loc->fX += fX;
40 loc->fY += fY;
41 return true;
42 }
43
44private:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060045 Node* const fPrevious;
46 GrRectanizer* const fRectanizer;
Chris Daltona550cf22020-02-07 13:35:31 -070047 const int fX, fY;
Chris Daltona550cf22020-02-07 13:35:31 -070048};
49
50sk_sp<GrTextureProxy> GrDynamicAtlas::MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040051 LazyInstantiateAtlasCallback&& callback,
52 GrColorType colorType,
53 InternalMultisample internalMultisample,
54 const GrCaps& caps,
Chris Daltona550cf22020-02-07 13:35:31 -070055 GrSurfaceProxy::UseAllocator useAllocator) {
56 GrBackendFormat format = caps.getDefaultBackendFormat(colorType, GrRenderable::kYes);
Chris Daltonf83d0342020-02-10 14:19:30 -070057
58 int sampleCount = 1;
Chris Dalton57ab06c2021-04-22 12:57:28 -060059 if (InternalMultisample::kYes == internalMultisample) {
Chris Daltonf83d0342020-02-10 14:19:30 -070060 sampleCount = caps.internalMultisampleCount(format);
61 }
Chris Daltona550cf22020-02-07 13:35:31 -070062
Brian Salomondf1bd6d2020-03-26 20:37:01 -040063 sk_sp<GrTextureProxy> proxy =
Brian Salomon63410e92020-03-23 18:32:50 -040064 GrProxyProvider::MakeFullyLazyProxy(std::move(callback), format, GrRenderable::kYes,
Brian Salomondf1bd6d2020-03-26 20:37:01 -040065 sampleCount, GrProtected::kNo, caps, useAllocator);
Chris Daltona550cf22020-02-07 13:35:31 -070066
67 return proxy;
68}
69
70GrDynamicAtlas::GrDynamicAtlas(GrColorType colorType, InternalMultisample internalMultisample,
Chris Daltond2dc8dd2020-05-19 16:32:02 -060071 SkISize initialSize, int maxAtlasSize, const GrCaps& caps,
72 RectanizerAlgorithm algorithm)
Chris Daltona550cf22020-02-07 13:35:31 -070073 : fColorType(colorType)
74 , fInternalMultisample(internalMultisample)
Chris Daltond2dc8dd2020-05-19 16:32:02 -060075 , fMaxAtlasSize(maxAtlasSize)
76 , fRectanizerAlgorithm(algorithm) {
Chris Daltona550cf22020-02-07 13:35:31 -070077 SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
78 this->reset(initialSize, caps);
79}
80
81GrDynamicAtlas::~GrDynamicAtlas() {
82}
83
84void GrDynamicAtlas::reset(SkISize initialSize, const GrCaps& caps) {
Chris Daltond2dc8dd2020-05-19 16:32:02 -060085 fNodeAllocator.reset();
Chris Daltona550cf22020-02-07 13:35:31 -070086 fWidth = std::min(SkNextPow2(initialSize.width()), fMaxAtlasSize);
87 fHeight = std::min(SkNextPow2(initialSize.height()), fMaxAtlasSize);
88 fTopNode = nullptr;
89 fDrawBounds.setEmpty();
90 fTextureProxy = MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040091 [this](GrResourceProvider* resourceProvider, const LazyAtlasDesc& desc) {
Chris Daltona550cf22020-02-07 13:35:31 -070092 if (!fBackingTexture) {
93 fBackingTexture = resourceProvider->createTexture(
Brian Salomon63410e92020-03-23 18:32:50 -040094 {fWidth, fHeight}, desc.fFormat, desc.fRenderable, desc.fSampleCnt,
Brian Salomon40a40622020-07-21 10:32:07 -040095 desc.fMipmapped, desc.fBudgeted, desc.fProtected);
Chris Daltona550cf22020-02-07 13:35:31 -070096 }
97 return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
98 },
99 fColorType, fInternalMultisample, caps, GrSurfaceProxy::UseAllocator::kNo);
100 fBackingTexture = nullptr;
101}
102
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600103GrDynamicAtlas::Node* GrDynamicAtlas::makeNode(Node* previous, int l, int t, int r, int b) {
104 int width = r - l;
105 int height = b - t;
106 GrRectanizer* rectanizer = (fRectanizerAlgorithm == RectanizerAlgorithm::kSkyline)
107 ? (GrRectanizer*)fNodeAllocator.make<GrRectanizerSkyline>(width, height)
108 : fNodeAllocator.make<GrRectanizerPow2>(width, height);
109 return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
110}
111
Chris Daltonabed2672021-06-17 16:54:28 -0600112GrSurfaceProxyView GrDynamicAtlas::surfaceProxyView(const GrCaps& caps) const {
113 return {fTextureProxy, kTextureOrigin,
114 caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
115}
116
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600117bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
Chris Daltona550cf22020-02-07 13:35:31 -0700118 // This can't be called anymore once instantiate() has been called.
119 SkASSERT(!this->isInstantiated());
120
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600121 if (!this->internalPlaceRect(width, height, location)) {
Chris Daltona550cf22020-02-07 13:35:31 -0700122 return false;
123 }
Chris Daltona550cf22020-02-07 13:35:31 -0700124
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600125 fDrawBounds.fWidth = std::max(fDrawBounds.width(), location->x() + width);
126 fDrawBounds.fHeight = std::max(fDrawBounds.height(), location->y() + height);
Chris Daltona550cf22020-02-07 13:35:31 -0700127 return true;
128}
129
130bool GrDynamicAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
131 if (std::max(h, w) > fMaxAtlasSize) {
132 return false;
133 }
134 if (std::min(h, w) <= 0) {
135 loc->set(0, 0);
136 return true;
137 }
138
139 if (!fTopNode) {
140 if (w > fWidth) {
141 fWidth = std::min(SkNextPow2(w), fMaxAtlasSize);
142 }
143 if (h > fHeight) {
144 fHeight = std::min(SkNextPow2(h), fMaxAtlasSize);
145 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600146 fTopNode = this->makeNode(nullptr, 0, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700147 }
148
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600149 for (Node* node = fTopNode; node; node = node->previous()) {
Chris Daltona550cf22020-02-07 13:35:31 -0700150 if (node->addRect(w, h, loc)) {
151 return true;
152 }
153 }
154
155 // The rect didn't fit. Grow the atlas and try again.
156 do {
157 if (fWidth >= fMaxAtlasSize && fHeight >= fMaxAtlasSize) {
158 return false;
159 }
160 if (fHeight <= fWidth) {
161 int top = fHeight;
162 fHeight = std::min(fHeight * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600163 fTopNode = this->makeNode(fTopNode, 0, top, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700164 } else {
165 int left = fWidth;
166 fWidth = std::min(fWidth * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600167 fTopNode = this->makeNode(fTopNode, left, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700168 }
169 } while (!fTopNode->addRect(w, h, loc));
170
171 return true;
172}
173
Brian Salomoneebe7352020-12-09 16:37:04 -0500174std::unique_ptr<GrSurfaceDrawContext> GrDynamicAtlas::instantiate(
Chris Daltona550cf22020-02-07 13:35:31 -0700175 GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
176 SkASSERT(!this->isInstantiated()); // This method should only be called once.
177 // Caller should have cropped any paths to the destination render target instead of asking for
178 // an atlas larger than maxRenderTargetSize.
179 SkASSERT(std::max(fHeight, fWidth) <= fMaxAtlasSize);
180 SkASSERT(fMaxAtlasSize <= onFlushRP->caps()->maxRenderTargetSize());
181
182 // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
183 // knows we only intend to write out a smaller sub-rectangle of the backing texture.
184 fTextureProxy->priv().setLazyDimensions(fDrawBounds);
185
186 if (backingTexture) {
187#ifdef SK_DEBUG
188 auto backingRT = backingTexture->asRenderTarget();
189 SkASSERT(backingRT);
190 SkASSERT(backingRT->backendFormat() == fTextureProxy->backendFormat());
191 SkASSERT(backingRT->numSamples() == fTextureProxy->asRenderTargetProxy()->numSamples());
192 SkASSERT(backingRT->width() == fWidth);
193 SkASSERT(backingRT->height() == fHeight);
194#endif
195 fBackingTexture = std::move(backingTexture);
196 }
John Stiles0fbc6a32021-06-04 14:40:57 -0400197 auto sdc = onFlushRP->makeSurfaceDrawContext(fTextureProxy, kTextureOrigin, fColorType,
198 nullptr, SkSurfaceProps());
199 if (!sdc) {
Chris Dalton5a5fe792020-02-15 11:41:30 -0700200 onFlushRP->printWarningMessage(SkStringPrintf(
201 "WARNING: failed to allocate a %ix%i atlas. Some masks will not be drawn.\n",
202 fWidth, fHeight).c_str());
Chris Daltona550cf22020-02-07 13:35:31 -0700203 return nullptr;
204 }
205
206 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
John Stiles0fbc6a32021-06-04 14:40:57 -0400207 sdc->clearAtLeast(clearRect, SK_PMColor4fTRANSPARENT);
208 return sdc;
Chris Daltona550cf22020-02-07 13:35:31 -0700209}