blob: 9c584d8a54c79d6c06f066abe60547238316f60d [file] [log] [blame]
Robert Phillips51e7ca32017-03-27 10:14:08 -04001/*
2 * Copyright 2017 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 "Test.h"
9
10#if SK_SUPPORT_GPU
11
12#include "GrSurfaceProxy.h"
13#include "GrTextureProducer.h"
14#include "GrTextureProxy.h"
15
Greg Danielc77085d2017-11-01 16:38:48 -040016// For DetermineDomainMode (in the MDB world) we have 3 rects:
Robert Phillips51e7ca32017-03-27 10:14:08 -040017// 1) the final instantiated backing storage (i.e., the actual GrTexture's extent)
18// 2) the proxy's extent, which may or may not match the GrTexture's extent
Greg Danielc77085d2017-11-01 16:38:48 -040019// 3) the constraint rect, which can optionally be hard or soft
Robert Phillips51e7ca32017-03-27 10:14:08 -040020// This test "fuzzes" all the combinations of these rects.
21class GrTextureProducer_TestAccess {
22public:
23 using DomainMode = GrTextureProducer::DomainMode;
24
Brian Salomon2bbdcc42017-09-07 12:36:34 -040025 static DomainMode DetermineDomainMode(const SkRect& constraintRect,
26 GrTextureProducer::FilterConstraint filterConstraint,
27 bool coordsLimitedToConstraintRect,
28 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040029 const GrSamplerState::Filter* filterModeOrNullForBicubic,
30 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040031 return GrTextureProducer::DetermineDomainMode(constraintRect,
32 filterConstraint,
33 coordsLimitedToConstraintRect,
34 proxy,
Robert Phillips51e7ca32017-03-27 10:14:08 -040035 filterModeOrNullForBicubic,
36 domainRect);
37 }
38};
39
40using DomainMode = GrTextureProducer_TestAccess::DomainMode;
41
Robert Phillips51e7ca32017-03-27 10:14:08 -040042class RectInfo {
43public:
44 enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 };
45
46 enum EdgeType {
47 kSoft = 0, // there is data on the other side of this edge that we are allowed to sample
48 kHard = 1, // the backing resource ends at this edge
49 kBad = 2 // we can't sample across this edge
50 };
51
52 void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot,
53 const char* name) {
54 fRect = rect;
55 fTypes[kLeft] = left;
56 fTypes[kTop] = top;
57 fTypes[kRight] = right;
58 fTypes[kBot] = bot;
59 fName = name;
60 }
61
62 const SkRect& rect() const { return fRect; }
63 EdgeType edgeType(Side side) const { return fTypes[side]; }
64 const char* name() const { return fName; }
65
66#ifdef SK_DEBUG
67 bool isHardOrBadAllAround() const {
68 for (int i = 0; i < 4; ++i) {
69 if (kHard != fTypes[i] && kBad != fTypes[i]) {
70 return false;
71 }
72 }
73 return true;
74 }
75#endif
76
77 bool hasABad() const {
78 for (int i = 0; i < 4; ++i) {
79 if (kBad == fTypes[i]) {
80 return true;
81 }
82 }
83 return false;
84 }
85
86#ifdef SK_DEBUG
87 void print(const char* label) const {
88 SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n",
89 label, fName,
90 fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
91 ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]),
92 ToStr(fTypes[kRight]), ToStr(fTypes[kBot]));
93 }
94#endif
95
96private:
97#ifdef SK_DEBUG
98 static const char* ToStr(EdgeType type) {
99 static const char* names[] = { "soft", "hard", "bad" };
100 return names[type];
101 }
102#endif
103
104 RectInfo operator=(const RectInfo& other); // disallow
105
106 SkRect fRect;
107 EdgeType fTypes[4];
108 const char* fName;
109
110};
111
112static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider,
113 bool isPowerOfTwo,
114 bool isExact,
115 RectInfo* rect) {
116 int size = isPowerOfTwo ? 128 : 100;
117 SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
118
119 GrSurfaceDesc desc;
Robert Phillips16d8ec62017-07-27 16:16:25 -0400120 desc.fOrigin = kTopLeft_GrSurfaceOrigin;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400121 desc.fWidth = size;
122 desc.fHeight = size;
Robert Phillips16d8ec62017-07-27 16:16:25 -0400123 desc.fConfig = kRGBA_8888_GrPixelConfig;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400124
125 static const char* name = "proxy";
126
127 // Proxies are always hard on the left and top but can be bad on the right and bottom
128 rect->set(SkRect::MakeWH(size, size),
129 RectInfo::kHard,
130 RectInfo::kHard,
131 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
132 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
133 name);
134
135 sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider,
136 desc, fit,
137 SkBudgeted::kYes);
138 return proxy;
139}
140
141static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
142 bool isInsetHard, bool coordsAreLimitedToRect,
143 float insetAmount, float halfFilterWidth) {
144 if (isInsetHard) {
145 if (coordsAreLimitedToRect) {
146 SkASSERT(halfFilterWidth >= 0.0f);
147 if (0.0f == halfFilterWidth) {
148 return RectInfo::kSoft;
149 }
150 }
151
152 if (0.0f == insetAmount && RectInfo::kHard == previous) {
153 return RectInfo::kHard;
154 }
155
156 return RectInfo::kBad;
157 }
158
159 if (RectInfo::kHard == previous) {
160 return RectInfo::kHard;
161 }
162
163 if (coordsAreLimitedToRect) {
164 SkASSERT(halfFilterWidth >= 0.0f);
165 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
166 return RectInfo::kSoft;
167 }
168 }
169
170 return previous;
171}
172
173static const int kInsetLeft_Flag = 0x1;
174static const int kInsetTop_Flag = 0x2;
175static const int kInsetRight_Flag = 0x4;
176static const int kInsetBot_Flag = 0x8;
177
178// If 'isInsetHard' is true we can't sample across the inset boundary.
179// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
180static const SkRect* generic_inset(const RectInfo& enclosing,
181 RectInfo* result,
182 bool isInsetHard,
183 bool areCoordsLimitedToRect,
184 float insetAmount,
185 float halfFilterWidth,
186 uint32_t flags,
187 const char* name) {
188 SkRect newR = enclosing.rect();
189
190 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
191 if (flags & kInsetLeft_Flag) {
192 newR.fLeft += insetAmount;
193 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
194 insetAmount, halfFilterWidth);
195 } else {
196 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
197 0.0f, halfFilterWidth);
198 }
199
200 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
201 if (flags & kInsetTop_Flag) {
202 newR.fTop += insetAmount;
203 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
204 insetAmount, halfFilterWidth);
205 } else {
206 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
207 0.0f, halfFilterWidth);
208 }
209
210 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
211 if (flags & kInsetRight_Flag) {
212 newR.fRight -= insetAmount;
213 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
214 insetAmount, halfFilterWidth);
215 } else {
216 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
217 0.0f, halfFilterWidth);
218 }
219
220 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
221 if (flags & kInsetBot_Flag) {
222 newR.fBottom -= insetAmount;
223 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
224 insetAmount, halfFilterWidth);
225 } else {
226 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
227 0.0f, halfFilterWidth);
228 }
229
230 result->set(newR, left, top, right, bot, name);
231 return &result->rect();
232}
233
234// Make a rect that only touches the enclosing rect on the left.
235static const SkRect* left_only(const RectInfo& enclosing,
236 RectInfo* result,
237 bool isInsetHard,
238 bool areCoordsLimitedToRect,
239 float insetAmount,
240 float halfFilterWidth) {
241 static const char* name = "left";
242 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
243 insetAmount, halfFilterWidth,
244 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
245}
246
247// Make a rect that only touches the enclosing rect on the top.
248static const SkRect* top_only(const RectInfo& enclosing,
249 RectInfo* result,
250 bool isInsetHard,
251 bool areCoordsLimitedToRect,
252 float insetAmount,
253 float halfFilterWidth) {
254 static const char* name = "top";
255 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
256 insetAmount, halfFilterWidth,
257 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
258}
259
260// Make a rect that only touches the enclosing rect on the right.
261static const SkRect* right_only(const RectInfo& enclosing,
262 RectInfo* result,
263 bool isInsetHard,
264 bool areCoordsLimitedToRect,
265 float insetAmount,
266 float halfFilterWidth) {
267 static const char* name = "right";
268 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
269 insetAmount, halfFilterWidth,
270 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
271}
272
273// Make a rect that only touches the enclosing rect on the bottom.
274static const SkRect* bot_only(const RectInfo& enclosing,
275 RectInfo* result,
276 bool isInsetHard,
277 bool areCoordsLimitedToRect,
278 float insetAmount,
279 float halfFilterWidth) {
280 static const char* name = "bot";
281 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
282 insetAmount, halfFilterWidth,
283 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
284}
285
286// Make a rect that is inset all around.
287static const SkRect* full_inset(const RectInfo& enclosing,
288 RectInfo* result,
289 bool isInsetHard,
290 bool areCoordsLimitedToRect,
291 float insetAmount,
292 float halfFilterWidth) {
293 static const char* name = "all";
294 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
295 insetAmount, halfFilterWidth,
296 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
297}
298
Robert Phillips51e7ca32017-03-27 10:14:08 -0400299// Make a rect with no inset. This is only used for constraint rect creation.
300static const SkRect* no_inset(const RectInfo& enclosing,
301 RectInfo* result,
302 bool isInsetHard,
303 bool areCoordsLimitedToRect,
304 float insetAmount,
305 float halfFilterWidth) {
306 static const char* name = "none";
307 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
308 insetAmount, halfFilterWidth, 0, name);
309}
310
311static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) {
312 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
313 SkRect actualDomainRect;
314
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400315 static const GrSamplerState::Filter gModes[] = {
316 GrSamplerState::Filter::kNearest,
317 GrSamplerState::Filter::kBilerp,
318 GrSamplerState::Filter::kMipMap,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400319 };
320
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400321 static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
322 &gModes[2]};
Robert Phillips51e7ca32017-03-27 10:14:08 -0400323
324 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
325
326 for (auto isPowerOfTwoSized : { true, false }) {
327 for (auto isExact : { true, false }) {
328 RectInfo outermost;
329
330 sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized,
331 isExact, &outermost);
332 SkASSERT(outermost.isHardOrBadAllAround());
Brian Salomon4df00922017-09-07 16:34:11 +0000333
Greg Danielc77085d2017-11-01 16:38:48 -0400334 for (auto isConstraintRectHard : { true, false }) {
335 for (auto areCoordsLimitedToConstraintRect : { true, false }) {
336 for (int filterMode = 0; filterMode < 4; ++filterMode) {
337 for (auto constraintRectMaker : { left_only, top_only, right_only,
338 bot_only, full_inset, no_inset }) {
339 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
340 RectInfo constraintRectStorage;
341 const SkRect* constraintRect = (*constraintRectMaker)(
342 outermost,
343 &constraintRectStorage,
344 isConstraintRectHard,
345 areCoordsLimitedToConstraintRect,
346 insetAmt,
347 gHalfFilterWidth[filterMode]);
348 SkASSERT(constraintRect); // always need one of these
349 SkASSERT(outermost.rect().contains(*constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400350
Greg Danielc77085d2017-11-01 16:38:48 -0400351 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
352 *constraintRect,
353 isConstraintRectHard
354 ? GrTextureProducer::kYes_FilterConstraint
355 : GrTextureProducer::kNo_FilterConstraint,
356 areCoordsLimitedToConstraintRect,
357 proxy.get(),
358 gModePtrs[filterMode],
359 &actualDomainRect);
360
361 expectedMode = DomainMode::kNoDomain_DomainMode;
362 if (constraintRectStorage.hasABad()) {
363 if (3 == filterMode) {
364 expectedMode = DomainMode::kTightCopy_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400365 } else {
Greg Danielc77085d2017-11-01 16:38:48 -0400366 expectedMode = DomainMode::kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400367 }
Brian Salomon4df00922017-09-07 16:34:11 +0000368 }
Greg Danielc77085d2017-11-01 16:38:48 -0400369
370 REPORTER_ASSERT(reporter, expectedMode == actualMode);
371 // TODO: add a check that the returned domain rect is correct
Robert Phillips51e7ca32017-03-27 10:14:08 -0400372 }
373 }
374 }
375 }
376 }
377 }
378 }
379}
380
381DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
382 GrContext* context = ctxInfo.grContext();
383
384 proxy_test(reporter, context->resourceProvider());
385}
386
387#endif