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