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