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