xref: /aosp_15_r20/external/skia/tests/DrawOpAtlasTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 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 "include/core/SkBitmap.h"
9 #include "include/core/SkColor.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkSurfaceProps.h"
17 #include "include/core/SkTypes.h"
18 #include "include/gpu/GpuTypes.h"
19 #include "include/gpu/ganesh/GrBackendSurface.h"
20 #include "include/gpu/ganesh/GrDirectContext.h"
21 #include "include/gpu/ganesh/GrTypes.h"
22 #include "include/private/gpu/ganesh/GrTypesPriv.h"
23 #include "src/gpu/AtlasTypes.h"
24 #include "src/gpu/SkBackingFit.h"
25 #include "src/gpu/ganesh/GrCaps.h"
26 #include "src/gpu/ganesh/GrDeferredUpload.h"
27 #include "src/gpu/ganesh/GrDirectContextPriv.h"
28 #include "src/gpu/ganesh/GrDrawOpAtlas.h"
29 #include "src/gpu/ganesh/GrDstProxyView.h"
30 #include "src/gpu/ganesh/GrOnFlushResourceProvider.h"
31 #include "src/gpu/ganesh/GrOpFlushState.h"
32 #include "src/gpu/ganesh/GrSurfaceProxy.h"
33 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
34 #include "src/gpu/ganesh/GrXferProcessor.h"
35 #include "src/gpu/ganesh/SurfaceDrawContext.h"
36 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
37 #include "src/gpu/ganesh/ops/GrOp.h"
38 #include "src/gpu/ganesh/text/GrAtlasManager.h"
39 #include "tests/CtsEnforcement.h"
40 #include "tests/Test.h"
41 #include "tools/fonts/FontToolUtils.h"
42 #include "tools/gpu/ganesh/AtlasTextOpTools.h"
43 #include "tools/gpu/ganesh/GrAtlasTools.h"
44 
45 #include <cstddef>
46 #include <cstdint>
47 #include <memory>
48 
49 class GrResourceProvider;
50 struct GrContextOptions;
51 
52 using namespace skgpu::ganesh;
53 using MaskFormat = skgpu::MaskFormat;
54 
55 
56 namespace {
57 const int kNumPlots = 2;
58 const int kPlotSize = 32;
59 const int kAtlasSize = kNumPlots * kPlotSize;
60 
61 class AssertOnEvict : public skgpu::PlotEvictionCallback {
62 public:
evict(skgpu::PlotLocator)63     void evict(skgpu::PlotLocator) override {
64         SkASSERT(0); // The unit test shouldn't exercise this code path
65     }
66 };
67 
check(skiatest::Reporter * r,GrDrawOpAtlas * atlas,uint32_t expectedActive,int expectedAlloced)68 void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas,
69            uint32_t expectedActive, int expectedAlloced) {
70     REPORTER_ASSERT(r, atlas->numActivePages() == expectedActive);
71     REPORTER_ASSERT(r, GrDrawOpAtlasTools::NumAllocated(atlas) == expectedAlloced);
72     REPORTER_ASSERT(r, atlas->maxPages() == skgpu::PlotLocator::kMaxMultitexturePages);
73 }
74 
fill_plot(GrDrawOpAtlas * atlas,GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,skgpu::AtlasLocator * atlasLocator,int alpha)75 bool fill_plot(GrDrawOpAtlas* atlas,
76                GrResourceProvider* resourceProvider,
77                GrDeferredUploadTarget* target,
78                skgpu::AtlasLocator* atlasLocator,
79                int alpha) {
80     SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
81 
82     SkBitmap data;
83     data.allocPixels(ii);
84     data.eraseARGB(alpha, 0, 0, 0);
85 
86     GrDrawOpAtlas::ErrorCode code;
87     code = atlas->addToAtlas(resourceProvider, target, kPlotSize, kPlotSize,
88                              data.getAddr(0, 0), atlasLocator);
89     return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
90 }
91 }  // anonymous namespace
92 
93 // Can't be in anonymous namespace because it needs friend access to TokenTracker
94 class TestingUploadTarget : public GrDeferredUploadTarget {
95 public:
TestingUploadTarget()96     TestingUploadTarget() { }
97 
tokenTracker()98     const skgpu::TokenTracker* tokenTracker() final { return &fTokenTracker; }
writeableTokenTracker()99     skgpu::TokenTracker* writeableTokenTracker() { return &fTokenTracker; }
100 
addInlineUpload(GrDeferredTextureUploadFn &&)101     skgpu::AtlasToken addInlineUpload(GrDeferredTextureUploadFn&&) final {
102         SkASSERT(0); // this test shouldn't invoke this code path
103         return fTokenTracker.nextDrawToken();
104     }
105 
addASAPUpload(GrDeferredTextureUploadFn && upload)106     skgpu::AtlasToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final {
107         return fTokenTracker.nextFlushToken();
108     }
109 
issueDrawToken()110     void issueDrawToken() { fTokenTracker.issueDrawToken(); }
issueFlushToken()111     void issueFlushToken() { fTokenTracker.issueFlushToken(); }
112 
113 private:
114     skgpu::TokenTracker fTokenTracker;
115 
116     using INHERITED = GrDeferredUploadTarget;
117 };
118 
119 // This is a basic DrawOpAtlas test. It simply verifies that multitexture atlases correctly
120 // add and remove pages. Note that this is simulating flush-time behavior.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)121 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas,
122                                        reporter,
123                                        ctxInfo,
124                                        CtsEnforcement::kApiLevel_T) {
125     auto context = ctxInfo.directContext();
126     auto proxyProvider = context->priv().proxyProvider();
127     auto resourceProvider = context->priv().resourceProvider();
128     auto drawingManager = context->priv().drawingManager();
129     const GrCaps* caps = context->priv().caps();
130 
131     GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
132     TestingUploadTarget uploadTarget;
133 
134     GrColorType atlasColorType = GrColorType::kAlpha_8;
135     GrBackendFormat format = caps->getDefaultBackendFormat(atlasColorType,
136                                                            GrRenderable::kNo);
137 
138     AssertOnEvict evictor;
139     skgpu::AtlasGenerationCounter counter;
140 
141     std::unique_ptr<GrDrawOpAtlas> atlas = GrDrawOpAtlas::Make(
142                                                 proxyProvider,
143                                                 format,
144                                                 GrColorTypeToSkColorType(atlasColorType),
145                                                 GrColorTypeBytesPerPixel(atlasColorType),
146                                                 kAtlasSize, kAtlasSize,
147                                                 kAtlasSize/kNumPlots, kAtlasSize/kNumPlots,
148                                                 &counter,
149                                                 GrDrawOpAtlas::AllowMultitexturing::kYes,
150                                                 &evictor,
151                                                 /*label=*/"BasicDrawOpAtlasTest");
152     check(reporter, atlas.get(), 0, 0);
153 
154     // Fill up the first level
155     skgpu::AtlasLocator atlasLocators[kNumPlots * kNumPlots];
156     for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
157         bool result = fill_plot(
158                 atlas.get(), resourceProvider, &uploadTarget, &atlasLocators[i], i * 32);
159         REPORTER_ASSERT(reporter, result);
160         check(reporter, atlas.get(), 1, 1);
161     }
162 
163     atlas->instantiate(&onFlushResourceProvider);
164     check(reporter, atlas.get(), 1, 1);
165 
166     // Force allocation of a second level
167     skgpu::AtlasLocator atlasLocator;
168     bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasLocator, 4 * 32);
169     REPORTER_ASSERT(reporter, result);
170     check(reporter, atlas.get(), 2, 2);
171 
172     // Simulate a lot of draws using only the first plot. The last texture should be compacted.
173     for (int i = 0; i < 512; ++i) {
174         atlas->setLastUseToken(atlasLocators[0], uploadTarget.tokenTracker()->nextDrawToken());
175         uploadTarget.issueDrawToken();
176         uploadTarget.issueFlushToken();
177         atlas->compact(uploadTarget.tokenTracker()->nextFlushToken());
178     }
179 
180     check(reporter, atlas.get(), 1, 1);
181 }
182 
183 // This test verifies that the AtlasTextOp::onPrepare method correctly handles a failure
184 // when allocating an atlas page.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)185 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation,
186                                        reporter,
187                                        ctxInfo,
188                                        CtsEnforcement::kApiLevel_T) {
189     auto dContext = ctxInfo.directContext();
190 
191     auto gpu = dContext->priv().getGpu();
192     auto resourceProvider = dContext->priv().resourceProvider();
193 
194     auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(dContext,
195                                                        GrColorType::kRGBA_8888,
196                                                        nullptr,
197                                                        SkBackingFit::kApprox,
198                                                        {32, 32},
199                                                        SkSurfaceProps(),
200                                                        /*label=*/"AtlasTextOpPreparation");
201 
202     SkPaint paint;
203     paint.setColor(SK_ColorRED);
204 
205     SkFont font = ToolUtils::DefaultFont();
206     font.setEdging(SkFont::Edging::kAlias);
207 
208     const char* text = "a";
209 
210     GrOp::Owner op =
211             AtlasTextOpTools::CreateOp(sdc.get(), paint, font, SkMatrix::I(), text, 16, 16);
212     if (!op) {
213         return;
214     }
215 
216     auto atlasTextOp = (AtlasTextOp*)op.get();
217     atlasTextOp->finalize(*dContext->priv().caps(), nullptr, GrClampType::kAuto);
218 
219     TestingUploadTarget uploadTarget;
220 
221     GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker());
222 
223     GrSurfaceProxyView surfaceView = sdc->writeSurfaceView();
224     GrOpFlushState::OpArgs opArgs(op.get(),
225                                   surfaceView,
226                                   false /*usesMSAASurface*/,
227                                   nullptr,
228                                   GrDstProxyView(),
229                                   GrXferBarrierFlags::kNone,
230                                   GrLoadOp::kLoad);
231 
232     // Modify the atlas manager so it can't allocate any pages. This will force a failure
233     // in the preparation of the text op
234     auto atlasManager = dContext->priv().getAtlasManager();
235     unsigned int numProxies;
236     atlasManager->getViews(MaskFormat::kA8, &numProxies);
237     GrAtlasManagerTools::SetMaxPages(atlasManager, 0);
238 
239     flushState.setOpArgs(&opArgs);
240     op->prepare(&flushState);
241     flushState.setOpArgs(nullptr);
242 }
243 
244 namespace {
test_atlas_config(skiatest::Reporter * reporter,int maxTextureSize,size_t maxBytes,MaskFormat maskFormat,SkISize expectedDimensions,SkISize expectedPlotDimensions)245 void test_atlas_config(skiatest::Reporter* reporter, int maxTextureSize, size_t maxBytes,
246                        MaskFormat maskFormat, SkISize expectedDimensions,
247                        SkISize expectedPlotDimensions) {
248     GrDrawOpAtlasConfig config(maxTextureSize, maxBytes);
249     REPORTER_ASSERT(reporter, config.atlasDimensions(maskFormat) == expectedDimensions);
250     REPORTER_ASSERT(reporter, config.plotDimensions(maskFormat) == expectedPlotDimensions);
251 }
252 }  // anonymous namespace
253 
DEF_GANESH_TEST(GrDrawOpAtlasConfig_Basic,reporter,options,CtsEnforcement::kApiLevel_T)254 DEF_GANESH_TEST(GrDrawOpAtlasConfig_Basic, reporter, options, CtsEnforcement::kApiLevel_T) {
255     // 1/4 MB
256     test_atlas_config(reporter, 65536, 256 * 1024, MaskFormat::kARGB,
257                       { 256, 256 }, { 256, 256 });
258     test_atlas_config(reporter, 65536, 256 * 1024, MaskFormat::kA8,
259                       { 512, 512 }, { 256, 256 });
260     // 1/2 MB
261     test_atlas_config(reporter, 65536, 512 * 1024, MaskFormat::kARGB,
262                       { 512, 256 }, { 256, 256 });
263     test_atlas_config(reporter, 65536, 512 * 1024, MaskFormat::kA8,
264                       { 1024, 512 }, { 256, 256 });
265     // 1 MB
266     test_atlas_config(reporter, 65536, 1024 * 1024, MaskFormat::kARGB,
267                       { 512, 512 }, { 256, 256 });
268     test_atlas_config(reporter, 65536, 1024 * 1024, MaskFormat::kA8,
269                       { 1024, 1024 }, { 256, 256 });
270     // 2 MB
271     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, MaskFormat::kARGB,
272                       { 1024, 512 }, { 256, 256 });
273     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, MaskFormat::kA8,
274                       { 2048, 1024 }, { 512, 256 });
275     // 4 MB
276     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, MaskFormat::kARGB,
277                       { 1024, 1024 }, { 256, 256 });
278     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, MaskFormat::kA8,
279                       { 2048, 2048 }, { 512, 512 });
280     // 8 MB
281     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, MaskFormat::kARGB,
282                       { 2048, 1024 }, { 256, 256 });
283     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, MaskFormat::kA8,
284                       { 2048, 2048 }, { 512, 512 });
285     // 16 MB (should be same as 8 MB)
286     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, MaskFormat::kARGB,
287                       { 2048, 1024 }, { 256, 256 });
288     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, MaskFormat::kA8,
289                       { 2048, 2048 }, { 512, 512 });
290 
291     // 4MB, restricted texture size
292     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, MaskFormat::kARGB,
293                       { 1024, 1024 }, { 256, 256 });
294     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, MaskFormat::kA8,
295                       { 1024, 1024 }, { 256, 256 });
296 
297     // 3 MB (should be same as 2 MB)
298     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, MaskFormat::kARGB,
299                       { 1024, 512 }, { 256, 256 });
300     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, MaskFormat::kA8,
301                       { 2048, 1024 }, { 512, 256 });
302 
303     // minimum size
304     test_atlas_config(reporter, 65536, 0, MaskFormat::kARGB,
305                       { 256, 256 }, { 256, 256 });
306     test_atlas_config(reporter, 65536, 0, MaskFormat::kA8,
307                       { 512, 512 }, { 256, 256 });
308 }
309