• 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 "SkTypes.h"
9 
10 #include "GrContext.h"
11 #include "GrContextFactory.h"
12 #include "GrContextPriv.h"
13 #include "GrDeferredUpload.h"
14 #include "GrDrawOpAtlas.h"
15 #include "GrDrawingManager.h"
16 #include "GrMemoryPool.h"
17 #include "GrOnFlushResourceProvider.h"
18 #include "GrOpFlushState.h"
19 #include "GrRenderTargetContext.h"
20 #include "GrSurfaceProxyPriv.h"
21 #include "GrTextureProxy.h"
22 #include "GrTypesPriv.h"
23 #include "GrXferProcessor.h"
24 #include "SkBitmap.h"
25 #include "SkColor.h"
26 #include "SkColorSpace.h"
27 #include "SkIPoint16.h"
28 #include "SkImageInfo.h"
29 #include "SkMatrix.h"
30 #include "SkPaint.h"
31 #include "SkPoint.h"
32 #include "SkRefCnt.h"
33 #include "Test.h"
34 #include "ops/GrDrawOp.h"
35 #include "text/GrAtlasManager.h"
36 #include "text/GrTextContext.h"
37 #include "text/GrTextTarget.h"
38 
39 #include <memory>
40 
41 class GrResourceProvider;
42 
43 static const int kNumPlots = 2;
44 static const int kPlotSize = 32;
45 static const int kAtlasSize = kNumPlots * kPlotSize;
46 
numAllocated_TestingOnly() const47 int GrDrawOpAtlas::numAllocated_TestingOnly() const {
48     int count = 0;
49     for (uint32_t i = 0; i < this->maxPages(); ++i) {
50         if (fProxies[i]->isInstantiated()) {
51             ++count;
52         }
53     }
54 
55     return count;
56 }
57 
setMaxPages_TestingOnly(uint32_t maxPages)58 void GrAtlasManager::setMaxPages_TestingOnly(uint32_t maxPages) {
59     for (int i = 0; i < kMaskFormatCount; i++) {
60         if (fAtlases[i]) {
61             fAtlases[i]->setMaxPages_TestingOnly(maxPages);
62         }
63     }
64 }
65 
setMaxPages_TestingOnly(uint32_t maxPages)66 void GrDrawOpAtlas::setMaxPages_TestingOnly(uint32_t maxPages) {
67     SkASSERT(!fNumActivePages);
68 
69     fMaxPages = maxPages;
70 }
71 
EvictionFunc(GrDrawOpAtlas::AtlasID atlasID,void *)72 void EvictionFunc(GrDrawOpAtlas::AtlasID atlasID, void*) {
73     SkASSERT(0); // The unit test shouldn't exercise this code path
74 }
75 
check(skiatest::Reporter * r,GrDrawOpAtlas * atlas,uint32_t expectedActive,uint32_t expectedMax,int expectedAlloced)76 static void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas,
77                   uint32_t expectedActive, uint32_t expectedMax, int expectedAlloced) {
78     REPORTER_ASSERT(r, expectedActive == atlas->numActivePages());
79     REPORTER_ASSERT(r, expectedMax == atlas->maxPages());
80     REPORTER_ASSERT(r, expectedAlloced == atlas->numAllocated_TestingOnly());
81 }
82 
83 class TestingUploadTarget : public GrDeferredUploadTarget {
84 public:
TestingUploadTarget()85     TestingUploadTarget() { }
86 
tokenTracker()87     const GrTokenTracker* tokenTracker() final { return &fTokenTracker; }
writeableTokenTracker()88     GrTokenTracker* writeableTokenTracker() { return &fTokenTracker; }
89 
addInlineUpload(GrDeferredTextureUploadFn &&)90     GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final {
91         SkASSERT(0); // this test shouldn't invoke this code path
92         return fTokenTracker.nextDrawToken();
93     }
94 
addASAPUpload(GrDeferredTextureUploadFn && upload)95     virtual GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final {
96         return fTokenTracker.nextTokenToFlush();
97     }
98 
issueDrawToken()99     void issueDrawToken() { fTokenTracker.issueDrawToken(); }
flushToken()100     void flushToken() { fTokenTracker.flushToken(); }
101 
102 private:
103     GrTokenTracker fTokenTracker;
104 
105     typedef GrDeferredUploadTarget INHERITED;
106 };
107 
fill_plot(GrDrawOpAtlas * atlas,GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,GrDrawOpAtlas::AtlasID * atlasID,int alpha)108 static bool fill_plot(GrDrawOpAtlas* atlas,
109                       GrResourceProvider* resourceProvider,
110                       GrDeferredUploadTarget* target,
111                       GrDrawOpAtlas::AtlasID* atlasID,
112                       int alpha) {
113     SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
114 
115     SkBitmap data;
116     data.allocPixels(ii);
117     data.eraseARGB(alpha, 0, 0, 0);
118 
119     SkIPoint16 loc;
120     GrDrawOpAtlas::ErrorCode code;
121     code = atlas->addToAtlas(resourceProvider, atlasID, target, kPlotSize, kPlotSize,
122                               data.getAddr(0, 0), &loc);
123     return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
124 }
125 
126 
127 // This is a basic DrawOpAtlas test. It simply verifies that multitexture atlases correctly
128 // add and remove pages. Note that this is simulating flush-time behavior.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas,reporter,ctxInfo)129 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas, reporter, ctxInfo) {
130     auto context = ctxInfo.grContext();
131     auto proxyProvider = context->contextPriv().proxyProvider();
132     auto resourceProvider = context->contextPriv().resourceProvider();
133     auto drawingManager = context->contextPriv().drawingManager();
134 
135     GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
136     TestingUploadTarget uploadTarget;
137 
138     GrBackendFormat format =
139             context->contextPriv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType);
140 
141     std::unique_ptr<GrDrawOpAtlas> atlas = GrDrawOpAtlas::Make(
142                                                 proxyProvider,
143                                                 format,
144                                                 kAlpha_8_GrPixelConfig,
145                                                 kAtlasSize, kAtlasSize,
146                                                 kAtlasSize/kNumPlots, kAtlasSize/kNumPlots,
147                                                 GrDrawOpAtlas::AllowMultitexturing::kYes,
148                                                 EvictionFunc, nullptr);
149     check(reporter, atlas.get(), 0, 4, 0);
150 
151     // Fill up the first level
152     GrDrawOpAtlas::AtlasID atlasIDs[kNumPlots * kNumPlots];
153     for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
154         bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasIDs[i], i*32);
155         REPORTER_ASSERT(reporter, result);
156         check(reporter, atlas.get(), 1, 4, 1);
157     }
158 
159     atlas->instantiate(&onFlushResourceProvider);
160     check(reporter, atlas.get(), 1, 4, 1);
161 
162     // Force allocation of a second level
163     GrDrawOpAtlas::AtlasID atlasID;
164     bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasID, 4*32);
165     REPORTER_ASSERT(reporter, result);
166     check(reporter, atlas.get(), 2, 4, 2);
167 
168     // Simulate a lot of draws using only the first plot. The last texture should be compacted.
169     for (int i = 0; i < 512; ++i) {
170         atlas->setLastUseToken(atlasIDs[0], uploadTarget.tokenTracker()->nextDrawToken());
171         uploadTarget.issueDrawToken();
172         uploadTarget.flushToken();
173         atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush());
174     }
175 
176     check(reporter, atlas.get(), 1, 4, 1);
177 }
178 
179 // This test verifies that the GrAtlasTextOp::onPrepare method correctly handles a failure
180 // when allocating an atlas page.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation,reporter,ctxInfo)181 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation, reporter, ctxInfo) {
182 
183     auto context = ctxInfo.grContext();
184 
185     auto gpu = context->contextPriv().getGpu();
186     auto resourceProvider = context->contextPriv().resourceProvider();
187     auto drawingManager = context->contextPriv().drawingManager();
188     auto textContext = drawingManager->getTextContext();
189     auto opMemoryPool = context->contextPriv().opMemoryPool();
190 
191     GrBackendFormat format =
192             context->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
193 
194     auto rtc =  context->contextPriv().makeDeferredRenderTargetContext(format,
195                                                                        SkBackingFit::kApprox,
196                                                                        32, 32,
197                                                                        kRGBA_8888_GrPixelConfig,
198                                                                        nullptr);
199 
200     SkPaint paint;
201     paint.setColor(SK_ColorRED);
202 
203     SkFont font;
204     font.setEdging(SkFont::Edging::kAlias);
205 
206     const char* text = "a";
207 
208     std::unique_ptr<GrDrawOp> op = textContext->createOp_TestingOnly(
209             context, textContext, rtc.get(), paint, font, SkMatrix::I(), text, 16, 16);
210     op->finalize(*context->contextPriv().caps(), nullptr);
211 
212     TestingUploadTarget uploadTarget;
213 
214     GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker(), nullptr,
215                               nullptr);
216     GrOpFlushState::OpArgs opArgs = {
217         op.get(),
218         rtc->asRenderTargetProxy(),
219         nullptr,
220         GrXferProcessor::DstProxy(nullptr, SkIPoint::Make(0, 0))
221     };
222 
223     // Cripple the atlas manager so it can't allocate any pages. This will force a failure
224     // in the preparation of the text op
225     auto atlasManager = context->contextPriv().getAtlasManager();
226     unsigned int numProxies;
227     atlasManager->getProxies(kA8_GrMaskFormat, &numProxies);
228     atlasManager->setMaxPages_TestingOnly(0);
229 
230     flushState.setOpArgs(&opArgs);
231     op->prepare(&flushState);
232     flushState.setOpArgs(nullptr);
233     opMemoryPool->release(std::move(op));
234 }
235 
test_atlas_config(skiatest::Reporter * reporter,int maxTextureSize,size_t maxBytes,GrMaskFormat maskFormat,SkISize expectedDimensions,SkISize expectedPlotDimensions)236 void test_atlas_config(skiatest::Reporter* reporter, int maxTextureSize, size_t maxBytes,
237                        GrMaskFormat maskFormat, SkISize expectedDimensions,
238                        SkISize expectedPlotDimensions) {
239     GrDrawOpAtlasConfig config(maxTextureSize, maxBytes);
240     REPORTER_ASSERT(reporter, config.atlasDimensions(maskFormat) == expectedDimensions);
241     REPORTER_ASSERT(reporter, config.plotDimensions(maskFormat) == expectedPlotDimensions);
242 }
243 
DEF_GPUTEST(GrDrawOpAtlasConfig_Basic,reporter,options)244 DEF_GPUTEST(GrDrawOpAtlasConfig_Basic, reporter, options) {
245     // 1/4 MB
246     test_atlas_config(reporter, 65536, 256 * 1024, kARGB_GrMaskFormat,
247                       { 256, 256 }, { 256, 256 });
248     test_atlas_config(reporter, 65536, 256 * 1024, kA8_GrMaskFormat,
249                       { 512, 512 }, { 256, 256 });
250     // 1/2 MB
251     test_atlas_config(reporter, 65536, 512 * 1024, kARGB_GrMaskFormat,
252                       { 512, 256 }, { 256, 256 });
253     test_atlas_config(reporter, 65536, 512 * 1024, kA8_GrMaskFormat,
254                       { 1024, 512 }, { 256, 256 });
255     // 1 MB
256     test_atlas_config(reporter, 65536, 1024 * 1024, kARGB_GrMaskFormat,
257                       { 512, 512 }, { 256, 256 });
258     test_atlas_config(reporter, 65536, 1024 * 1024, kA8_GrMaskFormat,
259                       { 1024, 1024 }, { 256, 256 });
260     // 2 MB
261     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kARGB_GrMaskFormat,
262                       { 1024, 512 }, { 256, 256 });
263     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kA8_GrMaskFormat,
264                       { 2048, 1024 }, { 512, 256 });
265     // 4 MB
266     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kARGB_GrMaskFormat,
267                       { 1024, 1024 }, { 256, 256 });
268     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kA8_GrMaskFormat,
269                       { 2048, 2048 }, { 512, 512 });
270     // 8 MB
271     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kARGB_GrMaskFormat,
272                       { 2048, 1024 }, { 256, 256 });
273     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kA8_GrMaskFormat,
274                       { 2048, 2048 }, { 512, 512 });
275     // 16 MB (should be same as 8 MB)
276     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kARGB_GrMaskFormat,
277                       { 2048, 1024 }, { 256, 256 });
278     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kA8_GrMaskFormat,
279                       { 2048, 2048 }, { 512, 512 });
280 
281     // 4MB, restricted texture size
282     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kARGB_GrMaskFormat,
283                       { 1024, 1024 }, { 256, 256 });
284     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kA8_GrMaskFormat,
285                       { 1024, 1024 }, { 256, 256 });
286 
287     // 3 MB (should be same as 2 MB)
288     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kARGB_GrMaskFormat,
289                       { 1024, 512 }, { 256, 256 });
290     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kA8_GrMaskFormat,
291                       { 2048, 1024 }, { 512, 256 });
292 
293     // minimum size
294     test_atlas_config(reporter, 65536, 0, kARGB_GrMaskFormat,
295                       { 256, 256 }, { 256, 256 });
296     test_atlas_config(reporter, 65536, 0, kA8_GrMaskFormat,
297                       { 512, 512 }, { 256, 256 });
298 }
299