blob: 0a5f991e69ab91f4de9a956b9c45cd4d4984e1f9 [file] [log] [blame]
Robert Phillips4bc70112018-03-01 10:24:02 -05001/*
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
Ben Wagnerd90cd3b2018-05-22 10:48:08 -040010#include "GrContext.h"
11#include "GrContextFactory.h"
Robert Phillips4bc70112018-03-01 10:24:02 -050012#include "GrContextPriv.h"
Ben Wagnerd90cd3b2018-05-22 10:48:08 -040013#include "GrDeferredUpload.h"
14#include "GrDrawOpAtlas.h"
15#include "GrDrawingManager.h"
16#include "GrOnFlushResourceProvider.h"
17#include "GrOpFlushState.h"
18#include "GrRenderTargetContext.h"
19#include "GrSurfaceProxyPriv.h"
20#include "GrTextureProxy.h"
21#include "GrTypesPriv.h"
22#include "GrXferProcessor.h"
23#include "SkBitmap.h"
24#include "SkColor.h"
25#include "SkColorSpace.h"
26#include "SkIPoint16.h"
27#include "SkImageInfo.h"
28#include "SkMatrix.h"
29#include "SkPaint.h"
30#include "SkPoint.h"
31#include "SkRefCnt.h"
Robert Phillips4bc70112018-03-01 10:24:02 -050032#include "Test.h"
Ben Wagnerd90cd3b2018-05-22 10:48:08 -040033#include "ops/GrDrawOp.h"
34#include "text/GrAtlasManager.h"
Herb Derby26cbe512018-05-24 14:39:01 -040035#include "text/GrTextContext.h"
Ben Wagnerd90cd3b2018-05-22 10:48:08 -040036#include "text/GrTextUtils.h"
37
38#include <memory>
39
40class GrResourceProvider;
Robert Phillips4bc70112018-03-01 10:24:02 -050041
42static const int kNumPlots = 2;
43static const int kPlotSize = 32;
44static const int kAtlasSize = kNumPlots * kPlotSize;
45
46int GrDrawOpAtlas::numAllocated_TestingOnly() const {
47 int count = 0;
48 for (uint32_t i = 0; i < this->maxPages(); ++i) {
49 if (fProxies[i]->priv().isInstantiated()) {
50 ++count;
51 }
52 }
53
54 return count;
55}
56
Kevin Lubickb5502b22018-03-12 10:17:06 -040057void GrAtlasManager::setMaxPages_TestingOnly(uint32_t maxPages) {
Robert Phillipsd2e9f762018-03-07 11:54:37 -050058 for (int i = 0; i < kMaskFormatCount; i++) {
59 if (fAtlases[i]) {
Kevin Lubickb5502b22018-03-12 10:17:06 -040060 fAtlases[i]->setMaxPages_TestingOnly(maxPages);
Robert Phillipsd2e9f762018-03-07 11:54:37 -050061 }
62 }
63}
64
65void GrDrawOpAtlas::setMaxPages_TestingOnly(uint32_t maxPages) {
66 SkASSERT(!fNumActivePages);
67
68 fMaxPages = maxPages;
69}
70
Robert Phillips4bc70112018-03-01 10:24:02 -050071void EvictionFunc(GrDrawOpAtlas::AtlasID atlasID, void*) {
72 SkASSERT(0); // The unit test shouldn't exercise this code path
73}
74
75static void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas,
76 uint32_t expectedActive, uint32_t expectedMax, int expectedAlloced) {
77 REPORTER_ASSERT(r, expectedActive == atlas->numActivePages());
78 REPORTER_ASSERT(r, expectedMax == atlas->maxPages());
79 REPORTER_ASSERT(r, expectedAlloced == atlas->numAllocated_TestingOnly());
80}
81
82class TestingUploadTarget : public GrDeferredUploadTarget {
83public:
84 TestingUploadTarget() { }
85
Robert Phillipsd2e9f762018-03-07 11:54:37 -050086 const GrTokenTracker* tokenTracker() final { return &fTokenTracker; }
87 GrTokenTracker* writeableTokenTracker() { return &fTokenTracker; }
Robert Phillips4bc70112018-03-01 10:24:02 -050088
89 GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final {
90 SkASSERT(0); // this test shouldn't invoke this code path
91 return fTokenTracker.nextDrawToken();
92 }
93
94 virtual GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final {
95 return fTokenTracker.nextTokenToFlush();
96 }
97
98 void issueDrawToken() { fTokenTracker.issueDrawToken(); }
99 void flushToken() { fTokenTracker.flushToken(); }
100
101private:
102 GrTokenTracker fTokenTracker;
103
104 typedef GrDeferredUploadTarget INHERITED;
105};
106
107static bool fill_plot(GrDrawOpAtlas* atlas,
108 GrResourceProvider* resourceProvider,
109 GrDeferredUploadTarget* target,
110 GrDrawOpAtlas::AtlasID* atlasID,
111 int alpha) {
112 SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
113
114 SkBitmap data;
115 data.allocPixels(ii);
116 data.eraseARGB(alpha, 0, 0, 0);
117
118 SkIPoint16 loc;
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500119 GrDrawOpAtlas::ErrorCode code;
120 code = atlas->addToAtlas(resourceProvider, atlasID, target, kPlotSize, kPlotSize,
121 data.getAddr(0, 0), &loc);
122 return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
Robert Phillips4bc70112018-03-01 10:24:02 -0500123}
124
125
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500126// This is a basic DrawOpAtlas test. It simply verifies that multitexture atlases correctly
127// add and remove pages. Note that this is simulating flush-time behavior.
128DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas, reporter, ctxInfo) {
Robert Phillips4bc70112018-03-01 10:24:02 -0500129 auto context = ctxInfo.grContext();
130 auto proxyProvider = context->contextPriv().proxyProvider();
131 auto resourceProvider = context->contextPriv().resourceProvider();
132 auto drawingManager = context->contextPriv().drawingManager();
133
134 GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
135 TestingUploadTarget uploadTarget;
136
137 std::unique_ptr<GrDrawOpAtlas> atlas = GrDrawOpAtlas::Make(
138 proxyProvider,
139 kAlpha_8_GrPixelConfig,
140 kAtlasSize, kAtlasSize,
141 kNumPlots, kNumPlots,
142 GrDrawOpAtlas::AllowMultitexturing::kYes,
143 EvictionFunc, nullptr);
144 check(reporter, atlas.get(), 0, 4, 0);
145
146 // Fill up the first level
147 GrDrawOpAtlas::AtlasID atlasIDs[kNumPlots * kNumPlots];
148 for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
149 bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasIDs[i], i*32);
150 REPORTER_ASSERT(reporter, result);
151 check(reporter, atlas.get(), 1, 4, 1);
152 }
153
154 atlas->instantiate(&onFlushResourceProvider);
155 check(reporter, atlas.get(), 1, 4, 1);
156
157 // Force allocation of a second level
158 GrDrawOpAtlas::AtlasID atlasID;
159 bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasID, 4*32);
160 REPORTER_ASSERT(reporter, result);
161 check(reporter, atlas.get(), 2, 4, 2);
162
163 // Simulate a lot of draws using only the first plot. The last texture should be compacted.
164 for (int i = 0; i < 512; ++i) {
165 atlas->setLastUseToken(atlasIDs[0], uploadTarget.tokenTracker()->nextDrawToken());
166 uploadTarget.issueDrawToken();
167 uploadTarget.flushToken();
168 atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush());
169 }
170
171 check(reporter, atlas.get(), 1, 4, 1);
172}
173
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500174// This test verifies that the GrAtlasTextOp::onPrepare method correctly handles a failure
175// when allocating an atlas page.
176DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation, reporter, ctxInfo) {
177
178 auto context = ctxInfo.grContext();
179
180 auto gpu = context->contextPriv().getGpu();
181 auto resourceProvider = context->contextPriv().resourceProvider();
182 auto drawingManager = context->contextPriv().drawingManager();
Herb Derby26cbe512018-05-24 14:39:01 -0400183 auto textContext = drawingManager->getTextContext();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500184
185 auto rtc = context->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
186 32, 32,
187 kRGBA_8888_GrPixelConfig,
188 nullptr);
189
190 SkPaint paint;
191 paint.setColor(SK_ColorRED);
192 paint.setLCDRenderText(false);
193 paint.setAntiAlias(false);
194 paint.setSubpixelText(false);
195 GrTextUtils::Paint utilsPaint(&paint, &rtc->colorSpaceInfo());
196
197 const char* text = "a";
198
199 std::unique_ptr<GrDrawOp> op = textContext->createOp_TestingOnly(context, textContext,
200 rtc.get(), paint,
201 SkMatrix::I(), text,
202 16, 16);
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400203 op->finalize(*context->contextPriv().caps(), nullptr, GrPixelConfigIsClamped::kNo);
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500204
205 TestingUploadTarget uploadTarget;
206
207 GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker());
208 GrOpFlushState::OpArgs opArgs = {
209 op.get(),
210 rtc->asRenderTargetProxy(),
211 nullptr,
212 GrXferProcessor::DstProxy(nullptr, SkIPoint::Make(0, 0))
213 };
214
215 // Cripple the atlas manager so it can't allocate any pages. This will force a failure
216 // in the preparation of the text op
Robert Phillips5a66efb2018-03-07 15:13:18 -0500217 auto atlasManager = context->contextPriv().getAtlasManager();
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500218 unsigned int numProxies;
219 atlasManager->getProxies(kA8_GrMaskFormat, &numProxies);
220 atlasManager->setMaxPages_TestingOnly(0);
221
222 flushState.setOpArgs(&opArgs);
223 op->prepare(&flushState);
224 flushState.setOpArgs(nullptr);
225}