1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/gpu/GrDynamicAtlas.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "src/core/SkIPoint16.h"
11cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h"
12cb93a386Sopenharmony_ci#include "src/gpu/GrOnFlushResourceProvider.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrProxyProvider.h"
14cb93a386Sopenharmony_ci#include "src/gpu/GrRectanizerPow2.h"
15cb93a386Sopenharmony_ci#include "src/gpu/GrRectanizerSkyline.h"
16cb93a386Sopenharmony_ci#include "src/gpu/GrRenderTarget.h"
17cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h"
18cb93a386Sopenharmony_ci#include "src/gpu/GrSurfaceProxyPriv.h"
19cb93a386Sopenharmony_ci#include "src/gpu/GrSurfaceProxyView.h"
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci// Each Node covers a sub-rectangle of the final atlas. When a GrDynamicAtlas runs out of room, we
22cb93a386Sopenharmony_ci// create a new Node the same size as all combined nodes in the atlas as-is, and then place the new
23cb93a386Sopenharmony_ci// Node immediately below or beside the others (thereby doubling the size of the GyDynamicAtlas).
24cb93a386Sopenharmony_ciclass GrDynamicAtlas::Node {
25cb93a386Sopenharmony_cipublic:
26cb93a386Sopenharmony_ci    Node(Node* previous, GrRectanizer* rectanizer, int x, int y)
27cb93a386Sopenharmony_ci            : fPrevious(previous), fRectanizer(rectanizer), fX(x), fY(y) {}
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci    Node* previous() const { return fPrevious; }
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_ci    bool addRect(int w, int h, SkIPoint16* loc) {
32cb93a386Sopenharmony_ci        // Pad all paths except those that are expected to take up an entire physical texture.
33cb93a386Sopenharmony_ci        if (w < fRectanizer->width()) {
34cb93a386Sopenharmony_ci            w = std::min(w + kPadding, fRectanizer->width());
35cb93a386Sopenharmony_ci        }
36cb93a386Sopenharmony_ci        if (h < fRectanizer->height()) {
37cb93a386Sopenharmony_ci            h = std::min(h + kPadding, fRectanizer->height());
38cb93a386Sopenharmony_ci        }
39cb93a386Sopenharmony_ci        if (!fRectanizer->addRect(w, h, loc)) {
40cb93a386Sopenharmony_ci            return false;
41cb93a386Sopenharmony_ci        }
42cb93a386Sopenharmony_ci        loc->fX += fX;
43cb93a386Sopenharmony_ci        loc->fY += fY;
44cb93a386Sopenharmony_ci        return true;
45cb93a386Sopenharmony_ci    }
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ciprivate:
48cb93a386Sopenharmony_ci    Node* const fPrevious;
49cb93a386Sopenharmony_ci    GrRectanizer* const fRectanizer;
50cb93a386Sopenharmony_ci    const int fX, fY;
51cb93a386Sopenharmony_ci};
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_cisk_sp<GrTextureProxy> GrDynamicAtlas::MakeLazyAtlasProxy(
54cb93a386Sopenharmony_ci        LazyInstantiateAtlasCallback&& callback,
55cb93a386Sopenharmony_ci        GrColorType colorType,
56cb93a386Sopenharmony_ci        InternalMultisample internalMultisample,
57cb93a386Sopenharmony_ci        const GrCaps& caps,
58cb93a386Sopenharmony_ci        GrSurfaceProxy::UseAllocator useAllocator) {
59cb93a386Sopenharmony_ci    GrBackendFormat format = caps.getDefaultBackendFormat(colorType, GrRenderable::kYes);
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    int sampleCount = 1;
62cb93a386Sopenharmony_ci    if (InternalMultisample::kYes == internalMultisample) {
63cb93a386Sopenharmony_ci        sampleCount = caps.internalMultisampleCount(format);
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    sk_sp<GrTextureProxy> proxy =
67cb93a386Sopenharmony_ci            GrProxyProvider::MakeFullyLazyProxy(std::move(callback), format, GrRenderable::kYes,
68cb93a386Sopenharmony_ci                                                sampleCount, GrProtected::kNo, caps, useAllocator);
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    return proxy;
71cb93a386Sopenharmony_ci}
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ciGrDynamicAtlas::GrDynamicAtlas(GrColorType colorType, InternalMultisample internalMultisample,
74cb93a386Sopenharmony_ci                               SkISize initialSize, int maxAtlasSize, const GrCaps& caps,
75cb93a386Sopenharmony_ci                               RectanizerAlgorithm algorithm)
76cb93a386Sopenharmony_ci        : fColorType(colorType)
77cb93a386Sopenharmony_ci        , fInternalMultisample(internalMultisample)
78cb93a386Sopenharmony_ci        , fMaxAtlasSize(maxAtlasSize)
79cb93a386Sopenharmony_ci        , fRectanizerAlgorithm(algorithm) {
80cb93a386Sopenharmony_ci    SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
81cb93a386Sopenharmony_ci    this->reset(initialSize, caps);
82cb93a386Sopenharmony_ci}
83cb93a386Sopenharmony_ci
84cb93a386Sopenharmony_ciGrDynamicAtlas::~GrDynamicAtlas() {
85cb93a386Sopenharmony_ci}
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_civoid GrDynamicAtlas::reset(SkISize initialSize, const GrCaps& caps) {
88cb93a386Sopenharmony_ci    fNodeAllocator.reset();
89cb93a386Sopenharmony_ci    fWidth = std::min(SkNextPow2(initialSize.width()), fMaxAtlasSize);
90cb93a386Sopenharmony_ci    fHeight = std::min(SkNextPow2(initialSize.height()), fMaxAtlasSize);
91cb93a386Sopenharmony_ci    fTopNode = nullptr;
92cb93a386Sopenharmony_ci    fDrawBounds.setEmpty();
93cb93a386Sopenharmony_ci    fTextureProxy = MakeLazyAtlasProxy(
94cb93a386Sopenharmony_ci            [this](GrResourceProvider* resourceProvider, const LazyAtlasDesc& desc) {
95cb93a386Sopenharmony_ci                if (!fBackingTexture) {
96cb93a386Sopenharmony_ci                    fBackingTexture = resourceProvider->createTexture(
97cb93a386Sopenharmony_ci                            fTextureProxy->backingStoreDimensions(),
98cb93a386Sopenharmony_ci                            desc.fFormat,
99cb93a386Sopenharmony_ci                            desc.fTextureType,
100cb93a386Sopenharmony_ci                            desc.fRenderable,
101cb93a386Sopenharmony_ci                            desc.fSampleCnt,
102cb93a386Sopenharmony_ci                            desc.fMipmapped,
103cb93a386Sopenharmony_ci                            desc.fBudgeted,
104cb93a386Sopenharmony_ci                            desc.fProtected);
105cb93a386Sopenharmony_ci                }
106cb93a386Sopenharmony_ci                return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
107cb93a386Sopenharmony_ci            },
108cb93a386Sopenharmony_ci            fColorType, fInternalMultisample, caps, GrSurfaceProxy::UseAllocator::kNo);
109cb93a386Sopenharmony_ci    fBackingTexture = nullptr;
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ciGrDynamicAtlas::Node* GrDynamicAtlas::makeNode(Node* previous, int l, int t, int r, int b) {
113cb93a386Sopenharmony_ci    int width = r - l;
114cb93a386Sopenharmony_ci    int height = b - t;
115cb93a386Sopenharmony_ci    GrRectanizer* rectanizer = (fRectanizerAlgorithm == RectanizerAlgorithm::kSkyline)
116cb93a386Sopenharmony_ci            ? (GrRectanizer*)fNodeAllocator.make<GrRectanizerSkyline>(width, height)
117cb93a386Sopenharmony_ci            : fNodeAllocator.make<GrRectanizerPow2>(width, height);
118cb93a386Sopenharmony_ci    return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
119cb93a386Sopenharmony_ci}
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_ciGrSurfaceProxyView GrDynamicAtlas::readView(const GrCaps& caps) const {
122cb93a386Sopenharmony_ci    return {fTextureProxy, kTextureOrigin,
123cb93a386Sopenharmony_ci            caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
124cb93a386Sopenharmony_ci}
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ciGrSurfaceProxyView GrDynamicAtlas::writeView(const GrCaps& caps) const {
127cb93a386Sopenharmony_ci    return {fTextureProxy, kTextureOrigin,
128cb93a386Sopenharmony_ci            caps.getWriteSwizzle(fTextureProxy->backendFormat(), fColorType)};
129cb93a386Sopenharmony_ci}
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_cibool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
132cb93a386Sopenharmony_ci    // This can't be called anymore once instantiate() has been called.
133cb93a386Sopenharmony_ci    SkASSERT(!this->isInstantiated());
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci    if (!this->internalPlaceRect(width, height, location)) {
136cb93a386Sopenharmony_ci        return false;
137cb93a386Sopenharmony_ci    }
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci    fDrawBounds.fWidth = std::max(fDrawBounds.width(), location->x() + width);
140cb93a386Sopenharmony_ci    fDrawBounds.fHeight = std::max(fDrawBounds.height(), location->y() + height);
141cb93a386Sopenharmony_ci    return true;
142cb93a386Sopenharmony_ci}
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_cibool GrDynamicAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
145cb93a386Sopenharmony_ci    if (std::max(h, w) > fMaxAtlasSize) {
146cb93a386Sopenharmony_ci        return false;
147cb93a386Sopenharmony_ci    }
148cb93a386Sopenharmony_ci    if (std::min(h, w) <= 0) {
149cb93a386Sopenharmony_ci        loc->set(0, 0);
150cb93a386Sopenharmony_ci        return true;
151cb93a386Sopenharmony_ci    }
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci    if (!fTopNode) {
154cb93a386Sopenharmony_ci        if (w > fWidth) {
155cb93a386Sopenharmony_ci            fWidth = std::min(SkNextPow2(w), fMaxAtlasSize);
156cb93a386Sopenharmony_ci        }
157cb93a386Sopenharmony_ci        if (h > fHeight) {
158cb93a386Sopenharmony_ci            fHeight = std::min(SkNextPow2(h), fMaxAtlasSize);
159cb93a386Sopenharmony_ci        }
160cb93a386Sopenharmony_ci        fTopNode = this->makeNode(nullptr, 0, 0, fWidth, fHeight);
161cb93a386Sopenharmony_ci    }
162cb93a386Sopenharmony_ci
163cb93a386Sopenharmony_ci    for (Node* node = fTopNode; node; node = node->previous()) {
164cb93a386Sopenharmony_ci        if (node->addRect(w, h, loc)) {
165cb93a386Sopenharmony_ci            return true;
166cb93a386Sopenharmony_ci        }
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    // The rect didn't fit. Grow the atlas and try again.
170cb93a386Sopenharmony_ci    do {
171cb93a386Sopenharmony_ci        if (fWidth >= fMaxAtlasSize && fHeight >= fMaxAtlasSize) {
172cb93a386Sopenharmony_ci            return false;
173cb93a386Sopenharmony_ci        }
174cb93a386Sopenharmony_ci        if (fHeight <= fWidth) {
175cb93a386Sopenharmony_ci            int top = fHeight;
176cb93a386Sopenharmony_ci            fHeight = std::min(fHeight * 2, fMaxAtlasSize);
177cb93a386Sopenharmony_ci            fTopNode = this->makeNode(fTopNode, 0, top, fWidth, fHeight);
178cb93a386Sopenharmony_ci        } else {
179cb93a386Sopenharmony_ci            int left = fWidth;
180cb93a386Sopenharmony_ci            fWidth = std::min(fWidth * 2, fMaxAtlasSize);
181cb93a386Sopenharmony_ci            fTopNode = this->makeNode(fTopNode, left, 0, fWidth, fHeight);
182cb93a386Sopenharmony_ci        }
183cb93a386Sopenharmony_ci    } while (!fTopNode->addRect(w, h, loc));
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci    return true;
186cb93a386Sopenharmony_ci}
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_civoid GrDynamicAtlas::instantiate(GrOnFlushResourceProvider* onFlushRP,
189cb93a386Sopenharmony_ci                                 sk_sp<GrTexture> backingTexture) {
190cb93a386Sopenharmony_ci    SkASSERT(!this->isInstantiated());  // This method should only be called once.
191cb93a386Sopenharmony_ci    // Caller should have cropped any paths to the destination render target instead of asking for
192cb93a386Sopenharmony_ci    // an atlas larger than maxRenderTargetSize.
193cb93a386Sopenharmony_ci    SkASSERT(std::max(fHeight, fWidth) <= fMaxAtlasSize);
194cb93a386Sopenharmony_ci    SkASSERT(fMaxAtlasSize <= onFlushRP->caps()->maxRenderTargetSize());
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci    if (fTextureProxy->isFullyLazy()) {
197cb93a386Sopenharmony_ci        // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
198cb93a386Sopenharmony_ci        // knows we only intend to write out a smaller sub-rectangle of the backing texture.
199cb93a386Sopenharmony_ci        fTextureProxy->priv().setLazyDimensions(fDrawBounds);
200cb93a386Sopenharmony_ci    }
201cb93a386Sopenharmony_ci    SkASSERT(fTextureProxy->dimensions() == fDrawBounds);
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci    if (backingTexture) {
204cb93a386Sopenharmony_ci#ifdef SK_DEBUG
205cb93a386Sopenharmony_ci        auto backingRT = backingTexture->asRenderTarget();
206cb93a386Sopenharmony_ci        SkASSERT(backingRT);
207cb93a386Sopenharmony_ci        SkASSERT(backingRT->backendFormat() == fTextureProxy->backendFormat());
208cb93a386Sopenharmony_ci        SkASSERT(backingRT->numSamples() == fTextureProxy->asRenderTargetProxy()->numSamples());
209cb93a386Sopenharmony_ci        SkASSERT(backingRT->dimensions() == fTextureProxy->backingStoreDimensions());
210cb93a386Sopenharmony_ci#endif
211cb93a386Sopenharmony_ci        fBackingTexture = std::move(backingTexture);
212cb93a386Sopenharmony_ci    }
213cb93a386Sopenharmony_ci    onFlushRP->instatiateProxy(fTextureProxy.get());
214cb93a386Sopenharmony_ci}
215