blob: 3cfb5d0a4dd93f2395aeae7f67c9243bca720f6f [file] [log] [blame]
Chris Dalton1a325d22017-07-14 15:17:41 -06001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ccpr/GrCCAtlas.h"
Chris Dalton1a325d22017-07-14 15:17:41 -06009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/gpu/GrTexture.h"
11#include "include/private/GrTextureProxy.h"
12#include "src/core/SkIPoint16.h"
13#include "src/core/SkMakeUnique.h"
14#include "src/core/SkMathPriv.h"
15#include "src/gpu/GrCaps.h"
16#include "src/gpu/GrOnFlushResourceProvider.h"
17#include "src/gpu/GrProxyProvider.h"
18#include "src/gpu/GrRectanizer_skyline.h"
19#include "src/gpu/GrRenderTargetContext.h"
20#include "src/gpu/ccpr/GrCCPathCache.h"
Mike Klein0ec1c572018-12-04 11:52:51 -050021#include <atomic>
Chris Dalton1a325d22017-07-14 15:17:41 -060022
Chris Dalton383a2ef2018-01-08 17:21:41 -050023class GrCCAtlas::Node {
Chris Dalton1a325d22017-07-14 15:17:41 -060024public:
25 Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
Chris Dalton383a2ef2018-01-08 17:21:41 -050026 : fPrevious(std::move(previous)), fX(l), fY(t), fRectanizer(r - l, b - t) {}
Chris Dalton1a325d22017-07-14 15:17:41 -060027
28 Node* previous() const { return fPrevious.get(); }
29
Chris Dalton2612bae2018-02-22 13:41:37 -070030 bool addRect(int w, int h, SkIPoint16* loc, int maxAtlasSize) {
31 // Pad all paths except those that are expected to take up an entire physical texture.
32 if (w < maxAtlasSize) {
33 w = SkTMin(w + kPadding, maxAtlasSize);
34 }
35 if (h < maxAtlasSize) {
36 h = SkTMin(h + kPadding, maxAtlasSize);
37 }
38 if (!fRectanizer.addRect(w, h, loc)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060039 return false;
40 }
41 loc->fX += fX;
42 loc->fY += fY;
43 return true;
44 }
45
46private:
Chris Dalton383a2ef2018-01-08 17:21:41 -050047 const std::unique_ptr<Node> fPrevious;
48 const int fX, fY;
49 GrRectanizerSkyline fRectanizer;
Chris Dalton1a325d22017-07-14 15:17:41 -060050};
51
Chris Dalton351e80c2019-01-06 22:51:00 -070052GrCCAtlas::GrCCAtlas(CoverageType coverageType, const Specs& specs, const GrCaps& caps)
53 : fCoverageType(coverageType)
54 , fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
Chris Dalton42c21152018-06-13 15:28:19 -060055 specs.fMaxPreferredTextureSize)) {
Chris Dalton4c458b12018-06-16 17:22:59 -060056 // Caller should have cropped any paths to the destination render target instead of asking for
57 // an atlas larger than maxRenderTargetSize.
58 SkASSERT(fMaxTextureSize <= caps.maxTextureSize());
Chris Dalton42c21152018-06-13 15:28:19 -060059 SkASSERT(specs.fMaxPreferredTextureSize > 0);
60
61 // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
62 // pending paths, favoring height over width if necessary.
63 int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
64 fHeight = 1 << ((log2area + 1) / 2);
65 fWidth = 1 << (log2area / 2);
66
67 fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
68 fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
69
70 if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
71 // They want to stuff a particularly large path into the atlas. Just punt and go with their
72 // min width and height. The atlas will grow as needed.
73 fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
74 fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
75 }
76
Chris Dalton2612bae2018-02-22 13:41:37 -070077 fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -060078
Chris Dalton351e80c2019-01-06 22:51:00 -070079 GrColorType colorType = (CoverageType::kFP16_CoverageCount == fCoverageType)
80 ? GrColorType::kAlpha_F16 : GrColorType::kAlpha_8;
Greg Daniel4065d452018-11-16 15:43:41 -050081 const GrBackendFormat format =
Chris Dalton351e80c2019-01-06 22:51:00 -070082 caps.getBackendFormatFromGrColorType(colorType, GrSRGBEncoded::kNo);
83 GrPixelConfig pixelConfig = (CoverageType::kFP16_CoverageCount == fCoverageType)
84 ? kAlpha_half_GrPixelConfig : kAlpha_8_GrPixelConfig;
Greg Daniel4065d452018-11-16 15:43:41 -050085
Chris Dalton4c458b12018-06-16 17:22:59 -060086 fTextureProxy = GrProxyProvider::MakeFullyLazyProxy(
87 [this, pixelConfig](GrResourceProvider* resourceProvider) {
Chris Daltonafde18f2018-06-22 12:44:19 -060088 if (!fBackingTexture) {
89 GrSurfaceDesc desc;
90 desc.fFlags = kRenderTarget_GrSurfaceFlag;
91 desc.fWidth = fWidth;
92 desc.fHeight = fHeight;
93 desc.fConfig = pixelConfig;
Robert Phillips9313aa72019-04-09 18:41:27 -040094 fBackingTexture = resourceProvider->createTexture(
95 desc, SkBudgeted::kYes, GrResourceProvider::Flags::kNoPendingIO);
Chris Daltonafde18f2018-06-22 12:44:19 -060096 }
Brian Salomonb6a3a3b2019-04-01 12:29:34 -040097 return GrSurfaceProxy::LazyInstantiationResult(fBackingTexture);
Chris Dalton4c458b12018-06-16 17:22:59 -060098 },
Greg Daniel4065d452018-11-16 15:43:41 -050099 format, GrProxyProvider::Renderable::kYes, kTextureOrigin, pixelConfig, caps);
Robert Phillips5f78adf2019-04-22 12:41:39 -0400100
101 fTextureProxy->priv().setIgnoredByResourceAllocator();
Chris Dalton1a325d22017-07-14 15:17:41 -0600102}
103
Chris Dalton2612bae2018-02-22 13:41:37 -0700104GrCCAtlas::~GrCCAtlas() {
105}
Chris Dalton1a325d22017-07-14 15:17:41 -0600106
Chris Dalton9414c962018-06-14 10:14:50 -0600107bool GrCCAtlas::addRect(const SkIRect& devIBounds, SkIVector* offset) {
Chris Dalton4c458b12018-06-16 17:22:59 -0600108 // This can't be called anymore once makeRenderTargetContext() has been called.
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400109 SkASSERT(!fTextureProxy->isInstantiated());
Chris Dalton1a325d22017-07-14 15:17:41 -0600110
Chris Dalton9414c962018-06-14 10:14:50 -0600111 SkIPoint16 location;
112 if (!this->internalPlaceRect(devIBounds.width(), devIBounds.height(), &location)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600113 return false;
114 }
Chris Dalton9414c962018-06-14 10:14:50 -0600115 offset->set(location.x() - devIBounds.left(), location.y() - devIBounds.top());
Chris Dalton1a325d22017-07-14 15:17:41 -0600116
Chris Dalton9414c962018-06-14 10:14:50 -0600117 fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), location.x() + devIBounds.width());
118 fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), location.y() + devIBounds.height());
Chris Dalton1a325d22017-07-14 15:17:41 -0600119 return true;
120}
121
Chris Dalton383a2ef2018-01-08 17:21:41 -0500122bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600123 for (Node* node = fTopNode.get(); node; node = node->previous()) {
Chris Dalton42c21152018-06-13 15:28:19 -0600124 if (node->addRect(w, h, loc, fMaxTextureSize)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600125 return true;
126 }
127 }
128
129 // The rect didn't fit. Grow the atlas and try again.
130 do {
Chris Dalton42c21152018-06-13 15:28:19 -0600131 if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600132 return false;
133 }
134 if (fHeight <= fWidth) {
135 int top = fHeight;
Chris Dalton42c21152018-06-13 15:28:19 -0600136 fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600137 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
138 } else {
139 int left = fWidth;
Chris Dalton42c21152018-06-13 15:28:19 -0600140 fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600141 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
142 }
Chris Dalton42c21152018-06-13 15:28:19 -0600143 } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
Chris Dalton1a325d22017-07-14 15:17:41 -0600144
145 return true;
146}
147
Chris Dalton09a7bb22018-08-31 19:53:15 +0800148void GrCCAtlas::setFillBatchID(int id) {
Chris Dalton4c458b12018-06-16 17:22:59 -0600149 // This can't be called anymore once makeRenderTargetContext() has been called.
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400150 SkASSERT(!fTextureProxy->isInstantiated());
Chris Dalton09a7bb22018-08-31 19:53:15 +0800151 fFillBatchID = id;
152}
153
154void GrCCAtlas::setStrokeBatchID(int id) {
155 // This can't be called anymore once makeRenderTargetContext() has been called.
156 SkASSERT(!fTextureProxy->isInstantiated());
157 fStrokeBatchID = id;
Chris Dalton4c458b12018-06-16 17:22:59 -0600158}
159
Chris Dalton4da70192018-06-18 09:51:36 -0600160static uint32_t next_atlas_unique_id() {
Mike Klein0ec1c572018-12-04 11:52:51 -0500161 static std::atomic<uint32_t> nextID;
162 return nextID++;
Chris Dalton4da70192018-06-18 09:51:36 -0600163}
164
Chris Dalton351e80c2019-01-06 22:51:00 -0700165sk_sp<GrCCCachedAtlas> GrCCAtlas::refOrMakeCachedAtlas(GrOnFlushResourceProvider* onFlushRP) {
166 if (!fCachedAtlas) {
167 static const GrUniqueKey::Domain kAtlasDomain = GrUniqueKey::GenerateDomain();
Chris Dalton4da70192018-06-18 09:51:36 -0600168
Chris Dalton351e80c2019-01-06 22:51:00 -0700169 GrUniqueKey atlasUniqueKey;
170 GrUniqueKey::Builder builder(&atlasUniqueKey, kAtlasDomain, 1, "CCPR Atlas");
Chris Dalton4da70192018-06-18 09:51:36 -0600171 builder[0] = next_atlas_unique_id();
172 builder.finish();
173
Chris Dalton351e80c2019-01-06 22:51:00 -0700174 onFlushRP->assignUniqueKeyToProxy(atlasUniqueKey, fTextureProxy.get());
Chris Daltond6fa4542019-01-04 13:23:51 -0700175
Chris Dalton351e80c2019-01-06 22:51:00 -0700176 fCachedAtlas = sk_make_sp<GrCCCachedAtlas>(fCoverageType, atlasUniqueKey, fTextureProxy);
Chris Dalton2e825a32019-01-04 22:14:27 +0000177 }
Chris Dalton351e80c2019-01-06 22:51:00 -0700178
179 SkASSERT(fCachedAtlas->coverageType() == fCoverageType);
180 SkASSERT(fCachedAtlas->getOnFlushProxy() == fTextureProxy.get());
181 return fCachedAtlas;
Chris Dalton4da70192018-06-18 09:51:36 -0600182}
183
Chris Dalton4c458b12018-06-16 17:22:59 -0600184sk_sp<GrRenderTargetContext> GrCCAtlas::makeRenderTargetContext(
Chris Daltonafde18f2018-06-22 12:44:19 -0600185 GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400186 SkASSERT(!fTextureProxy->isInstantiated()); // This method should only be called once.
Chris Dalton4da70192018-06-18 09:51:36 -0600187 // Caller should have cropped any paths to the destination render target instead of asking for
188 // an atlas larger than maxRenderTargetSize.
189 SkASSERT(SkTMax(fHeight, fWidth) <= fMaxTextureSize);
Chris Dalton42c21152018-06-13 15:28:19 -0600190 SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
Chris Dalton1a325d22017-07-14 15:17:41 -0600191
Chris Daltonf91b7552019-04-29 16:21:18 -0600192 // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
193 // knows we only intend to write out a smaller sub-rectangle of the backing texture.
194 fTextureProxy->priv().setLazySize(fDrawBounds.width(), fDrawBounds.height());
195
Chris Daltonafde18f2018-06-22 12:44:19 -0600196 if (backingTexture) {
197 SkASSERT(backingTexture->config() == kAlpha_half_GrPixelConfig);
198 SkASSERT(backingTexture->width() == fWidth);
199 SkASSERT(backingTexture->height() == fHeight);
200 fBackingTexture = std::move(backingTexture);
201 }
202
Brian Salomon2a4f9832018-03-03 22:43:43 -0500203 sk_sp<GrRenderTargetContext> rtc =
Chris Dalton4c458b12018-06-16 17:22:59 -0600204 onFlushRP->makeRenderTargetContext(fTextureProxy, nullptr, nullptr);
Chris Dalton1a325d22017-07-14 15:17:41 -0600205 if (!rtc) {
206 SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
207 fWidth, fHeight);
208 return nullptr;
209 }
210
211 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
Brian Osman9a9baae2018-11-05 15:06:26 -0500212 rtc->clear(&clearRect, SK_PMColor4fTRANSPARENT,
213 GrRenderTargetContext::CanClearFullscreen::kYes);
Chris Dalton1a325d22017-07-14 15:17:41 -0600214 return rtc;
215}
Chris Dalton9414c962018-06-14 10:14:50 -0600216
Chris Dalton4c458b12018-06-16 17:22:59 -0600217GrCCAtlas* GrCCAtlasStack::addRect(const SkIRect& devIBounds, SkIVector* devToAtlasOffset) {
Chris Dalton9414c962018-06-14 10:14:50 -0600218 GrCCAtlas* retiredAtlas = nullptr;
Chris Dalton4c458b12018-06-16 17:22:59 -0600219 if (fAtlases.empty() || !fAtlases.back().addRect(devIBounds, devToAtlasOffset)) {
Chris Dalton9414c962018-06-14 10:14:50 -0600220 // The retired atlas is out of room and can't grow any bigger.
221 retiredAtlas = !fAtlases.empty() ? &fAtlases.back() : nullptr;
Chris Dalton351e80c2019-01-06 22:51:00 -0700222 fAtlases.emplace_back(fCoverageType, fSpecs, *fCaps);
Chris Dalton9414c962018-06-14 10:14:50 -0600223 SkASSERT(devIBounds.width() <= fSpecs.fMinWidth);
224 SkASSERT(devIBounds.height() <= fSpecs.fMinHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -0600225 SkAssertResult(fAtlases.back().addRect(devIBounds, devToAtlasOffset));
Chris Dalton9414c962018-06-14 10:14:50 -0600226 }
227 return retiredAtlas;
228}