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