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