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