1 /*
2 * Copyright 2017 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 "GrCCAtlas.h"
9
10 #include "GrCaps.h"
11 #include "GrOnFlushResourceProvider.h"
12 #include "GrProxyProvider.h"
13 #include "GrRectanizer_skyline.h"
14 #include "GrRenderTargetContext.h"
15 #include "GrTexture.h"
16 #include "GrTextureProxy.h"
17 #include "SkMakeUnique.h"
18 #include "SkMathPriv.h"
19 #include "ccpr/GrCCPathCache.h"
20 #include <atomic>
21
22 class GrCCAtlas::Node {
23 public:
Node(std::unique_ptr<Node> previous,int l,int t,int r,int b)24 Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
25 : fPrevious(std::move(previous)), fX(l), fY(t), fRectanizer(r - l, b - t) {}
26
previous() const27 Node* previous() const { return fPrevious.get(); }
28
addRect(int w,int h,SkIPoint16 * loc,int maxAtlasSize)29 bool addRect(int w, int h, SkIPoint16* loc, int maxAtlasSize) {
30 // Pad all paths except those that are expected to take up an entire physical texture.
31 if (w < maxAtlasSize) {
32 w = SkTMin(w + kPadding, maxAtlasSize);
33 }
34 if (h < maxAtlasSize) {
35 h = SkTMin(h + kPadding, maxAtlasSize);
36 }
37 if (!fRectanizer.addRect(w, h, loc)) {
38 return false;
39 }
40 loc->fX += fX;
41 loc->fY += fY;
42 return true;
43 }
44
45 private:
46 const std::unique_ptr<Node> fPrevious;
47 const int fX, fY;
48 GrRectanizerSkyline fRectanizer;
49 };
50
GrCCAtlas(CoverageType coverageType,const Specs & specs,const GrCaps & caps)51 GrCCAtlas::GrCCAtlas(CoverageType coverageType, const Specs& specs, const GrCaps& caps)
52 : fCoverageType(coverageType)
53 , fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
54 specs.fMaxPreferredTextureSize)) {
55 // Caller should have cropped any paths to the destination render target instead of asking for
56 // an atlas larger than maxRenderTargetSize.
57 SkASSERT(fMaxTextureSize <= caps.maxTextureSize());
58 SkASSERT(specs.fMaxPreferredTextureSize > 0);
59
60 // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
61 // pending paths, favoring height over width if necessary.
62 int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
63 fHeight = 1 << ((log2area + 1) / 2);
64 fWidth = 1 << (log2area / 2);
65
66 fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
67 fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
68
69 if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
70 // They want to stuff a particularly large path into the atlas. Just punt and go with their
71 // min width and height. The atlas will grow as needed.
72 fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
73 fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
74 }
75
76 fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
77
78 GrColorType colorType = (CoverageType::kFP16_CoverageCount == fCoverageType)
79 ? GrColorType::kAlpha_F16 : GrColorType::kAlpha_8;
80 const GrBackendFormat format =
81 caps.getBackendFormatFromGrColorType(colorType, GrSRGBEncoded::kNo);
82 GrPixelConfig pixelConfig = (CoverageType::kFP16_CoverageCount == fCoverageType)
83 ? kAlpha_half_GrPixelConfig : kAlpha_8_GrPixelConfig;
84
85 fTextureProxy = GrProxyProvider::MakeFullyLazyProxy(
86 [this, pixelConfig](GrResourceProvider* resourceProvider) {
87 if (!resourceProvider) {
88 return sk_sp<GrTexture>();
89 }
90 if (!fBackingTexture) {
91 GrSurfaceDesc desc;
92 desc.fFlags = kRenderTarget_GrSurfaceFlag;
93 desc.fWidth = fWidth;
94 desc.fHeight = fHeight;
95 desc.fConfig = pixelConfig;
96 fBackingTexture = resourceProvider->createTexture(desc, SkBudgeted::kYes);
97 }
98 return fBackingTexture;
99 },
100 format, GrProxyProvider::Renderable::kYes, kTextureOrigin, pixelConfig, caps);
101 }
102
~GrCCAtlas()103 GrCCAtlas::~GrCCAtlas() {
104 }
105
addRect(const SkIRect & devIBounds,SkIVector * offset)106 bool GrCCAtlas::addRect(const SkIRect& devIBounds, SkIVector* offset) {
107 // This can't be called anymore once makeRenderTargetContext() has been called.
108 SkASSERT(!fTextureProxy->isInstantiated());
109
110 SkIPoint16 location;
111 if (!this->internalPlaceRect(devIBounds.width(), devIBounds.height(), &location)) {
112 return false;
113 }
114 offset->set(location.x() - devIBounds.left(), location.y() - devIBounds.top());
115
116 fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), location.x() + devIBounds.width());
117 fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), location.y() + devIBounds.height());
118 return true;
119 }
120
internalPlaceRect(int w,int h,SkIPoint16 * loc)121 bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
122 for (Node* node = fTopNode.get(); node; node = node->previous()) {
123 if (node->addRect(w, h, loc, fMaxTextureSize)) {
124 return true;
125 }
126 }
127
128 // The rect didn't fit. Grow the atlas and try again.
129 do {
130 if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
131 return false;
132 }
133 if (fHeight <= fWidth) {
134 int top = fHeight;
135 fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
136 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
137 } else {
138 int left = fWidth;
139 fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
140 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
141 }
142 } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
143
144 return true;
145 }
146
setFillBatchID(int id)147 void GrCCAtlas::setFillBatchID(int id) {
148 // This can't be called anymore once makeRenderTargetContext() has been called.
149 SkASSERT(!fTextureProxy->isInstantiated());
150 fFillBatchID = id;
151 }
152
setStrokeBatchID(int id)153 void GrCCAtlas::setStrokeBatchID(int id) {
154 // This can't be called anymore once makeRenderTargetContext() has been called.
155 SkASSERT(!fTextureProxy->isInstantiated());
156 fStrokeBatchID = id;
157 }
158
next_atlas_unique_id()159 static uint32_t next_atlas_unique_id() {
160 static std::atomic<uint32_t> nextID;
161 return nextID++;
162 }
163
refOrMakeCachedAtlas(GrOnFlushResourceProvider * onFlushRP)164 sk_sp<GrCCCachedAtlas> GrCCAtlas::refOrMakeCachedAtlas(GrOnFlushResourceProvider* onFlushRP) {
165 if (!fCachedAtlas) {
166 static const GrUniqueKey::Domain kAtlasDomain = GrUniqueKey::GenerateDomain();
167
168 GrUniqueKey atlasUniqueKey;
169 GrUniqueKey::Builder builder(&atlasUniqueKey, kAtlasDomain, 1, "CCPR Atlas");
170 builder[0] = next_atlas_unique_id();
171 builder.finish();
172
173 onFlushRP->assignUniqueKeyToProxy(atlasUniqueKey, fTextureProxy.get());
174
175 fCachedAtlas = sk_make_sp<GrCCCachedAtlas>(fCoverageType, atlasUniqueKey, fTextureProxy);
176 }
177
178 SkASSERT(fCachedAtlas->coverageType() == fCoverageType);
179 SkASSERT(fCachedAtlas->getOnFlushProxy() == fTextureProxy.get());
180 return fCachedAtlas;
181 }
182
makeRenderTargetContext(GrOnFlushResourceProvider * onFlushRP,sk_sp<GrTexture> backingTexture)183 sk_sp<GrRenderTargetContext> GrCCAtlas::makeRenderTargetContext(
184 GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
185 SkASSERT(!fTextureProxy->isInstantiated()); // This method should only be called once.
186 // Caller should have cropped any paths to the destination render target instead of asking for
187 // an atlas larger than maxRenderTargetSize.
188 SkASSERT(SkTMax(fHeight, fWidth) <= fMaxTextureSize);
189 SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
190
191 if (backingTexture) {
192 SkASSERT(backingTexture->config() == kAlpha_half_GrPixelConfig);
193 SkASSERT(backingTexture->width() == fWidth);
194 SkASSERT(backingTexture->height() == fHeight);
195 fBackingTexture = std::move(backingTexture);
196 }
197
198 sk_sp<GrRenderTargetContext> rtc =
199 onFlushRP->makeRenderTargetContext(fTextureProxy, nullptr, nullptr);
200 if (!rtc) {
201 SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
202 fWidth, fHeight);
203 return nullptr;
204 }
205
206 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
207 rtc->clear(&clearRect, SK_PMColor4fTRANSPARENT,
208 GrRenderTargetContext::CanClearFullscreen::kYes);
209 return rtc;
210 }
211
addRect(const SkIRect & devIBounds,SkIVector * devToAtlasOffset)212 GrCCAtlas* GrCCAtlasStack::addRect(const SkIRect& devIBounds, SkIVector* devToAtlasOffset) {
213 GrCCAtlas* retiredAtlas = nullptr;
214 if (fAtlases.empty() || !fAtlases.back().addRect(devIBounds, devToAtlasOffset)) {
215 // The retired atlas is out of room and can't grow any bigger.
216 retiredAtlas = !fAtlases.empty() ? &fAtlases.back() : nullptr;
217 fAtlases.emplace_back(fCoverageType, fSpecs, *fCaps);
218 SkASSERT(devIBounds.width() <= fSpecs.fMinWidth);
219 SkASSERT(devIBounds.height() <= fSpecs.fMinHeight);
220 SkAssertResult(fAtlases.back().addRect(devIBounds, devToAtlasOffset));
221 }
222 return retiredAtlas;
223 }
224