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