blob: a41eb38fcb577ae2bd1966f6422a237e179069a2 [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
Chris Dalton383a2ef2018-01-08 17:21:41 -05008#include "GrCCAtlas.h"
Chris Dalton1a325d22017-07-14 15:17:41 -06009
Chris Dalton9414c962018-06-14 10:14:50 -060010#include "GrCaps.h"
Chris Dalton383a2ef2018-01-08 17:21:41 -050011#include "GrOnFlushResourceProvider.h"
Chris Dalton4c458b12018-06-16 17:22:59 -060012#include "GrProxyProvider.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060013#include "GrRectanizer_skyline.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060014#include "GrRenderTargetContext.h"
Chris Dalton4c458b12018-06-16 17:22:59 -060015#include "GrTexture.h"
Chris Dalton383a2ef2018-01-08 17:21:41 -050016#include "GrTextureProxy.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060017#include "SkMakeUnique.h"
18#include "SkMathPriv.h"
Mike Klein0ec1c572018-12-04 11:52:51 -050019#include <atomic>
Chris Dalton1a325d22017-07-14 15:17:41 -060020
Chris Dalton383a2ef2018-01-08 17:21:41 -050021class GrCCAtlas::Node {
Chris Dalton1a325d22017-07-14 15:17:41 -060022public:
23 Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
Chris Dalton383a2ef2018-01-08 17:21:41 -050024 : fPrevious(std::move(previous)), fX(l), fY(t), fRectanizer(r - l, b - t) {}
Chris Dalton1a325d22017-07-14 15:17:41 -060025
26 Node* previous() const { return fPrevious.get(); }
27
Chris Dalton2612bae2018-02-22 13:41:37 -070028 bool addRect(int w, int h, SkIPoint16* loc, int maxAtlasSize) {
29 // Pad all paths except those that are expected to take up an entire physical texture.
30 if (w < maxAtlasSize) {
31 w = SkTMin(w + kPadding, maxAtlasSize);
32 }
33 if (h < maxAtlasSize) {
34 h = SkTMin(h + kPadding, maxAtlasSize);
35 }
36 if (!fRectanizer.addRect(w, h, loc)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060037 return false;
38 }
39 loc->fX += fX;
40 loc->fY += fY;
41 return true;
42 }
43
44private:
Chris Dalton383a2ef2018-01-08 17:21:41 -050045 const std::unique_ptr<Node> fPrevious;
46 const int fX, fY;
47 GrRectanizerSkyline fRectanizer;
Chris Dalton1a325d22017-07-14 15:17:41 -060048};
49
Chris Dalton4c458b12018-06-16 17:22:59 -060050GrCCAtlas::GrCCAtlas(GrPixelConfig pixelConfig, const Specs& specs, const GrCaps& caps)
Chris Dalton42c21152018-06-13 15:28:19 -060051 : fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
52 specs.fMaxPreferredTextureSize)) {
Chris Dalton4c458b12018-06-16 17:22:59 -060053 // Caller should have cropped any paths to the destination render target instead of asking for
54 // an atlas larger than maxRenderTargetSize.
55 SkASSERT(fMaxTextureSize <= caps.maxTextureSize());
Chris Dalton42c21152018-06-13 15:28:19 -060056 SkASSERT(specs.fMaxPreferredTextureSize > 0);
57
58 // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
59 // pending paths, favoring height over width if necessary.
60 int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
61 fHeight = 1 << ((log2area + 1) / 2);
62 fWidth = 1 << (log2area / 2);
63
64 fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
65 fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
66
67 if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
68 // They want to stuff a particularly large path into the atlas. Just punt and go with their
69 // min width and height. The atlas will grow as needed.
70 fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
71 fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
72 }
73
Chris Dalton2612bae2018-02-22 13:41:37 -070074 fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -060075
Greg Daniel4065d452018-11-16 15:43:41 -050076 // TODO: don't have this rely on the GrPixelConfig
77 GrSRGBEncoded srgbEncoded = GrSRGBEncoded::kNo;
78 GrColorType colorType = GrPixelConfigToColorTypeAndEncoding(pixelConfig, &srgbEncoded);
79
80 const GrBackendFormat format =
81 caps.getBackendFormatFromGrColorType(colorType, srgbEncoded);
82
Chris Dalton4c458b12018-06-16 17:22:59 -060083 fTextureProxy = GrProxyProvider::MakeFullyLazyProxy(
84 [this, pixelConfig](GrResourceProvider* resourceProvider) {
85 if (!resourceProvider) {
86 return sk_sp<GrTexture>();
87 }
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;
94 fBackingTexture = resourceProvider->createTexture(desc, SkBudgeted::kYes);
95 }
96 return fBackingTexture;
Chris Dalton4c458b12018-06-16 17:22:59 -060097 },
Greg Daniel4065d452018-11-16 15:43:41 -050098 format, GrProxyProvider::Renderable::kYes, kTextureOrigin, pixelConfig, caps);
Chris Dalton1a325d22017-07-14 15:17:41 -060099}
100
Chris Dalton2612bae2018-02-22 13:41:37 -0700101GrCCAtlas::~GrCCAtlas() {
102}
Chris Dalton1a325d22017-07-14 15:17:41 -0600103
Chris Dalton9414c962018-06-14 10:14:50 -0600104bool GrCCAtlas::addRect(const SkIRect& devIBounds, SkIVector* offset) {
Chris Dalton4c458b12018-06-16 17:22:59 -0600105 // This can't be called anymore once makeRenderTargetContext() has been called.
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400106 SkASSERT(!fTextureProxy->isInstantiated());
Chris Dalton1a325d22017-07-14 15:17:41 -0600107
Chris Dalton9414c962018-06-14 10:14:50 -0600108 SkIPoint16 location;
109 if (!this->internalPlaceRect(devIBounds.width(), devIBounds.height(), &location)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600110 return false;
111 }
Chris Dalton9414c962018-06-14 10:14:50 -0600112 offset->set(location.x() - devIBounds.left(), location.y() - devIBounds.top());
Chris Dalton1a325d22017-07-14 15:17:41 -0600113
Chris Dalton9414c962018-06-14 10:14:50 -0600114 fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), location.x() + devIBounds.width());
115 fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), location.y() + devIBounds.height());
Chris Dalton1a325d22017-07-14 15:17:41 -0600116 return true;
117}
118
Chris Dalton383a2ef2018-01-08 17:21:41 -0500119bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600120 for (Node* node = fTopNode.get(); node; node = node->previous()) {
Chris Dalton42c21152018-06-13 15:28:19 -0600121 if (node->addRect(w, h, loc, fMaxTextureSize)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600122 return true;
123 }
124 }
125
126 // The rect didn't fit. Grow the atlas and try again.
127 do {
Chris Dalton42c21152018-06-13 15:28:19 -0600128 if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600129 return false;
130 }
131 if (fHeight <= fWidth) {
132 int top = fHeight;
Chris Dalton42c21152018-06-13 15:28:19 -0600133 fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600134 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
135 } else {
136 int left = fWidth;
Chris Dalton42c21152018-06-13 15:28:19 -0600137 fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600138 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
139 }
Chris Dalton42c21152018-06-13 15:28:19 -0600140 } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
Chris Dalton1a325d22017-07-14 15:17:41 -0600141
142 return true;
143}
144
Chris Dalton09a7bb22018-08-31 19:53:15 +0800145void GrCCAtlas::setFillBatchID(int id) {
Chris Dalton4c458b12018-06-16 17:22:59 -0600146 // This can't be called anymore once makeRenderTargetContext() has been called.
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400147 SkASSERT(!fTextureProxy->isInstantiated());
Chris Dalton09a7bb22018-08-31 19:53:15 +0800148 fFillBatchID = id;
149}
150
151void GrCCAtlas::setStrokeBatchID(int id) {
152 // This can't be called anymore once makeRenderTargetContext() has been called.
153 SkASSERT(!fTextureProxy->isInstantiated());
154 fStrokeBatchID = id;
Chris Dalton4c458b12018-06-16 17:22:59 -0600155}
156
Chris Dalton4da70192018-06-18 09:51:36 -0600157static uint32_t next_atlas_unique_id() {
Mike Klein0ec1c572018-12-04 11:52:51 -0500158 static std::atomic<uint32_t> nextID;
159 return nextID++;
Chris Dalton4da70192018-06-18 09:51:36 -0600160}
161
162const GrUniqueKey& GrCCAtlas::getOrAssignUniqueKey(GrOnFlushResourceProvider* onFlushRP) {
163 static const GrUniqueKey::Domain kAtlasDomain = GrUniqueKey::GenerateDomain();
164
165 if (!fUniqueKey.isValid()) {
166 GrUniqueKey::Builder builder(&fUniqueKey, kAtlasDomain, 1, "CCPR Atlas");
167 builder[0] = next_atlas_unique_id();
168 builder.finish();
169
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400170 if (fTextureProxy->isInstantiated()) {
Chris Dalton4da70192018-06-18 09:51:36 -0600171 onFlushRP->assignUniqueKeyToProxy(fUniqueKey, fTextureProxy.get());
172 }
173 }
174 return fUniqueKey;
175}
176
Chris Dalton8429c792018-10-23 15:56:22 -0600177sk_sp<GrCCAtlas::CachedAtlasInfo> GrCCAtlas::refOrMakeCachedAtlasInfo(uint32_t contextUniqueID) {
Chris Dalton4da70192018-06-18 09:51:36 -0600178 if (!fCachedAtlasInfo) {
Chris Dalton8429c792018-10-23 15:56:22 -0600179 fCachedAtlasInfo = sk_make_sp<CachedAtlasInfo>(contextUniqueID);
Chris Dalton4da70192018-06-18 09:51:36 -0600180 }
Chris Dalton8429c792018-10-23 15:56:22 -0600181 SkASSERT(fCachedAtlasInfo->fContextUniqueID == contextUniqueID);
Chris Dalton4da70192018-06-18 09:51:36 -0600182 return fCachedAtlasInfo;
183}
184
Chris Dalton4c458b12018-06-16 17:22:59 -0600185sk_sp<GrRenderTargetContext> GrCCAtlas::makeRenderTargetContext(
Chris Daltonafde18f2018-06-22 12:44:19 -0600186 GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
Brian Salomonfd98c2c2018-07-31 17:25:29 -0400187 SkASSERT(!fTextureProxy->isInstantiated()); // This method should only be called once.
Chris Dalton4da70192018-06-18 09:51:36 -0600188 // Caller should have cropped any paths to the destination render target instead of asking for
189 // an atlas larger than maxRenderTargetSize.
190 SkASSERT(SkTMax(fHeight, fWidth) <= fMaxTextureSize);
Chris Dalton42c21152018-06-13 15:28:19 -0600191 SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
Chris Dalton1a325d22017-07-14 15:17:41 -0600192
Chris Daltonafde18f2018-06-22 12:44:19 -0600193 if (backingTexture) {
194 SkASSERT(backingTexture->config() == kAlpha_half_GrPixelConfig);
195 SkASSERT(backingTexture->width() == fWidth);
196 SkASSERT(backingTexture->height() == fHeight);
197 fBackingTexture = std::move(backingTexture);
198 }
199
Brian Salomon2a4f9832018-03-03 22:43:43 -0500200 sk_sp<GrRenderTargetContext> rtc =
Chris Dalton4c458b12018-06-16 17:22:59 -0600201 onFlushRP->makeRenderTargetContext(fTextureProxy, nullptr, nullptr);
Chris Dalton1a325d22017-07-14 15:17:41 -0600202 if (!rtc) {
203 SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
204 fWidth, fHeight);
205 return nullptr;
206 }
207
Chris Dalton4da70192018-06-18 09:51:36 -0600208 if (fUniqueKey.isValid()) {
209 onFlushRP->assignUniqueKeyToProxy(fUniqueKey, fTextureProxy.get());
210 }
211
Chris Dalton1a325d22017-07-14 15:17:41 -0600212 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
Brian Osman9a9baae2018-11-05 15:06:26 -0500213 rtc->clear(&clearRect, SK_PMColor4fTRANSPARENT,
214 GrRenderTargetContext::CanClearFullscreen::kYes);
Chris Dalton1a325d22017-07-14 15:17:41 -0600215 return rtc;
216}
Chris Dalton9414c962018-06-14 10:14:50 -0600217
Chris Dalton4c458b12018-06-16 17:22:59 -0600218GrCCAtlas* GrCCAtlasStack::addRect(const SkIRect& devIBounds, SkIVector* devToAtlasOffset) {
Chris Dalton9414c962018-06-14 10:14:50 -0600219 GrCCAtlas* retiredAtlas = nullptr;
Chris Dalton4c458b12018-06-16 17:22:59 -0600220 if (fAtlases.empty() || !fAtlases.back().addRect(devIBounds, devToAtlasOffset)) {
Chris Dalton9414c962018-06-14 10:14:50 -0600221 // The retired atlas is out of room and can't grow any bigger.
222 retiredAtlas = !fAtlases.empty() ? &fAtlases.back() : nullptr;
Chris Dalton4c458b12018-06-16 17:22:59 -0600223 fAtlases.emplace_back(fPixelConfig, fSpecs, *fCaps);
Chris Dalton9414c962018-06-14 10:14:50 -0600224 SkASSERT(devIBounds.width() <= fSpecs.fMinWidth);
225 SkASSERT(devIBounds.height() <= fSpecs.fMinHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -0600226 SkAssertResult(fAtlases.back().addRect(devIBounds, devToAtlasOffset));
Chris Dalton9414c962018-06-14 10:14:50 -0600227 }
228 return retiredAtlas;
229}