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 &evictor);
158 check(reporter, atlas.get(), 0, 4, 0);
159
160 // Fill up the first level
161 GrDrawOpAtlas::AtlasLocator atlasLocators[kNumPlots * kNumPlots];
162 for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
163 bool result = fill_plot(
164 atlas.get(), resourceProvider, &uploadTarget, &atlasLocators[i], i * 32);
165 REPORTER_ASSERT(reporter, result);
166 check(reporter, atlas.get(), 1, 4, 1);
167 }
168
169 atlas->instantiate(&onFlushResourceProvider);
170 check(reporter, atlas.get(), 1, 4, 1);
171
172 // Force allocation of a second level
173 GrDrawOpAtlas::AtlasLocator atlasLocator;
174 bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasLocator, 4 * 32);
175 REPORTER_ASSERT(reporter, result);
176 check(reporter, atlas.get(), 2, 4, 2);
177
178 // Simulate a lot of draws using only the first plot. The last texture should be compacted.
179 for (int i = 0; i < 512; ++i) {
180 atlas->setLastUseToken(atlasLocators[0], uploadTarget.tokenTracker()->nextDrawToken());
181 uploadTarget.issueDrawToken();
182 uploadTarget.flushToken();
183 atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush());
184 }
185
186 check(reporter, atlas.get(), 1, 4, 1);
187 }
188
189 #if SK_GPU_V1
190 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
191
192 // This test verifies that the AtlasTextOp::onPrepare method correctly handles a failure
193 // when allocating an atlas page.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation,reporter,ctxInfo)194 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation, reporter, ctxInfo) {
195
196 auto dContext = ctxInfo.directContext();
197
198 auto gpu = dContext->priv().getGpu();
199 auto resourceProvider = dContext->priv().resourceProvider();
200
201 auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
202 SkBackingFit::kApprox, {32, 32},
203 SkSurfaceProps());
204
205 SkPaint paint;
206 paint.setColor(SK_ColorRED);
207
208 SkFont font;
209 font.setEdging(SkFont::Edging::kAlias);
210
211 const char* text = "a";
212 SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
213
214 GrOp::Owner op = skgpu::v1::AtlasTextOp::CreateOpTestingOnly(sdc.get(), paint,
215 font, matrixProvider,
216 text, 16, 16);
217 if (!op) {
218 return;
219 }
220
221 auto atlasTextOp = (skgpu::v1::AtlasTextOp*)op.get();
222 atlasTextOp->finalize(*dContext->priv().caps(), nullptr, GrClampType::kAuto);
223
224 TestingUploadTarget uploadTarget;
225
226 GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker());
227
228 GrSurfaceProxyView surfaceView = sdc->writeSurfaceView();
229 GrOpFlushState::OpArgs opArgs(op.get(),
230 surfaceView,
231 false /*usesMSAASurface*/,
232 nullptr,
233 GrDstProxyView(),
234 GrXferBarrierFlags::kNone,
235 GrLoadOp::kLoad);
236
237 // Modify the atlas manager so it can't allocate any pages. This will force a failure
238 // in the preparation of the text op
239 auto atlasManager = dContext->priv().getAtlasManager();
240 unsigned int numProxies;
241 atlasManager->getViews(kA8_GrMaskFormat, &numProxies);
242 atlasManager->setMaxPages_TestingOnly(0);
243
244 flushState.setOpArgs(&opArgs);
245 op->prepare(&flushState);
246 flushState.setOpArgs(nullptr);
247 }
248 #endif // SK_GPU_V1
249
test_atlas_config(skiatest::Reporter * reporter,int maxTextureSize,size_t maxBytes,GrMaskFormat maskFormat,SkISize expectedDimensions,SkISize expectedPlotDimensions)250 void test_atlas_config(skiatest::Reporter* reporter, int maxTextureSize, size_t maxBytes,
251 GrMaskFormat maskFormat, SkISize expectedDimensions,
252 SkISize expectedPlotDimensions) {
253 GrDrawOpAtlasConfig config(maxTextureSize, maxBytes);
254 REPORTER_ASSERT(reporter, config.atlasDimensions(maskFormat) == expectedDimensions);
255 REPORTER_ASSERT(reporter, config.plotDimensions(maskFormat) == expectedPlotDimensions);
256 }
257
DEF_GPUTEST(GrDrawOpAtlasConfig_Basic,reporter,options)258 DEF_GPUTEST(GrDrawOpAtlasConfig_Basic, reporter, options) {
259 // 1/4 MB
260 test_atlas_config(reporter, 65536, 256 * 1024, kARGB_GrMaskFormat,
261 { 256, 256 }, { 256, 256 });
262 test_atlas_config(reporter, 65536, 256 * 1024, kA8_GrMaskFormat,
263 { 512, 512 }, { 256, 256 });
264 // 1/2 MB
265 test_atlas_config(reporter, 65536, 512 * 1024, kARGB_GrMaskFormat,
266 { 512, 256 }, { 256, 256 });
267 test_atlas_config(reporter, 65536, 512 * 1024, kA8_GrMaskFormat,
268 { 1024, 512 }, { 256, 256 });
269 // 1 MB
270 test_atlas_config(reporter, 65536, 1024 * 1024, kARGB_GrMaskFormat,
271 { 512, 512 }, { 256, 256 });
272 test_atlas_config(reporter, 65536, 1024 * 1024, kA8_GrMaskFormat,
273 { 1024, 1024 }, { 256, 256 });
274 // 2 MB
275 test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kARGB_GrMaskFormat,
276 { 1024, 512 }, { 256, 256 });
277 test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kA8_GrMaskFormat,
278 { 2048, 1024 }, { 512, 256 });
279 // 4 MB
280 test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kARGB_GrMaskFormat,
281 { 1024, 1024 }, { 256, 256 });
282 test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kA8_GrMaskFormat,
283 { 2048, 2048 }, { 512, 512 });
284 // 8 MB
285 test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kARGB_GrMaskFormat,
286 { 2048, 1024 }, { 256, 256 });
287 test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kA8_GrMaskFormat,
288 { 2048, 2048 }, { 512, 512 });
289 // 16 MB (should be same as 8 MB)
290 test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kARGB_GrMaskFormat,
291 { 2048, 1024 }, { 256, 256 });
292 test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kA8_GrMaskFormat,
293 { 2048, 2048 }, { 512, 512 });
294
295 // 4MB, restricted texture size
296 test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kARGB_GrMaskFormat,
297 { 1024, 1024 }, { 256, 256 });
298 test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kA8_GrMaskFormat,
299 { 1024, 1024 }, { 256, 256 });
300
301 // 3 MB (should be same as 2 MB)
302 test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kARGB_GrMaskFormat,
303 { 1024, 512 }, { 256, 256 });
304 test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kA8_GrMaskFormat,
305 { 2048, 1024 }, { 512, 256 });
306
307 // minimum size
308 test_atlas_config(reporter, 65536, 0, kARGB_GrMaskFormat,
309 { 256, 256 }, { 256, 256 });
310 test_atlas_config(reporter, 65536, 0, kA8_GrMaskFormat,
311 { 512, 512 }, { 256, 256 });
312 }
313