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