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