blob: cbf6993f043eb1c7f0a6dc93ccbaf3d30573d48e [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 Dalton1a325d22017-07-14 15:17:41 -060012#include "GrRectanizer_skyline.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060013#include "GrRenderTargetContext.h"
Chris Dalton383a2ef2018-01-08 17:21:41 -050014#include "GrTextureProxy.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060015#include "SkMakeUnique.h"
16#include "SkMathPriv.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060017
Chris Dalton383a2ef2018-01-08 17:21:41 -050018class GrCCAtlas::Node {
Chris Dalton1a325d22017-07-14 15:17:41 -060019public:
20 Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
Chris Dalton383a2ef2018-01-08 17:21:41 -050021 : fPrevious(std::move(previous)), fX(l), fY(t), fRectanizer(r - l, b - t) {}
Chris Dalton1a325d22017-07-14 15:17:41 -060022
23 Node* previous() const { return fPrevious.get(); }
24
Chris Dalton2612bae2018-02-22 13:41:37 -070025 bool addRect(int w, int h, SkIPoint16* loc, int maxAtlasSize) {
26 // Pad all paths except those that are expected to take up an entire physical texture.
27 if (w < maxAtlasSize) {
28 w = SkTMin(w + kPadding, maxAtlasSize);
29 }
30 if (h < maxAtlasSize) {
31 h = SkTMin(h + kPadding, maxAtlasSize);
32 }
33 if (!fRectanizer.addRect(w, h, loc)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060034 return false;
35 }
36 loc->fX += fX;
37 loc->fY += fY;
38 return true;
39 }
40
41private:
Chris Dalton383a2ef2018-01-08 17:21:41 -050042 const std::unique_ptr<Node> fPrevious;
43 const int fX, fY;
44 GrRectanizerSkyline fRectanizer;
Chris Dalton1a325d22017-07-14 15:17:41 -060045};
46
Chris Dalton42c21152018-06-13 15:28:19 -060047GrCCAtlas::GrCCAtlas(const Specs& specs)
48 : fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
49 specs.fMaxPreferredTextureSize)) {
50 SkASSERT(specs.fMaxPreferredTextureSize > 0);
51
52 // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
53 // pending paths, favoring height over width if necessary.
54 int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
55 fHeight = 1 << ((log2area + 1) / 2);
56 fWidth = 1 << (log2area / 2);
57
58 fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
59 fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
60
61 if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
62 // They want to stuff a particularly large path into the atlas. Just punt and go with their
63 // min width and height. The atlas will grow as needed.
64 fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
65 fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
66 }
67
Chris Dalton2612bae2018-02-22 13:41:37 -070068 fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
Chris Dalton1a325d22017-07-14 15:17:41 -060069}
70
Chris Dalton2612bae2018-02-22 13:41:37 -070071GrCCAtlas::~GrCCAtlas() {
72}
Chris Dalton1a325d22017-07-14 15:17:41 -060073
Chris Dalton9414c962018-06-14 10:14:50 -060074bool GrCCAtlas::addRect(const SkIRect& devIBounds, SkIVector* offset) {
75 // This can't be called anymore once makeClearedTextureProxy() has been called.
Chris Dalton1a325d22017-07-14 15:17:41 -060076 SkASSERT(!fTextureProxy);
77
Chris Dalton9414c962018-06-14 10:14:50 -060078 SkIPoint16 location;
79 if (!this->internalPlaceRect(devIBounds.width(), devIBounds.height(), &location)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060080 return false;
81 }
Chris Dalton9414c962018-06-14 10:14:50 -060082 offset->set(location.x() - devIBounds.left(), location.y() - devIBounds.top());
Chris Dalton1a325d22017-07-14 15:17:41 -060083
Chris Dalton9414c962018-06-14 10:14:50 -060084 fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), location.x() + devIBounds.width());
85 fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), location.y() + devIBounds.height());
Chris Dalton1a325d22017-07-14 15:17:41 -060086 return true;
87}
88
Chris Dalton383a2ef2018-01-08 17:21:41 -050089bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
Chris Dalton1a325d22017-07-14 15:17:41 -060090 for (Node* node = fTopNode.get(); node; node = node->previous()) {
Chris Dalton42c21152018-06-13 15:28:19 -060091 if (node->addRect(w, h, loc, fMaxTextureSize)) {
Chris Dalton1a325d22017-07-14 15:17:41 -060092 return true;
93 }
94 }
95
96 // The rect didn't fit. Grow the atlas and try again.
97 do {
Chris Dalton42c21152018-06-13 15:28:19 -060098 if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
Chris Dalton1a325d22017-07-14 15:17:41 -060099 return false;
100 }
101 if (fHeight <= fWidth) {
102 int top = fHeight;
Chris Dalton42c21152018-06-13 15:28:19 -0600103 fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600104 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
105 } else {
106 int left = fWidth;
Chris Dalton42c21152018-06-13 15:28:19 -0600107 fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
Chris Dalton1a325d22017-07-14 15:17:41 -0600108 fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
109 }
Chris Dalton42c21152018-06-13 15:28:19 -0600110 } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
Chris Dalton1a325d22017-07-14 15:17:41 -0600111
112 return true;
113}
114
Chris Dalton9414c962018-06-14 10:14:50 -0600115sk_sp<GrRenderTargetContext> GrCCAtlas::initInternalTextureProxy(
116 GrOnFlushResourceProvider* onFlushRP, GrPixelConfig config) {
Chris Dalton1a325d22017-07-14 15:17:41 -0600117 SkASSERT(!fTextureProxy);
Chris Dalton42c21152018-06-13 15:28:19 -0600118 // Caller should have cropped any paths to the destination render target instead of asking for
119 // an atlas larger than maxRenderTargetSize.
Chris Dalton9414c962018-06-14 10:14:50 -0600120 SkASSERT(SkTMax(fHeight, fWidth) <= fMaxTextureSize);
Chris Dalton42c21152018-06-13 15:28:19 -0600121 SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
Chris Dalton1a325d22017-07-14 15:17:41 -0600122
123 GrSurfaceDesc desc;
Robert Phillipsce5209a2018-02-13 11:13:51 -0500124 desc.fFlags = kRenderTarget_GrSurfaceFlag;
Chris Dalton1a325d22017-07-14 15:17:41 -0600125 desc.fWidth = fWidth;
126 desc.fHeight = fHeight;
Chris Dalton9414c962018-06-14 10:14:50 -0600127 desc.fConfig = config;
Brian Salomon2a4f9832018-03-03 22:43:43 -0500128 sk_sp<GrRenderTargetContext> rtc =
Chris Dalton9414c962018-06-14 10:14:50 -0600129 onFlushRP->makeRenderTargetContext(desc, kTextureOrigin, nullptr, nullptr);
Chris Dalton1a325d22017-07-14 15:17:41 -0600130 if (!rtc) {
131 SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
132 fWidth, fHeight);
133 return nullptr;
134 }
135
136 SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
Chris Dalton344e9032017-12-11 15:42:09 -0700137 rtc->clear(&clearRect, 0, GrRenderTargetContext::CanClearFullscreen::kYes);
Chris Dalton9ca27842018-01-18 12:24:50 -0700138
Chris Dalton1a325d22017-07-14 15:17:41 -0600139 fTextureProxy = sk_ref_sp(rtc->asTextureProxy());
140 return rtc;
141}
Chris Dalton9414c962018-06-14 10:14:50 -0600142
143GrCCAtlas* GrCCAtlasStack::addRect(const SkIRect& devIBounds, SkIVector* offset) {
144 GrCCAtlas* retiredAtlas = nullptr;
145 if (fAtlases.empty() || !fAtlases.back().addRect(devIBounds, offset)) {
146 // The retired atlas is out of room and can't grow any bigger.
147 retiredAtlas = !fAtlases.empty() ? &fAtlases.back() : nullptr;
148 fAtlases.emplace_back(fSpecs);
149 SkASSERT(devIBounds.width() <= fSpecs.fMinWidth);
150 SkASSERT(devIBounds.height() <= fSpecs.fMinHeight);
151 SkAssertResult(fAtlases.back().addRect(devIBounds, offset));
152 }
153 return retiredAtlas;
154}