blob: b1e5bb1a591598436519c155b6d732549366743c [file] [log] [blame]
Chris Daltona550cf22020-02-07 13:35:31 -07001/*
2 * Copyright 2020 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
8#include "src/gpu/GrDynamicAtlas.h"
9
Chris Daltone5a141d2020-05-19 11:57:53 -060010#include "src/core/SkIPoint16.h"
Robert Phillips400f52e2021-07-26 13:23:10 -040011#include "src/gpu/GrCaps.h"
Chris Daltona550cf22020-02-07 13:35:31 -070012#include "src/gpu/GrOnFlushResourceProvider.h"
13#include "src/gpu/GrProxyProvider.h"
Chris Daltond2dc8dd2020-05-19 16:32:02 -060014#include "src/gpu/GrRectanizerPow2.h"
Chris Daltona550cf22020-02-07 13:35:31 -070015#include "src/gpu/GrRectanizerSkyline.h"
16#include "src/gpu/GrRenderTarget.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040017#include "src/gpu/GrResourceProvider.h"
Robert Phillips400f52e2021-07-26 13:23:10 -040018#include "src/gpu/GrSurfaceProxyPriv.h"
19#include "src/gpu/GrSurfaceProxyView.h"
Chris Daltona550cf22020-02-07 13:35:31 -070020
21// Each Node covers a sub-rectangle of the final atlas. When a GrDynamicAtlas runs out of room, we
22// create a new Node the same size as all combined nodes in the atlas as-is, and then place the new
23// Node immediately below or beside the others (thereby doubling the size of the GyDynamicAtlas).
24class GrDynamicAtlas::Node {
25public:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060026 Node(Node* previous, GrRectanizer* rectanizer, int x, int y)
27 : fPrevious(previous), fRectanizer(rectanizer), fX(x), fY(y) {}
Chris Daltona550cf22020-02-07 13:35:31 -070028
Chris Daltond2dc8dd2020-05-19 16:32:02 -060029 Node* previous() const { return fPrevious; }
Chris Daltona550cf22020-02-07 13:35:31 -070030
31 bool addRect(int w, int h, SkIPoint16* loc) {
32 // Pad all paths except those that are expected to take up an entire physical texture.
Chris Daltond2dc8dd2020-05-19 16:32:02 -060033 if (w < fRectanizer->width()) {
34 w = std::min(w + kPadding, fRectanizer->width());
Chris Daltona550cf22020-02-07 13:35:31 -070035 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060036 if (h < fRectanizer->height()) {
37 h = std::min(h + kPadding, fRectanizer->height());
Chris Daltona550cf22020-02-07 13:35:31 -070038 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -060039 if (!fRectanizer->addRect(w, h, loc)) {
Chris Daltona550cf22020-02-07 13:35:31 -070040 return false;
41 }
42 loc->fX += fX;
43 loc->fY += fY;
44 return true;
45 }
46
47private:
Chris Daltond2dc8dd2020-05-19 16:32:02 -060048 Node* const fPrevious;
49 GrRectanizer* const fRectanizer;
Chris Daltona550cf22020-02-07 13:35:31 -070050 const int fX, fY;
Chris Daltona550cf22020-02-07 13:35:31 -070051};
52
53sk_sp<GrTextureProxy> GrDynamicAtlas::MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040054 LazyInstantiateAtlasCallback&& callback,
55 GrColorType colorType,
56 InternalMultisample internalMultisample,
57 const GrCaps& caps,
Chris Daltona550cf22020-02-07 13:35:31 -070058 GrSurfaceProxy::UseAllocator useAllocator) {
59 GrBackendFormat format = caps.getDefaultBackendFormat(colorType, GrRenderable::kYes);
Chris Daltonf83d0342020-02-10 14:19:30 -070060
61 int sampleCount = 1;
Chris Dalton57ab06c2021-04-22 12:57:28 -060062 if (InternalMultisample::kYes == internalMultisample) {
Chris Daltonf83d0342020-02-10 14:19:30 -070063 sampleCount = caps.internalMultisampleCount(format);
64 }
Chris Daltona550cf22020-02-07 13:35:31 -070065
Brian Salomondf1bd6d2020-03-26 20:37:01 -040066 sk_sp<GrTextureProxy> proxy =
Brian Salomon63410e92020-03-23 18:32:50 -040067 GrProxyProvider::MakeFullyLazyProxy(std::move(callback), format, GrRenderable::kYes,
Brian Salomondf1bd6d2020-03-26 20:37:01 -040068 sampleCount, GrProtected::kNo, caps, useAllocator);
Chris Daltona550cf22020-02-07 13:35:31 -070069
70 return proxy;
71}
72
73GrDynamicAtlas::GrDynamicAtlas(GrColorType colorType, InternalMultisample internalMultisample,
Chris Daltond2dc8dd2020-05-19 16:32:02 -060074 SkISize initialSize, int maxAtlasSize, const GrCaps& caps,
75 RectanizerAlgorithm algorithm)
Chris Daltona550cf22020-02-07 13:35:31 -070076 : fColorType(colorType)
77 , fInternalMultisample(internalMultisample)
Chris Daltond2dc8dd2020-05-19 16:32:02 -060078 , fMaxAtlasSize(maxAtlasSize)
79 , fRectanizerAlgorithm(algorithm) {
Chris Daltona550cf22020-02-07 13:35:31 -070080 SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
81 this->reset(initialSize, caps);
82}
83
84GrDynamicAtlas::~GrDynamicAtlas() {
85}
86
87void GrDynamicAtlas::reset(SkISize initialSize, const GrCaps& caps) {
Chris Daltond2dc8dd2020-05-19 16:32:02 -060088 fNodeAllocator.reset();
Chris Daltona550cf22020-02-07 13:35:31 -070089 fWidth = std::min(SkNextPow2(initialSize.width()), fMaxAtlasSize);
90 fHeight = std::min(SkNextPow2(initialSize.height()), fMaxAtlasSize);
91 fTopNode = nullptr;
92 fDrawBounds.setEmpty();
93 fTextureProxy = MakeLazyAtlasProxy(
Brian Salomon63410e92020-03-23 18:32:50 -040094 [this](GrResourceProvider* resourceProvider, const LazyAtlasDesc& desc) {
Chris Daltona550cf22020-02-07 13:35:31 -070095 if (!fBackingTexture) {
96 fBackingTexture = resourceProvider->createTexture(
Greg Daniel0e9d34d2021-08-13 16:20:18 -040097 fTextureProxy->backingStoreDimensions(),
98 desc.fFormat,
99 desc.fFormat.textureType(),
100 desc.fRenderable,
101 desc.fSampleCnt,
102 desc.fMipmapped,
103 desc.fBudgeted,
104 desc.fProtected);
Chris Daltona550cf22020-02-07 13:35:31 -0700105 }
106 return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
107 },
108 fColorType, fInternalMultisample, caps, GrSurfaceProxy::UseAllocator::kNo);
109 fBackingTexture = nullptr;
110}
111
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600112GrDynamicAtlas::Node* GrDynamicAtlas::makeNode(Node* previous, int l, int t, int r, int b) {
113 int width = r - l;
114 int height = b - t;
115 GrRectanizer* rectanizer = (fRectanizerAlgorithm == RectanizerAlgorithm::kSkyline)
116 ? (GrRectanizer*)fNodeAllocator.make<GrRectanizerSkyline>(width, height)
117 : fNodeAllocator.make<GrRectanizerPow2>(width, height);
118 return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
119}
120
Chris Dalton83420eb2021-06-23 18:47:09 -0600121GrSurfaceProxyView GrDynamicAtlas::readView(const GrCaps& caps) const {
Chris Daltonabed2672021-06-17 16:54:28 -0600122 return {fTextureProxy, kTextureOrigin,
123 caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
124}
125
Chris Dalton83420eb2021-06-23 18:47:09 -0600126GrSurfaceProxyView GrDynamicAtlas::writeView(const GrCaps& caps) const {
127 return {fTextureProxy, kTextureOrigin,
128 caps.getWriteSwizzle(fTextureProxy->backendFormat(), fColorType)};
129}
130
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600131bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
Chris Daltona550cf22020-02-07 13:35:31 -0700132 // This can't be called anymore once instantiate() has been called.
133 SkASSERT(!this->isInstantiated());
134
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600135 if (!this->internalPlaceRect(width, height, location)) {
Chris Daltona550cf22020-02-07 13:35:31 -0700136 return false;
137 }
Chris Daltona550cf22020-02-07 13:35:31 -0700138
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600139 fDrawBounds.fWidth = std::max(fDrawBounds.width(), location->x() + width);
140 fDrawBounds.fHeight = std::max(fDrawBounds.height(), location->y() + height);
Chris Daltona550cf22020-02-07 13:35:31 -0700141 return true;
142}
143
144bool GrDynamicAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
145 if (std::max(h, w) > fMaxAtlasSize) {
146 return false;
147 }
148 if (std::min(h, w) <= 0) {
149 loc->set(0, 0);
150 return true;
151 }
152
153 if (!fTopNode) {
154 if (w > fWidth) {
155 fWidth = std::min(SkNextPow2(w), fMaxAtlasSize);
156 }
157 if (h > fHeight) {
158 fHeight = std::min(SkNextPow2(h), fMaxAtlasSize);
159 }
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600160 fTopNode = this->makeNode(nullptr, 0, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700161 }
162
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600163 for (Node* node = fTopNode; node; node = node->previous()) {
Chris Daltona550cf22020-02-07 13:35:31 -0700164 if (node->addRect(w, h, loc)) {
165 return true;
166 }
167 }
168
169 // The rect didn't fit. Grow the atlas and try again.
170 do {
171 if (fWidth >= fMaxAtlasSize && fHeight >= fMaxAtlasSize) {
172 return false;
173 }
174 if (fHeight <= fWidth) {
175 int top = fHeight;
176 fHeight = std::min(fHeight * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600177 fTopNode = this->makeNode(fTopNode, 0, top, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700178 } else {
179 int left = fWidth;
180 fWidth = std::min(fWidth * 2, fMaxAtlasSize);
Chris Daltond2dc8dd2020-05-19 16:32:02 -0600181 fTopNode = this->makeNode(fTopNode, left, 0, fWidth, fHeight);
Chris Daltona550cf22020-02-07 13:35:31 -0700182 }
183 } while (!fTopNode->addRect(w, h, loc));
184
185 return true;
186}
187
Chris Dalton83420eb2021-06-23 18:47:09 -0600188void GrDynamicAtlas::instantiate(GrOnFlushResourceProvider* onFlushRP,
189 sk_sp<GrTexture> backingTexture) {
Chris Daltona550cf22020-02-07 13:35:31 -0700190 SkASSERT(!this->isInstantiated()); // This method should only be called once.
191 // Caller should have cropped any paths to the destination render target instead of asking for
192 // an atlas larger than maxRenderTargetSize.
193 SkASSERT(std::max(fHeight, fWidth) <= fMaxAtlasSize);
194 SkASSERT(fMaxAtlasSize <= onFlushRP->caps()->maxRenderTargetSize());
195
Chris Dalton83420eb2021-06-23 18:47:09 -0600196 if (fTextureProxy->isFullyLazy()) {
197 // Finalize the content size of our proxy. The GPU can potentially make optimizations if it
198 // knows we only intend to write out a smaller sub-rectangle of the backing texture.
199 fTextureProxy->priv().setLazyDimensions(fDrawBounds);
200 }
201 SkASSERT(fTextureProxy->dimensions() == fDrawBounds);
Chris Daltona550cf22020-02-07 13:35:31 -0700202
203 if (backingTexture) {
204#ifdef SK_DEBUG
205 auto backingRT = backingTexture->asRenderTarget();
206 SkASSERT(backingRT);
207 SkASSERT(backingRT->backendFormat() == fTextureProxy->backendFormat());
208 SkASSERT(backingRT->numSamples() == fTextureProxy->asRenderTargetProxy()->numSamples());
Chris Dalton83420eb2021-06-23 18:47:09 -0600209 SkASSERT(backingRT->dimensions() == fTextureProxy->backingStoreDimensions());
Chris Daltona550cf22020-02-07 13:35:31 -0700210#endif
211 fBackingTexture = std::move(backingTexture);
212 }
Chris Dalton83420eb2021-06-23 18:47:09 -0600213 onFlushRP->instatiateProxy(fTextureProxy.get());
Chris Daltona550cf22020-02-07 13:35:31 -0700214}