blob: b336e8c58f207b567c53b049b718534dfec2a770 [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"
Chris Dalton1a325d22017-07-14 15:17:41 -060019
Chris Dalton383a2ef2018-01-08 17:21:41 -050020class GrCCAtlas::Node {
Chris Dalton1a325d22017-07-14 15:17:41 -060021public:
22 Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
Chris Dalton383a2ef2018-01-08 17:21:41 -050023 : fPrevious(std::move(previous)), fX(l), fY(t), fRectanizer(r - l, b - t) {}
Chris Dalton1a325d22017-07-14 15:17:41 -060024
25 Node* previous() const { return fPrevious.get(); }
26
Chris Dalton2612bae2018-02-22 13:41:37 -070027 bool addRect(int w, int h, SkIPoint16* loc, int maxAtlasSize) {
28 // Pad all paths except those that are expected to take up an entire physical texture.
29 if (w < maxAtlasSize) {
30 w = SkTMin(w + kPadding, maxAtlasSize);
31 }
32 if (h < maxAtlasSize) {
33 h = SkTMin(h + kPadding, maxAtlasSize);
34 }
35 if (!fRectanizer.addRect(w, h, loc)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060036 return false;
37 }
38 loc->fX += fX;
39 loc->fY += fY;
40 return true;
41 }
42
43private:
Chris Dalton383a2ef2018-01-08 17:21:41 -050044 const std::unique_ptr<Node> fPrevious;
45 const int fX, fY;
46 GrRectanizerSkyline fRectanizer;
Chris Dalton1a325d22017-07-14 15:17:41 -060047};
48
Chris Dalton4c458b12018-06-16 17:22:59 -060049GrCCAtlas::GrCCAtlas(GrPixelConfig pixelConfig, const Specs& specs, const GrCaps& caps)
Chris Dalton42c21152018-06-13 15:28:19 -060050 : fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
51 specs.fMaxPreferredTextureSize)) {
Chris Dalton4c458b12018-06-16 17:22:59 -060052 // Caller should have cropped any paths to the destination render target instead of asking for
53 // an atlas larger than maxRenderTargetSize.
54 SkASSERT(fMaxTextureSize <= caps.maxTextureSize());
Chris Dalton42c21152018-06-13 15:28:19 -060055 SkASSERT(specs.fMaxPreferredTextureSize > 0);
56
57 // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
58 // pending paths, favoring height over width if necessary.
59 int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
60 fHeight = 1 << ((log2area + 1) / 2);
61 fWidth = 1 << (log2area / 2);
62
63 fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
64 fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
65
66 if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
67 // They want to stuff a particularly large path into the atlas. Just punt and go with their
68 // min width and height. The atlas will grow as needed.
69 fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
70 fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
71 }
72
Chris Dalton2612bae2018-02-22 13:41:37 -070073 fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -060074
75 fTextureProxy = GrProxyProvider::MakeFullyLazyProxy(
76 [this, pixelConfig](GrResourceProvider* resourceProvider) {
77 if (!resourceProvider) {
78 return sk_sp<GrTexture>();
79 }
Chris Daltonafde18f2018-06-22 12:44:19 -060080 if (!fBackingTexture) {
81 GrSurfaceDesc desc;
82 desc.fFlags = kRenderTarget_GrSurfaceFlag;
83 desc.fWidth = fWidth;
84 desc.fHeight = fHeight;
85 desc.fConfig = pixelConfig;
86 fBackingTexture = resourceProvider->createTexture(desc, SkBudgeted::kYes);
87 }
88 return fBackingTexture;
Chris Dalton4c458b12018-06-16 17:22:59 -060089 },
90 GrProxyProvider::Renderable::kYes, kTextureOrigin, pixelConfig, caps);
Chris Dalton1a325d22017-07-14 15:17:41 -060091}
92
Chris Dalton2612bae2018-02-22 13:41:37 -070093GrCCAtlas::~GrCCAtlas() {
94}
Chris Dalton1a325d22017-07-14 15:17:41 -060095
Chris Dalton9414c962018-06-14 10:14:50 -060096bool GrCCAtlas::addRect(const SkIRect& devIBounds, SkIVector* offset) {
Chris Dalton4c458b12018-06-16 17:22:59 -060097 // This can't be called anymore once makeRenderTargetContext() has been called.
98 SkASSERT(!fTextureProxy->priv().isInstantiated());
Chris Dalton1a325d22017-07-14 15:17:41 -060099
Chris Dalton9414c962018-06-14 10:14:50 -0600100 SkIPoint16 location;
101 if (!this->internalPlaceRect(devIBounds.width(), devIBounds.height(), &location)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600102 return false;
103 }
Chris Dalton9414c962018-06-14 10:14:50 -0600104 offset->set(location.x() - devIBounds.left(), location.y() - devIBounds.top());
Chris Dalton1a325d22017-07-14 15:17:41 -0600105
Chris Dalton9414c962018-06-14 10:14:50 -0600106 fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), location.x() + devIBounds.width());
107 fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), location.y() + devIBounds.height());
Chris Dalton1a325d22017-07-14 15:17:41 -0600108 return true;
109}
110
Chris Dalton383a2ef2018-01-08 17:21:41 -0500111bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600112 for (Node* node = fTopNode.get(); node; node = node->previous()) {
Chris Dalton42c21152018-06-13 15:28:19 -0600113 if (node->addRect(w, h, loc, fMaxTextureSize)) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600114 return true;
115 }
116 }
117
118 // The rect didn't fit. Grow the atlas and try again.
119 do {
Chris Dalton42c21152018-06-13 15:28:19 -0600120 if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600121 return false;
122 }
123 if (fHeight <= fWidth) {
124 int top = fHeight;
Chris Dalton42c21152018-06-13 15:28:19 -0600125 fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600126 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
127 } else {
128 int left = fWidth;
Chris Dalton42c21152018-06-13 15:28:19 -0600129 fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600130 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
131 }
Chris Dalton42c21152018-06-13 15:28:19 -0600132 } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
Chris Dalton1a325d22017-07-14 15:17:41 -0600133
134 return true;
135}
136
Chris Dalton4c458b12018-06-16 17:22:59 -0600137void GrCCAtlas::setUserBatchID(int id) {
138 // This can't be called anymore once makeRenderTargetContext() has been called.
139 SkASSERT(!fTextureProxy->priv().isInstantiated());
140 fUserBatchID = id;
141}
142
Chris Dalton4da70192018-06-18 09:51:36 -0600143static uint32_t next_atlas_unique_id() {
144 static int32_t nextID;
145 return sk_atomic_inc(&nextID);
146}
147
148const GrUniqueKey& GrCCAtlas::getOrAssignUniqueKey(GrOnFlushResourceProvider* onFlushRP) {
149 static const GrUniqueKey::Domain kAtlasDomain = GrUniqueKey::GenerateDomain();
150
151 if (!fUniqueKey.isValid()) {
152 GrUniqueKey::Builder builder(&fUniqueKey, kAtlasDomain, 1, "CCPR Atlas");
153 builder[0] = next_atlas_unique_id();
154 builder.finish();
155
156 if (fTextureProxy->priv().isInstantiated()) {
157 onFlushRP->assignUniqueKeyToProxy(fUniqueKey, fTextureProxy.get());
158 }
159 }
160 return fUniqueKey;
161}
162
163sk_sp<GrCCAtlas::CachedAtlasInfo> GrCCAtlas::refOrMakeCachedAtlasInfo() {
164 if (!fCachedAtlasInfo) {
165 fCachedAtlasInfo = sk_make_sp<CachedAtlasInfo>();
166 }
167 return fCachedAtlasInfo;
168}
169
Chris Dalton4c458b12018-06-16 17:22:59 -0600170sk_sp<GrRenderTargetContext> GrCCAtlas::makeRenderTargetContext(
Chris Daltonafde18f2018-06-22 12:44:19 -0600171 GrOnFlushResourceProvider* onFlushRP, sk_sp<GrTexture> backingTexture) {
Chris Dalton4c458b12018-06-16 17:22:59 -0600172 SkASSERT(!fTextureProxy->priv().isInstantiated()); // This method should only be called once.
Chris Dalton4da70192018-06-18 09:51:36 -0600173 // Caller should have cropped any paths to the destination render target instead of asking for
174 // an atlas larger than maxRenderTargetSize.
175 SkASSERT(SkTMax(fHeight, fWidth) <= fMaxTextureSize);
Chris Dalton42c21152018-06-13 15:28:19 -0600176 SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
Chris Dalton1a325d22017-07-14 15:17:41 -0600177
Chris Daltonafde18f2018-06-22 12:44:19 -0600178 if (backingTexture) {
179 SkASSERT(backingTexture->config() == kAlpha_half_GrPixelConfig);
180 SkASSERT(backingTexture->width() == fWidth);
181 SkASSERT(backingTexture->height() == fHeight);
182 fBackingTexture = std::move(backingTexture);
183 }
184
Brian Salomon2a4f9832018-03-03 22:43:43 -0500185 sk_sp<GrRenderTargetContext> rtc =
Chris Dalton4c458b12018-06-16 17:22:59 -0600186 onFlushRP->makeRenderTargetContext(fTextureProxy, nullptr, nullptr);
Chris Dalton1a325d22017-07-14 15:17:41 -0600187 if (!rtc) {
188 SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
189 fWidth, fHeight);
190 return nullptr;
191 }
192
Chris Dalton4da70192018-06-18 09:51:36 -0600193 if (fUniqueKey.isValid()) {
194 onFlushRP->assignUniqueKeyToProxy(fUniqueKey, fTextureProxy.get());
195 }
196
Chris Dalton1a325d22017-07-14 15:17:41 -0600197 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
Chris Dalton344e9032017-12-11 15:42:09 -0700198 rtc->clear(&clearRect, 0, GrRenderTargetContext::CanClearFullscreen::kYes);
Chris Dalton1a325d22017-07-14 15:17:41 -0600199 return rtc;
200}
Chris Dalton9414c962018-06-14 10:14:50 -0600201
Chris Dalton4c458b12018-06-16 17:22:59 -0600202GrCCAtlas* GrCCAtlasStack::addRect(const SkIRect& devIBounds, SkIVector* devToAtlasOffset) {
Chris Dalton9414c962018-06-14 10:14:50 -0600203 GrCCAtlas* retiredAtlas = nullptr;
Chris Dalton4c458b12018-06-16 17:22:59 -0600204 if (fAtlases.empty() || !fAtlases.back().addRect(devIBounds, devToAtlasOffset)) {
Chris Dalton9414c962018-06-14 10:14:50 -0600205 // The retired atlas is out of room and can't grow any bigger.
206 retiredAtlas = !fAtlases.empty() ? &fAtlases.back() : nullptr;
Chris Dalton4c458b12018-06-16 17:22:59 -0600207 fAtlases.emplace_back(fPixelConfig, fSpecs, *fCaps);
Chris Dalton9414c962018-06-14 10:14:50 -0600208 SkASSERT(devIBounds.width() <= fSpecs.fMinWidth);
209 SkASSERT(devIBounds.height() <= fSpecs.fMinHeight);
Chris Dalton4c458b12018-06-16 17:22:59 -0600210 SkAssertResult(fAtlases.back().addRect(devIBounds, devToAtlasOffset));
Chris Dalton9414c962018-06-14 10:14:50 -0600211 }
212 return retiredAtlas;
213}