1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2019 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/AtlasPathRenderer.h"
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrBackendSurface.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrContextOptions.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSpan_impl.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkIPoint16.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrClip.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDirectContextPriv.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDrawingManager.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDynamicAtlas.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrPaint.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTargetProxy.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTask.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrStyle.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxyView.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTexture.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTextureProxy.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceDrawContext.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrModulateAtlasCoverageEffect.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrStyledShape.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/AtlasRenderTask.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/DrawAtlasPathOp.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/GrOp.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/TessellationPathRenderer.h"
47*c8dee2aaSAndroid Build Coastguard Worker
48*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
49*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
52*c8dee2aaSAndroid Build Coastguard Worker
53*c8dee2aaSAndroid Build Coastguard Worker namespace {
54*c8dee2aaSAndroid Build Coastguard Worker
55*c8dee2aaSAndroid Build Coastguard Worker // Returns the rect [topLeftFloor, botRightCeil], which is the rect [r] rounded out to integer
56*c8dee2aaSAndroid Build Coastguard Worker // boundaries.
round_out(const SkRect & r)57*c8dee2aaSAndroid Build Coastguard Worker std::pair<skvx::float2, skvx::float2> round_out(const SkRect& r) {
58*c8dee2aaSAndroid Build Coastguard Worker return {floor(skvx::float2::Load(&r.fLeft)),
59*c8dee2aaSAndroid Build Coastguard Worker ceil(skvx::float2::Load(&r.fRight))};
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker // Returns whether the given proxyOwner uses the atlasProxy.
refs_atlas(const T * proxyOwner,const GrSurfaceProxy * atlasProxy)63*c8dee2aaSAndroid Build Coastguard Worker template<typename T> bool refs_atlas(const T* proxyOwner, const GrSurfaceProxy* atlasProxy) {
64*c8dee2aaSAndroid Build Coastguard Worker bool refsAtlas = false;
65*c8dee2aaSAndroid Build Coastguard Worker auto checkForAtlasRef = [atlasProxy, &refsAtlas](GrSurfaceProxy* proxy, skgpu::Mipmapped) {
66*c8dee2aaSAndroid Build Coastguard Worker if (proxy == atlasProxy) {
67*c8dee2aaSAndroid Build Coastguard Worker refsAtlas = true;
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker };
70*c8dee2aaSAndroid Build Coastguard Worker if (proxyOwner) {
71*c8dee2aaSAndroid Build Coastguard Worker proxyOwner->visitProxies(checkForAtlasRef);
72*c8dee2aaSAndroid Build Coastguard Worker }
73*c8dee2aaSAndroid Build Coastguard Worker return refsAtlas;
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
is_visible(const SkRect & pathDevBounds,const SkIRect & clipBounds)76*c8dee2aaSAndroid Build Coastguard Worker bool is_visible(const SkRect& pathDevBounds, const SkIRect& clipBounds) {
77*c8dee2aaSAndroid Build Coastguard Worker auto pathTopLeft = skvx::float2::Load(&pathDevBounds.fLeft);
78*c8dee2aaSAndroid Build Coastguard Worker auto pathBotRight = skvx::float2::Load(&pathDevBounds.fRight);
79*c8dee2aaSAndroid Build Coastguard Worker // Empty paths are never visible. Phrase this as a NOT of positive logic so we also return false
80*c8dee2aaSAndroid Build Coastguard Worker // in the case of NaN.
81*c8dee2aaSAndroid Build Coastguard Worker if (!all(pathTopLeft < pathBotRight)) {
82*c8dee2aaSAndroid Build Coastguard Worker return false;
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker auto clipTopLeft = skvx::cast<float>(skvx::int2::Load(&clipBounds.fLeft));
85*c8dee2aaSAndroid Build Coastguard Worker auto clipBotRight = skvx::cast<float>(skvx::int2::Load(&clipBounds.fRight));
86*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(clipBounds) == sizeof(clipTopLeft) + sizeof(clipBotRight));
87*c8dee2aaSAndroid Build Coastguard Worker return all(pathTopLeft < clipBotRight) && all(pathBotRight > clipTopLeft);
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
91*c8dee2aaSAndroid Build Coastguard Worker // Ensures the atlas dependencies are set up such that each atlas will be totally out of service
92*c8dee2aaSAndroid Build Coastguard Worker // before we render the next one in line. This means there will only ever be one atlas active at a
93*c8dee2aaSAndroid Build Coastguard Worker // time and that they can all share the same texture.
validate_atlas_dependencies(const TArray<sk_sp<skgpu::ganesh::AtlasRenderTask>> & atlasTasks)94*c8dee2aaSAndroid Build Coastguard Worker void validate_atlas_dependencies(
95*c8dee2aaSAndroid Build Coastguard Worker const TArray<sk_sp<skgpu::ganesh::AtlasRenderTask>>& atlasTasks) {
96*c8dee2aaSAndroid Build Coastguard Worker for (int i = atlasTasks.size() - 1; i >= 1; --i) {
97*c8dee2aaSAndroid Build Coastguard Worker auto atlasTask = atlasTasks[i].get();
98*c8dee2aaSAndroid Build Coastguard Worker auto previousAtlasTask = atlasTasks[i - 1].get();
99*c8dee2aaSAndroid Build Coastguard Worker // Double check that atlasTask depends on every dependent of its previous atlas. If this
100*c8dee2aaSAndroid Build Coastguard Worker // fires it might mean previousAtlasTask gained a new dependent after atlasTask came into
101*c8dee2aaSAndroid Build Coastguard Worker // service (maybe by an op that hadn't yet been added to an opsTask when we registered the
102*c8dee2aaSAndroid Build Coastguard Worker // new atlas with the drawingManager).
103*c8dee2aaSAndroid Build Coastguard Worker for (GrRenderTask* previousAtlasUser : previousAtlasTask->dependents()) {
104*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(atlasTask->dependsOn(previousAtlasUser));
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker }
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker #endif
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
111*c8dee2aaSAndroid Build Coastguard Worker
112*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::ganesh {
113*c8dee2aaSAndroid Build Coastguard Worker
114*c8dee2aaSAndroid Build Coastguard Worker constexpr static auto kAtlasAlpha8Type = GrColorType::kAlpha_8;
115*c8dee2aaSAndroid Build Coastguard Worker constexpr static int kAtlasInitialSize = 512;
116*c8dee2aaSAndroid Build Coastguard Worker
117*c8dee2aaSAndroid Build Coastguard Worker // The atlas is only used for small-area paths, which means at least one dimension of every path is
118*c8dee2aaSAndroid Build Coastguard Worker // guaranteed to be quite small. So if we transpose tall paths, then every path will have a small
119*c8dee2aaSAndroid Build Coastguard Worker // height, which lends very well to efficient pow2 atlas packing.
120*c8dee2aaSAndroid Build Coastguard Worker constexpr static auto kAtlasAlgorithm = GrDynamicAtlas::RectanizerAlgorithm::kPow2;
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker // Ensure every path in the atlas falls in or below the 256px high rectanizer band.
123*c8dee2aaSAndroid Build Coastguard Worker constexpr static int kAtlasMaxPathHeight = 256;
124*c8dee2aaSAndroid Build Coastguard Worker
125*c8dee2aaSAndroid Build Coastguard Worker // If we have MSAA to fall back on, paths are already fast enough that we really only benefit from
126*c8dee2aaSAndroid Build Coastguard Worker // atlasing when they are very small.
127*c8dee2aaSAndroid Build Coastguard Worker constexpr static int kAtlasMaxPathHeightWithMSAAFallback = 128;
128*c8dee2aaSAndroid Build Coastguard Worker
129*c8dee2aaSAndroid Build Coastguard Worker // http://skbug.com/12291 -- The way GrDynamicAtlas works, a single 2048x1 path is given an entire
130*c8dee2aaSAndroid Build Coastguard Worker // 2048x2048 atlas with draw bounds of 2048x1025. Limit the max width to 1024 to avoid this landmine
131*c8dee2aaSAndroid Build Coastguard Worker // until it's resolved.
132*c8dee2aaSAndroid Build Coastguard Worker constexpr static int kAtlasMaxPathWidth = 1024;
133*c8dee2aaSAndroid Build Coastguard Worker
IsSupported(GrRecordingContext * rContext)134*c8dee2aaSAndroid Build Coastguard Worker bool AtlasPathRenderer::IsSupported(GrRecordingContext* rContext) {
135*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_BUILD_FOR_IOS
136*c8dee2aaSAndroid Build Coastguard Worker // b/195095846: There is a bug with the atlas path renderer on OpenGL iOS. Disable until we can
137*c8dee2aaSAndroid Build Coastguard Worker // investigate.
138*c8dee2aaSAndroid Build Coastguard Worker if (rContext->backend() == GrBackendApi::kOpenGL) {
139*c8dee2aaSAndroid Build Coastguard Worker return false;
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker #endif
142*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_BUILD_FOR_WIN
143*c8dee2aaSAndroid Build Coastguard Worker // http://skbug.com/13519 There is a bug with the atlas path renderer on Direct3D, running on
144*c8dee2aaSAndroid Build Coastguard Worker // Radeon hardware and possibly others. Disable until we can investigate.
145*c8dee2aaSAndroid Build Coastguard Worker if (rContext->backend() == GrBackendApi::kDirect3D) {
146*c8dee2aaSAndroid Build Coastguard Worker return false;
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker #endif
149*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps = *rContext->priv().caps();
150*c8dee2aaSAndroid Build Coastguard Worker auto atlasFormat = caps.getDefaultBackendFormat(kAtlasAlpha8Type, GrRenderable::kYes);
151*c8dee2aaSAndroid Build Coastguard Worker return rContext->asDirectContext() && // The atlas doesn't support DDL yet.
152*c8dee2aaSAndroid Build Coastguard Worker caps.internalMultisampleCount(atlasFormat) > 1 &&
153*c8dee2aaSAndroid Build Coastguard Worker // GrAtlasRenderTask currently requires tessellation. In the future it could use the
154*c8dee2aaSAndroid Build Coastguard Worker // default path renderer when tessellation isn't available.
155*c8dee2aaSAndroid Build Coastguard Worker TessellationPathRenderer::IsSupported(caps);
156*c8dee2aaSAndroid Build Coastguard Worker }
157*c8dee2aaSAndroid Build Coastguard Worker
Make(GrRecordingContext * rContext)158*c8dee2aaSAndroid Build Coastguard Worker sk_sp<AtlasPathRenderer> AtlasPathRenderer::Make(GrRecordingContext* rContext) {
159*c8dee2aaSAndroid Build Coastguard Worker return IsSupported(rContext)
160*c8dee2aaSAndroid Build Coastguard Worker ? sk_sp<AtlasPathRenderer>(new AtlasPathRenderer(rContext->asDirectContext()))
161*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker
AtlasPathRenderer(GrDirectContext * dContext)164*c8dee2aaSAndroid Build Coastguard Worker AtlasPathRenderer::AtlasPathRenderer(GrDirectContext* dContext) {
165*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(IsSupported(dContext));
166*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps = *dContext->priv().caps();
167*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
168*c8dee2aaSAndroid Build Coastguard Worker fAtlasMaxSize = dContext->priv().options().fMaxTextureAtlasSize;
169*c8dee2aaSAndroid Build Coastguard Worker #else
170*c8dee2aaSAndroid Build Coastguard Worker fAtlasMaxSize = 2048;
171*c8dee2aaSAndroid Build Coastguard Worker #endif
172*c8dee2aaSAndroid Build Coastguard Worker fAtlasMaxSize = SkPrevPow2(std::min(fAtlasMaxSize, (float)caps.maxPreferredRenderTargetSize()));
173*c8dee2aaSAndroid Build Coastguard Worker fAtlasMaxPathWidth = std::min((float)kAtlasMaxPathWidth, fAtlasMaxSize);
174*c8dee2aaSAndroid Build Coastguard Worker fAtlasInitialSize = SkNextPow2(std::min(kAtlasInitialSize, (int)fAtlasMaxSize));
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
pathFitsInAtlas(const SkRect & pathDevBounds,GrAAType fallbackAAType) const177*c8dee2aaSAndroid Build Coastguard Worker bool AtlasPathRenderer::pathFitsInAtlas(const SkRect& pathDevBounds,
178*c8dee2aaSAndroid Build Coastguard Worker GrAAType fallbackAAType) const {
179*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fallbackAAType != GrAAType::kNone); // The atlas doesn't support non-AA.
180*c8dee2aaSAndroid Build Coastguard Worker float atlasMaxPathHeight_p2 = (fallbackAAType == GrAAType::kMSAA)
181*c8dee2aaSAndroid Build Coastguard Worker ? kAtlasMaxPathHeightWithMSAAFallback * kAtlasMaxPathHeightWithMSAAFallback
182*c8dee2aaSAndroid Build Coastguard Worker : kAtlasMaxPathHeight * kAtlasMaxPathHeight;
183*c8dee2aaSAndroid Build Coastguard Worker auto [topLeftFloor, botRightCeil] = round_out(pathDevBounds);
184*c8dee2aaSAndroid Build Coastguard Worker auto size = botRightCeil - topLeftFloor;
185*c8dee2aaSAndroid Build Coastguard Worker return // Ensure the path's largest dimension fits in the atlas.
186*c8dee2aaSAndroid Build Coastguard Worker all(size <= fAtlasMaxPathWidth) &&
187*c8dee2aaSAndroid Build Coastguard Worker // Since we will transpose tall skinny paths, limiting to atlasMaxPathHeight^2 pixels
188*c8dee2aaSAndroid Build Coastguard Worker // guarantees heightInAtlas <= atlasMaxPathHeight, while also allowing paths that are
189*c8dee2aaSAndroid Build Coastguard Worker // very wide and short.
190*c8dee2aaSAndroid Build Coastguard Worker size[0] * size[1] <= atlasMaxPathHeight_p2;
191*c8dee2aaSAndroid Build Coastguard Worker }
192*c8dee2aaSAndroid Build Coastguard Worker
set(const SkMatrix & m,const SkPath & path)193*c8dee2aaSAndroid Build Coastguard Worker void AtlasPathRenderer::AtlasPathKey::set(const SkMatrix& m, const SkPath& path) {
194*c8dee2aaSAndroid Build Coastguard Worker fPathGenID = path.getGenerationID();
195*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[0] = m.getScaleX();
196*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[1] = m.getSkewX();
197*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[2] = m.getTranslateX();
198*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[3] = m.getSkewY();
199*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[4] = m.getScaleY();
200*c8dee2aaSAndroid Build Coastguard Worker fAffineMatrix[5] = m.getTranslateY();
201*c8dee2aaSAndroid Build Coastguard Worker fFillRule = (uint32_t)GrFillRuleForSkPath(path); // Fill rule doesn't affect the path's genID.
202*c8dee2aaSAndroid Build Coastguard Worker }
203*c8dee2aaSAndroid Build Coastguard Worker
addPathToAtlas(GrRecordingContext * rContext,const SkMatrix & viewMatrix,const SkPath & path,const SkRect & pathDevBounds,SkIRect * devIBounds,SkIPoint16 * locationInAtlas,bool * transposedInAtlas,const DrawRefsAtlasCallback & drawRefsAtlasCallback)204*c8dee2aaSAndroid Build Coastguard Worker bool AtlasPathRenderer::addPathToAtlas(GrRecordingContext* rContext,
205*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& viewMatrix,
206*c8dee2aaSAndroid Build Coastguard Worker const SkPath& path,
207*c8dee2aaSAndroid Build Coastguard Worker const SkRect& pathDevBounds,
208*c8dee2aaSAndroid Build Coastguard Worker SkIRect* devIBounds,
209*c8dee2aaSAndroid Build Coastguard Worker SkIPoint16* locationInAtlas,
210*c8dee2aaSAndroid Build Coastguard Worker bool* transposedInAtlas,
211*c8dee2aaSAndroid Build Coastguard Worker const DrawRefsAtlasCallback& drawRefsAtlasCallback) {
212*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!viewMatrix.hasPerspective()); // See onCanDrawPath().
213*c8dee2aaSAndroid Build Coastguard Worker
214*c8dee2aaSAndroid Build Coastguard Worker pathDevBounds.roundOut(devIBounds);
215*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
216*c8dee2aaSAndroid Build Coastguard Worker // is_visible() should have guaranteed the path's bounds were representable as ints, since clip
217*c8dee2aaSAndroid Build Coastguard Worker // bounds within the max render target size are nowhere near INT_MAX.
218*c8dee2aaSAndroid Build Coastguard Worker auto [topLeftFloor, botRightCeil] = round_out(pathDevBounds);
219*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(all(skvx::cast<float>(skvx::int2::Load(&devIBounds->fLeft)) == topLeftFloor));
220*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(all(skvx::cast<float>(skvx::int2::Load(&devIBounds->fRight)) == botRightCeil));
221*c8dee2aaSAndroid Build Coastguard Worker #endif
222*c8dee2aaSAndroid Build Coastguard Worker
223*c8dee2aaSAndroid Build Coastguard Worker int widthInAtlas = devIBounds->width();
224*c8dee2aaSAndroid Build Coastguard Worker int heightInAtlas = devIBounds->height();
225*c8dee2aaSAndroid Build Coastguard Worker // is_visible() should have guaranteed the path's bounds were non-empty.
226*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(widthInAtlas > 0 && heightInAtlas > 0);
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker if (SkNextPow2(widthInAtlas) == SkNextPow2(heightInAtlas)) {
229*c8dee2aaSAndroid Build Coastguard Worker // Both dimensions go to the same pow2 band in the atlas. Use the larger dimension as height
230*c8dee2aaSAndroid Build Coastguard Worker // for more efficient packing.
231*c8dee2aaSAndroid Build Coastguard Worker *transposedInAtlas = widthInAtlas > heightInAtlas;
232*c8dee2aaSAndroid Build Coastguard Worker } else {
233*c8dee2aaSAndroid Build Coastguard Worker // Both dimensions go to different pow2 bands in the atlas. Use the smaller pow2 band for
234*c8dee2aaSAndroid Build Coastguard Worker // most efficient packing.
235*c8dee2aaSAndroid Build Coastguard Worker *transposedInAtlas = heightInAtlas > widthInAtlas;
236*c8dee2aaSAndroid Build Coastguard Worker }
237*c8dee2aaSAndroid Build Coastguard Worker if (*transposedInAtlas) {
238*c8dee2aaSAndroid Build Coastguard Worker std::swap(heightInAtlas, widthInAtlas);
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker // pathFitsInAtlas() should have guaranteed these constraints on the path size.
241*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(widthInAtlas <= (int)fAtlasMaxPathWidth);
242*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(heightInAtlas <= kAtlasMaxPathHeight);
243*c8dee2aaSAndroid Build Coastguard Worker
244*c8dee2aaSAndroid Build Coastguard Worker // Check if this path is already in the atlas. This is mainly for clip paths.
245*c8dee2aaSAndroid Build Coastguard Worker AtlasPathKey atlasPathKey;
246*c8dee2aaSAndroid Build Coastguard Worker if (!path.isVolatile()) {
247*c8dee2aaSAndroid Build Coastguard Worker atlasPathKey.set(viewMatrix, path);
248*c8dee2aaSAndroid Build Coastguard Worker if (const SkIPoint16* existingLocation = fAtlasPathCache.find(atlasPathKey)) {
249*c8dee2aaSAndroid Build Coastguard Worker *locationInAtlas = *existingLocation;
250*c8dee2aaSAndroid Build Coastguard Worker return true;
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker }
253*c8dee2aaSAndroid Build Coastguard Worker
254*c8dee2aaSAndroid Build Coastguard Worker if (fAtlasRenderTasks.empty() ||
255*c8dee2aaSAndroid Build Coastguard Worker !fAtlasRenderTasks.back()->addPath(viewMatrix, path, devIBounds->topLeft(), widthInAtlas,
256*c8dee2aaSAndroid Build Coastguard Worker heightInAtlas, *transposedInAtlas, locationInAtlas)) {
257*c8dee2aaSAndroid Build Coastguard Worker // We either don't have an atlas yet or the current one is full. Try to replace it.
258*c8dee2aaSAndroid Build Coastguard Worker auto currentAtlasTask = (!fAtlasRenderTasks.empty()) ? fAtlasRenderTasks.back().get()
259*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
260*c8dee2aaSAndroid Build Coastguard Worker if (currentAtlasTask &&
261*c8dee2aaSAndroid Build Coastguard Worker drawRefsAtlasCallback &&
262*c8dee2aaSAndroid Build Coastguard Worker drawRefsAtlasCallback(currentAtlasTask->atlasProxy())) {
263*c8dee2aaSAndroid Build Coastguard Worker // The draw already refs the current atlas. Give up. Otherwise the draw would ref two
264*c8dee2aaSAndroid Build Coastguard Worker // different atlases and they couldn't share a texture.
265*c8dee2aaSAndroid Build Coastguard Worker return false;
266*c8dee2aaSAndroid Build Coastguard Worker }
267*c8dee2aaSAndroid Build Coastguard Worker // Replace the atlas with a new one.
268*c8dee2aaSAndroid Build Coastguard Worker auto dynamicAtlas = std::make_unique<GrDynamicAtlas>(
269*c8dee2aaSAndroid Build Coastguard Worker kAtlasAlpha8Type, GrDynamicAtlas::InternalMultisample::kYes,
270*c8dee2aaSAndroid Build Coastguard Worker SkISize{fAtlasInitialSize, fAtlasInitialSize}, fAtlasMaxSize,
271*c8dee2aaSAndroid Build Coastguard Worker *rContext->priv().caps(), kAtlasAlgorithm);
272*c8dee2aaSAndroid Build Coastguard Worker auto newAtlasTask = sk_make_sp<AtlasRenderTask>(rContext,
273*c8dee2aaSAndroid Build Coastguard Worker sk_make_sp<GrArenas>(),
274*c8dee2aaSAndroid Build Coastguard Worker std::move(dynamicAtlas));
275*c8dee2aaSAndroid Build Coastguard Worker rContext->priv().drawingManager()->addAtlasTask(newAtlasTask, currentAtlasTask);
276*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(newAtlasTask->addPath(viewMatrix, path, devIBounds->topLeft(), widthInAtlas,
277*c8dee2aaSAndroid Build Coastguard Worker heightInAtlas, *transposedInAtlas, locationInAtlas));
278*c8dee2aaSAndroid Build Coastguard Worker fAtlasRenderTasks.push_back(std::move(newAtlasTask));
279*c8dee2aaSAndroid Build Coastguard Worker fAtlasPathCache.reset();
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker
282*c8dee2aaSAndroid Build Coastguard Worker // Remember this path's location in the atlas, in case it gets drawn again.
283*c8dee2aaSAndroid Build Coastguard Worker if (!path.isVolatile()) {
284*c8dee2aaSAndroid Build Coastguard Worker fAtlasPathCache.set(atlasPathKey, *locationInAtlas);
285*c8dee2aaSAndroid Build Coastguard Worker }
286*c8dee2aaSAndroid Build Coastguard Worker return true;
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker
onCanDrawPath(const CanDrawPathArgs & args) const289*c8dee2aaSAndroid Build Coastguard Worker PathRenderer::CanDrawPath AtlasPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
290*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
291*c8dee2aaSAndroid Build Coastguard Worker if (!fAtlasRenderTasks.empty()) {
292*c8dee2aaSAndroid Build Coastguard Worker // args.fPaint should NEVER reference our current atlas. If it does, it means somebody
293*c8dee2aaSAndroid Build Coastguard Worker // intercepted a clip FP meant for a different op and will cause rendering artifacts.
294*c8dee2aaSAndroid Build Coastguard Worker const GrSurfaceProxy* atlasProxy = fAtlasRenderTasks.back()->atlasProxy();
295*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!refs_atlas(args.fPaint->getColorFragmentProcessor(), atlasProxy));
296*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!refs_atlas(args.fPaint->getCoverageFragmentProcessor(), atlasProxy));
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!args.fHasUserStencilSettings); // See onGetStencilSupport().
299*c8dee2aaSAndroid Build Coastguard Worker #endif
300*c8dee2aaSAndroid Build Coastguard Worker bool canDrawPath = args.fShape->style().isSimpleFill() &&
301*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DISABLE_ATLAS_PATH_RENDERER_WITH_COVERAGE_AA
302*c8dee2aaSAndroid Build Coastguard Worker // The MSAA requirement is a temporary limitation in order to preserve
303*c8dee2aaSAndroid Build Coastguard Worker // functionality for refactoring. TODO: Allow kCoverage AA types.
304*c8dee2aaSAndroid Build Coastguard Worker args.fAAType == GrAAType::kMSAA &&
305*c8dee2aaSAndroid Build Coastguard Worker #else
306*c8dee2aaSAndroid Build Coastguard Worker args.fAAType != GrAAType::kNone &&
307*c8dee2aaSAndroid Build Coastguard Worker #endif
308*c8dee2aaSAndroid Build Coastguard Worker // Non-DMSAA convex paths should be handled by the convex tessellator.
309*c8dee2aaSAndroid Build Coastguard Worker // (With DMSAA we continue to use the atlas for these paths in order to avoid
310*c8dee2aaSAndroid Build Coastguard Worker // triggering MSAA.)
311*c8dee2aaSAndroid Build Coastguard Worker (args.fProxy->numSamples() == 1 || !args.fShape->knownToBeConvex()) &&
312*c8dee2aaSAndroid Build Coastguard Worker !args.fShape->style().hasPathEffect() &&
313*c8dee2aaSAndroid Build Coastguard Worker !args.fViewMatrix->hasPerspective() &&
314*c8dee2aaSAndroid Build Coastguard Worker this->pathFitsInAtlas(args.fViewMatrix->mapRect(args.fShape->bounds()),
315*c8dee2aaSAndroid Build Coastguard Worker args.fAAType);
316*c8dee2aaSAndroid Build Coastguard Worker return canDrawPath ? CanDrawPath::kYes : CanDrawPath::kNo;
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker
onDrawPath(const DrawPathArgs & args)319*c8dee2aaSAndroid Build Coastguard Worker bool AtlasPathRenderer::onDrawPath(const DrawPathArgs& args) {
320*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
321*c8dee2aaSAndroid Build Coastguard Worker args.fShape->asPath(&path);
322*c8dee2aaSAndroid Build Coastguard Worker
323*c8dee2aaSAndroid Build Coastguard Worker const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
324*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->pathFitsInAtlas(pathDevBounds, args.fAAType));
325*c8dee2aaSAndroid Build Coastguard Worker
326*c8dee2aaSAndroid Build Coastguard Worker if (!is_visible(pathDevBounds, args.fClip->getConservativeBounds())) {
327*c8dee2aaSAndroid Build Coastguard Worker // The path is empty or outside the clip. No mask is needed.
328*c8dee2aaSAndroid Build Coastguard Worker if (path.isInverseFillType()) {
329*c8dee2aaSAndroid Build Coastguard Worker args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
330*c8dee2aaSAndroid Build Coastguard Worker *args.fViewMatrix);
331*c8dee2aaSAndroid Build Coastguard Worker }
332*c8dee2aaSAndroid Build Coastguard Worker return true;
333*c8dee2aaSAndroid Build Coastguard Worker }
334*c8dee2aaSAndroid Build Coastguard Worker
335*c8dee2aaSAndroid Build Coastguard Worker SkIRect devIBounds;
336*c8dee2aaSAndroid Build Coastguard Worker SkIPoint16 locationInAtlas;
337*c8dee2aaSAndroid Build Coastguard Worker bool transposedInAtlas;
338*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(this->addPathToAtlas(args.fContext, *args.fViewMatrix, path, pathDevBounds,
339*c8dee2aaSAndroid Build Coastguard Worker &devIBounds, &locationInAtlas, &transposedInAtlas,
340*c8dee2aaSAndroid Build Coastguard Worker nullptr/*DrawRefsAtlasCallback -- see onCanDrawPath()*/));
341*c8dee2aaSAndroid Build Coastguard Worker
342*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& fillBounds = args.fShape->inverseFilled()
343*c8dee2aaSAndroid Build Coastguard Worker ? (args.fClip
344*c8dee2aaSAndroid Build Coastguard Worker ? args.fClip->getConservativeBounds()
345*c8dee2aaSAndroid Build Coastguard Worker : args.fSurfaceDrawContext->asSurfaceProxy()->backingStoreBoundsIRect())
346*c8dee2aaSAndroid Build Coastguard Worker : devIBounds;
347*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps = *args.fSurfaceDrawContext->caps();
348*c8dee2aaSAndroid Build Coastguard Worker auto op = GrOp::Make<DrawAtlasPathOp>(args.fContext,
349*c8dee2aaSAndroid Build Coastguard Worker args.fSurfaceDrawContext->arenaAlloc(),
350*c8dee2aaSAndroid Build Coastguard Worker fillBounds, *args.fViewMatrix,
351*c8dee2aaSAndroid Build Coastguard Worker std::move(args.fPaint), locationInAtlas,
352*c8dee2aaSAndroid Build Coastguard Worker devIBounds, transposedInAtlas,
353*c8dee2aaSAndroid Build Coastguard Worker fAtlasRenderTasks.back()->readView(caps),
354*c8dee2aaSAndroid Build Coastguard Worker args.fShape->inverseFilled());
355*c8dee2aaSAndroid Build Coastguard Worker args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
356*c8dee2aaSAndroid Build Coastguard Worker return true;
357*c8dee2aaSAndroid Build Coastguard Worker }
358*c8dee2aaSAndroid Build Coastguard Worker
makeAtlasClipEffect(const SurfaceDrawContext * sdc,const GrOp * opBeingClipped,std::unique_ptr<GrFragmentProcessor> inputFP,const SkIRect & drawBounds,const SkMatrix & viewMatrix,const SkPath & path)359*c8dee2aaSAndroid Build Coastguard Worker GrFPResult AtlasPathRenderer::makeAtlasClipEffect(const SurfaceDrawContext* sdc,
360*c8dee2aaSAndroid Build Coastguard Worker const GrOp* opBeingClipped,
361*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> inputFP,
362*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& drawBounds,
363*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& viewMatrix,
364*c8dee2aaSAndroid Build Coastguard Worker const SkPath& path) {
365*c8dee2aaSAndroid Build Coastguard Worker if (viewMatrix.hasPerspective()) {
366*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(inputFP));
367*c8dee2aaSAndroid Build Coastguard Worker }
368*c8dee2aaSAndroid Build Coastguard Worker
369*c8dee2aaSAndroid Build Coastguard Worker const SkRect pathDevBounds = viewMatrix.mapRect(path.getBounds());
370*c8dee2aaSAndroid Build Coastguard Worker if (!is_visible(pathDevBounds, drawBounds)) {
371*c8dee2aaSAndroid Build Coastguard Worker // The path is empty or outside the drawBounds. No mask is needed. We explicitly allow the
372*c8dee2aaSAndroid Build Coastguard Worker // returned successful "fp" to be null in case this bypassed atlas clip effect was the first
373*c8dee2aaSAndroid Build Coastguard Worker // clip to be processed by the clip stack (at which point inputFP is null).
374*c8dee2aaSAndroid Build Coastguard Worker return path.isInverseFillType() ? GrFPNullableSuccess(std::move(inputFP))
375*c8dee2aaSAndroid Build Coastguard Worker : GrFPFailure(std::move(inputFP));
376*c8dee2aaSAndroid Build Coastguard Worker }
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker auto fallbackAAType = (sdc->numSamples() > 1 || sdc->canUseDynamicMSAA()) ? GrAAType::kMSAA
379*c8dee2aaSAndroid Build Coastguard Worker : GrAAType::kCoverage;
380*c8dee2aaSAndroid Build Coastguard Worker if (!this->pathFitsInAtlas(pathDevBounds, fallbackAAType)) {
381*c8dee2aaSAndroid Build Coastguard Worker // The path is too big.
382*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(inputFP));
383*c8dee2aaSAndroid Build Coastguard Worker }
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker SkIRect devIBounds;
386*c8dee2aaSAndroid Build Coastguard Worker SkIPoint16 locationInAtlas;
387*c8dee2aaSAndroid Build Coastguard Worker bool transposedInAtlas;
388*c8dee2aaSAndroid Build Coastguard Worker // Called if the atlas runs out of room, to determine if it's safe to create a new one. (Draws
389*c8dee2aaSAndroid Build Coastguard Worker // can never access more than one atlas.)
390*c8dee2aaSAndroid Build Coastguard Worker auto drawRefsAtlasCallback = [opBeingClipped, &inputFP](const GrSurfaceProxy* atlasProxy) {
391*c8dee2aaSAndroid Build Coastguard Worker return refs_atlas(opBeingClipped, atlasProxy) ||
392*c8dee2aaSAndroid Build Coastguard Worker refs_atlas(inputFP.get(), atlasProxy);
393*c8dee2aaSAndroid Build Coastguard Worker };
394*c8dee2aaSAndroid Build Coastguard Worker // addPathToAtlas() ignores inverseness of the fill. See GrAtlasRenderTask::getAtlasUberPath().
395*c8dee2aaSAndroid Build Coastguard Worker if (!this->addPathToAtlas(sdc->recordingContext(), viewMatrix, path, pathDevBounds, &devIBounds,
396*c8dee2aaSAndroid Build Coastguard Worker &locationInAtlas, &transposedInAtlas, drawRefsAtlasCallback)) {
397*c8dee2aaSAndroid Build Coastguard Worker // The atlas ran out of room and we were unable to start a new one.
398*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(inputFP));
399*c8dee2aaSAndroid Build Coastguard Worker }
400*c8dee2aaSAndroid Build Coastguard Worker
401*c8dee2aaSAndroid Build Coastguard Worker SkMatrix atlasMatrix;
402*c8dee2aaSAndroid Build Coastguard Worker auto [atlasX, atlasY] = locationInAtlas;
403*c8dee2aaSAndroid Build Coastguard Worker if (!transposedInAtlas) {
404*c8dee2aaSAndroid Build Coastguard Worker atlasMatrix = SkMatrix::Translate(atlasX - devIBounds.left(), atlasY - devIBounds.top());
405*c8dee2aaSAndroid Build Coastguard Worker } else {
406*c8dee2aaSAndroid Build Coastguard Worker atlasMatrix.setAll(0, 1, atlasX - devIBounds.top(),
407*c8dee2aaSAndroid Build Coastguard Worker 1, 0, atlasY - devIBounds.left(),
408*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1);
409*c8dee2aaSAndroid Build Coastguard Worker }
410*c8dee2aaSAndroid Build Coastguard Worker auto flags = GrModulateAtlasCoverageEffect::Flags::kNone;
411*c8dee2aaSAndroid Build Coastguard Worker if (path.isInverseFillType()) {
412*c8dee2aaSAndroid Build Coastguard Worker flags |= GrModulateAtlasCoverageEffect::Flags::kInvertCoverage;
413*c8dee2aaSAndroid Build Coastguard Worker }
414*c8dee2aaSAndroid Build Coastguard Worker if (!devIBounds.contains(drawBounds)) {
415*c8dee2aaSAndroid Build Coastguard Worker flags |= GrModulateAtlasCoverageEffect::Flags::kCheckBounds;
416*c8dee2aaSAndroid Build Coastguard Worker // At this point in time we expect callers to tighten the scissor for "kIntersect" clips, as
417*c8dee2aaSAndroid Build Coastguard Worker // opposed to us having to check the path bounds. Feel free to remove this assert if that
418*c8dee2aaSAndroid Build Coastguard Worker // ever changes.
419*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(path.isInverseFillType());
420*c8dee2aaSAndroid Build Coastguard Worker }
421*c8dee2aaSAndroid Build Coastguard Worker GrSurfaceProxyView atlasView = fAtlasRenderTasks.back()->readView(*sdc->caps());
422*c8dee2aaSAndroid Build Coastguard Worker return GrFPSuccess(std::make_unique<GrModulateAtlasCoverageEffect>(flags, std::move(inputFP),
423*c8dee2aaSAndroid Build Coastguard Worker std::move(atlasView),
424*c8dee2aaSAndroid Build Coastguard Worker atlasMatrix, devIBounds));
425*c8dee2aaSAndroid Build Coastguard Worker }
426*c8dee2aaSAndroid Build Coastguard Worker
preFlush(GrOnFlushResourceProvider * onFlushRP)427*c8dee2aaSAndroid Build Coastguard Worker bool AtlasPathRenderer::preFlush(GrOnFlushResourceProvider* onFlushRP) {
428*c8dee2aaSAndroid Build Coastguard Worker if (fAtlasRenderTasks.empty()) {
429*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fAtlasPathCache.count() == 0);
430*c8dee2aaSAndroid Build Coastguard Worker return true;
431*c8dee2aaSAndroid Build Coastguard Worker }
432*c8dee2aaSAndroid Build Coastguard Worker
433*c8dee2aaSAndroid Build Coastguard Worker // Verify the atlases can all share the same texture.
434*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(validate_atlas_dependencies(fAtlasRenderTasks);)
435*c8dee2aaSAndroid Build Coastguard Worker
436*c8dee2aaSAndroid Build Coastguard Worker bool successful;
437*c8dee2aaSAndroid Build Coastguard Worker
438*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
439*c8dee2aaSAndroid Build Coastguard Worker if (onFlushRP->failFlushTimeCallbacks()) {
440*c8dee2aaSAndroid Build Coastguard Worker successful = false;
441*c8dee2aaSAndroid Build Coastguard Worker } else
442*c8dee2aaSAndroid Build Coastguard Worker #endif
443*c8dee2aaSAndroid Build Coastguard Worker {
444*c8dee2aaSAndroid Build Coastguard Worker // TODO: it seems like this path renderer's backing-texture reuse could be greatly
445*c8dee2aaSAndroid Build Coastguard Worker // improved. Please see skbug.com/13298.
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker // Instantiate the first atlas.
448*c8dee2aaSAndroid Build Coastguard Worker successful = fAtlasRenderTasks[0]->instantiate(onFlushRP);
449*c8dee2aaSAndroid Build Coastguard Worker
450*c8dee2aaSAndroid Build Coastguard Worker // Instantiate the remaining atlases.
451*c8dee2aaSAndroid Build Coastguard Worker GrTexture* firstAtlas = fAtlasRenderTasks[0]->atlasProxy()->peekTexture();
452*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(firstAtlas);
453*c8dee2aaSAndroid Build Coastguard Worker for (int i = 1; successful && i < fAtlasRenderTasks.size(); ++i) {
454*c8dee2aaSAndroid Build Coastguard Worker auto atlasTask = fAtlasRenderTasks[i].get();
455*c8dee2aaSAndroid Build Coastguard Worker if (atlasTask->atlasProxy()->backingStoreDimensions() == firstAtlas->dimensions()) {
456*c8dee2aaSAndroid Build Coastguard Worker successful &= atlasTask->instantiate(onFlushRP, sk_ref_sp(firstAtlas));
457*c8dee2aaSAndroid Build Coastguard Worker } else {
458*c8dee2aaSAndroid Build Coastguard Worker // The atlases are expected to all be full size except possibly the final one.
459*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(i == fAtlasRenderTasks.size() - 1);
460*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(atlasTask->atlasProxy()->backingStoreDimensions().area() <
461*c8dee2aaSAndroid Build Coastguard Worker firstAtlas->dimensions().area());
462*c8dee2aaSAndroid Build Coastguard Worker // TODO: Recycle the larger atlas texture anyway?
463*c8dee2aaSAndroid Build Coastguard Worker successful &= atlasTask->instantiate(onFlushRP);
464*c8dee2aaSAndroid Build Coastguard Worker }
465*c8dee2aaSAndroid Build Coastguard Worker }
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker
468*c8dee2aaSAndroid Build Coastguard Worker // Reset all atlas data.
469*c8dee2aaSAndroid Build Coastguard Worker fAtlasRenderTasks.clear();
470*c8dee2aaSAndroid Build Coastguard Worker fAtlasPathCache.reset();
471*c8dee2aaSAndroid Build Coastguard Worker return successful;
472*c8dee2aaSAndroid Build Coastguard Worker }
473*c8dee2aaSAndroid Build Coastguard Worker
474*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::ganesh
475