blob: d1b1cd85557cf7ed5b1e35e4eaba1a5d876c1b36 [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 Wagner9707a7e2019-05-06 17:17:19 -04008#include "include/core/SkImageInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/core/SkRect.h"
10#include "include/core/SkRefCnt.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040011#include "include/core/SkTypes.h"
12#include "include/gpu/GrBackendSurface.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/gpu/GrContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/gpu/GrTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/private/GrTypesPriv.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040016#include "src/gpu/GrCaps.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrContextPriv.h"
18#include "src/gpu/GrProxyProvider.h"
Brian Salomon201cdbb2019-08-14 17:00:30 -040019#include "src/gpu/GrSamplerState.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/gpu/GrTextureProducer.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040021#include "src/gpu/GrTextureProxy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "tests/Test.h"
23#include "tools/gpu/GrContextFactory.h"
Ben Wagnerd90cd3b2018-05-22 10:48:08 -040024
25#include <initializer_list>
Robert Phillips51e7ca32017-03-27 10:14:08 -040026
Greg Danielc77085d2017-11-01 16:38:48 -040027// For DetermineDomainMode (in the MDB world) we have 3 rects:
Robert Phillips51e7ca32017-03-27 10:14:08 -040028// 1) the final instantiated backing storage (i.e., the actual GrTexture's extent)
29// 2) the proxy's extent, which may or may not match the GrTexture's extent
Greg Danielc77085d2017-11-01 16:38:48 -040030// 3) the constraint rect, which can optionally be hard or soft
Robert Phillips51e7ca32017-03-27 10:14:08 -040031// This test "fuzzes" all the combinations of these rects.
32class GrTextureProducer_TestAccess {
33public:
34 using DomainMode = GrTextureProducer::DomainMode;
35
Brian Salomon2bbdcc42017-09-07 12:36:34 -040036 static DomainMode DetermineDomainMode(const SkRect& constraintRect,
37 GrTextureProducer::FilterConstraint filterConstraint,
38 bool coordsLimitedToConstraintRect,
39 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040040 const GrSamplerState::Filter* filterModeOrNullForBicubic,
41 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040042 return GrTextureProducer::DetermineDomainMode(constraintRect,
43 filterConstraint,
44 coordsLimitedToConstraintRect,
45 proxy,
Robert Phillips51e7ca32017-03-27 10:14:08 -040046 filterModeOrNullForBicubic,
47 domainRect);
48 }
49};
50
51using DomainMode = GrTextureProducer_TestAccess::DomainMode;
52
Robert Phillips51e7ca32017-03-27 10:14:08 -040053class RectInfo {
54public:
55 enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 };
56
57 enum EdgeType {
58 kSoft = 0, // there is data on the other side of this edge that we are allowed to sample
59 kHard = 1, // the backing resource ends at this edge
60 kBad = 2 // we can't sample across this edge
61 };
62
63 void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot,
64 const char* name) {
65 fRect = rect;
66 fTypes[kLeft] = left;
67 fTypes[kTop] = top;
68 fTypes[kRight] = right;
69 fTypes[kBot] = bot;
70 fName = name;
71 }
72
73 const SkRect& rect() const { return fRect; }
74 EdgeType edgeType(Side side) const { return fTypes[side]; }
75 const char* name() const { return fName; }
76
77#ifdef SK_DEBUG
78 bool isHardOrBadAllAround() const {
79 for (int i = 0; i < 4; ++i) {
80 if (kHard != fTypes[i] && kBad != fTypes[i]) {
81 return false;
82 }
83 }
84 return true;
85 }
86#endif
87
88 bool hasABad() const {
89 for (int i = 0; i < 4; ++i) {
90 if (kBad == fTypes[i]) {
91 return true;
92 }
93 }
94 return false;
95 }
96
97#ifdef SK_DEBUG
98 void print(const char* label) const {
99 SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n",
100 label, fName,
101 fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
102 ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]),
103 ToStr(fTypes[kRight]), ToStr(fTypes[kBot]));
104 }
105#endif
106
107private:
108#ifdef SK_DEBUG
109 static const char* ToStr(EdgeType type) {
110 static const char* names[] = { "soft", "hard", "bad" };
111 return names[type];
112 }
113#endif
114
115 RectInfo operator=(const RectInfo& other); // disallow
116
117 SkRect fRect;
118 EdgeType fTypes[4];
119 const char* fName;
120
121};
122
Greg Daniel4065d452018-11-16 15:43:41 -0500123static sk_sp<GrTextureProxy> create_proxy(GrContext* ctx,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400124 bool isPowerOfTwo,
125 bool isExact,
126 RectInfo* rect) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500127 GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
Robert Phillips0a15cc62019-07-30 12:49:10 -0400128 const GrCaps* caps = ctx->priv().caps();
129
Robert Phillips51e7ca32017-03-27 10:14:08 -0400130 int size = isPowerOfTwo ? 128 : 100;
131 SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
132
133 GrSurfaceDesc desc;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400134 desc.fWidth = size;
135 desc.fHeight = size;
136
Robert Phillips0a15cc62019-07-30 12:49:10 -0400137 GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
138 GrRenderable::kNo);
Greg Daniel4065d452018-11-16 15:43:41 -0500139
Robert Phillips51e7ca32017-03-27 10:14:08 -0400140 static const char* name = "proxy";
141
142 // Proxies are always hard on the left and top but can be bad on the right and bottom
143 rect->set(SkRect::MakeWH(size, size),
144 RectInfo::kHard,
145 RectInfo::kHard,
146 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
147 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
148 name);
149
Greg Daniel47c20e82020-01-21 14:29:57 -0500150 GrSwizzle swizzle = caps->getReadSwizzle(format, GrColorType::kRGBA_8888);
151
152 return proxyProvider->createProxy(format, desc, swizzle, GrRenderable::kNo, 1,
153 kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, fit,
154 SkBudgeted::kYes, GrProtected::kNo);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400155}
156
157static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
158 bool isInsetHard, bool coordsAreLimitedToRect,
159 float insetAmount, float halfFilterWidth) {
160 if (isInsetHard) {
161 if (coordsAreLimitedToRect) {
162 SkASSERT(halfFilterWidth >= 0.0f);
163 if (0.0f == halfFilterWidth) {
164 return RectInfo::kSoft;
165 }
166 }
167
168 if (0.0f == insetAmount && RectInfo::kHard == previous) {
169 return RectInfo::kHard;
170 }
171
172 return RectInfo::kBad;
173 }
174
175 if (RectInfo::kHard == previous) {
176 return RectInfo::kHard;
177 }
178
179 if (coordsAreLimitedToRect) {
180 SkASSERT(halfFilterWidth >= 0.0f);
181 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
182 return RectInfo::kSoft;
183 }
184 }
185
186 return previous;
187}
188
189static const int kInsetLeft_Flag = 0x1;
190static const int kInsetTop_Flag = 0x2;
191static const int kInsetRight_Flag = 0x4;
192static const int kInsetBot_Flag = 0x8;
193
194// If 'isInsetHard' is true we can't sample across the inset boundary.
195// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
196static const SkRect* generic_inset(const RectInfo& enclosing,
197 RectInfo* result,
198 bool isInsetHard,
199 bool areCoordsLimitedToRect,
200 float insetAmount,
201 float halfFilterWidth,
202 uint32_t flags,
203 const char* name) {
204 SkRect newR = enclosing.rect();
205
206 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
207 if (flags & kInsetLeft_Flag) {
208 newR.fLeft += insetAmount;
209 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
210 insetAmount, halfFilterWidth);
211 } else {
212 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
213 0.0f, halfFilterWidth);
214 }
215
216 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
217 if (flags & kInsetTop_Flag) {
218 newR.fTop += insetAmount;
219 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
220 insetAmount, halfFilterWidth);
221 } else {
222 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
223 0.0f, halfFilterWidth);
224 }
225
226 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
227 if (flags & kInsetRight_Flag) {
228 newR.fRight -= insetAmount;
229 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
230 insetAmount, halfFilterWidth);
231 } else {
232 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
233 0.0f, halfFilterWidth);
234 }
235
236 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
237 if (flags & kInsetBot_Flag) {
238 newR.fBottom -= insetAmount;
239 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
240 insetAmount, halfFilterWidth);
241 } else {
242 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
243 0.0f, halfFilterWidth);
244 }
245
246 result->set(newR, left, top, right, bot, name);
247 return &result->rect();
248}
249
250// Make a rect that only touches the enclosing rect on the left.
251static const SkRect* left_only(const RectInfo& enclosing,
252 RectInfo* result,
253 bool isInsetHard,
254 bool areCoordsLimitedToRect,
255 float insetAmount,
256 float halfFilterWidth) {
257 static const char* name = "left";
258 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
259 insetAmount, halfFilterWidth,
260 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
261}
262
263// Make a rect that only touches the enclosing rect on the top.
264static const SkRect* top_only(const RectInfo& enclosing,
265 RectInfo* result,
266 bool isInsetHard,
267 bool areCoordsLimitedToRect,
268 float insetAmount,
269 float halfFilterWidth) {
270 static const char* name = "top";
271 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
272 insetAmount, halfFilterWidth,
273 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
274}
275
276// Make a rect that only touches the enclosing rect on the right.
277static const SkRect* right_only(const RectInfo& enclosing,
278 RectInfo* result,
279 bool isInsetHard,
280 bool areCoordsLimitedToRect,
281 float insetAmount,
282 float halfFilterWidth) {
283 static const char* name = "right";
284 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
285 insetAmount, halfFilterWidth,
286 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
287}
288
289// Make a rect that only touches the enclosing rect on the bottom.
290static const SkRect* bot_only(const RectInfo& enclosing,
291 RectInfo* result,
292 bool isInsetHard,
293 bool areCoordsLimitedToRect,
294 float insetAmount,
295 float halfFilterWidth) {
296 static const char* name = "bot";
297 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
298 insetAmount, halfFilterWidth,
299 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
300}
301
302// Make a rect that is inset all around.
303static const SkRect* full_inset(const RectInfo& enclosing,
304 RectInfo* result,
305 bool isInsetHard,
306 bool areCoordsLimitedToRect,
307 float insetAmount,
308 float halfFilterWidth) {
309 static const char* name = "all";
310 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
311 insetAmount, halfFilterWidth,
312 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
313}
314
Robert Phillips51e7ca32017-03-27 10:14:08 -0400315// Make a rect with no inset. This is only used for constraint rect creation.
316static const SkRect* no_inset(const RectInfo& enclosing,
317 RectInfo* result,
318 bool isInsetHard,
319 bool areCoordsLimitedToRect,
320 float insetAmount,
321 float halfFilterWidth) {
322 static const char* name = "none";
323 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
324 insetAmount, halfFilterWidth, 0, name);
325}
326
Greg Daniel4065d452018-11-16 15:43:41 -0500327static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400328 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
329 SkRect actualDomainRect;
330
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400331 static const GrSamplerState::Filter gModes[] = {
332 GrSamplerState::Filter::kNearest,
333 GrSamplerState::Filter::kBilerp,
334 GrSamplerState::Filter::kMipMap,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400335 };
336
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400337 static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
338 &gModes[2]};
Robert Phillips51e7ca32017-03-27 10:14:08 -0400339
340 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
341
342 for (auto isPowerOfTwoSized : { true, false }) {
343 for (auto isExact : { true, false }) {
344 RectInfo outermost;
345
Greg Daniel4065d452018-11-16 15:43:41 -0500346 sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400347 isExact, &outermost);
348 SkASSERT(outermost.isHardOrBadAllAround());
Brian Salomon4df00922017-09-07 16:34:11 +0000349
Greg Danielc77085d2017-11-01 16:38:48 -0400350 for (auto isConstraintRectHard : { true, false }) {
351 for (auto areCoordsLimitedToConstraintRect : { true, false }) {
352 for (int filterMode = 0; filterMode < 4; ++filterMode) {
353 for (auto constraintRectMaker : { left_only, top_only, right_only,
354 bot_only, full_inset, no_inset }) {
355 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
356 RectInfo constraintRectStorage;
357 const SkRect* constraintRect = (*constraintRectMaker)(
358 outermost,
359 &constraintRectStorage,
360 isConstraintRectHard,
361 areCoordsLimitedToConstraintRect,
362 insetAmt,
363 gHalfFilterWidth[filterMode]);
364 SkASSERT(constraintRect); // always need one of these
365 SkASSERT(outermost.rect().contains(*constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400366
Greg Danielc77085d2017-11-01 16:38:48 -0400367 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
368 *constraintRect,
369 isConstraintRectHard
370 ? GrTextureProducer::kYes_FilterConstraint
371 : GrTextureProducer::kNo_FilterConstraint,
372 areCoordsLimitedToConstraintRect,
373 proxy.get(),
374 gModePtrs[filterMode],
375 &actualDomainRect);
376
377 expectedMode = DomainMode::kNoDomain_DomainMode;
378 if (constraintRectStorage.hasABad()) {
379 if (3 == filterMode) {
380 expectedMode = DomainMode::kTightCopy_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400381 } else {
Greg Danielc77085d2017-11-01 16:38:48 -0400382 expectedMode = DomainMode::kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400383 }
Brian Salomon4df00922017-09-07 16:34:11 +0000384 }
Greg Danielc77085d2017-11-01 16:38:48 -0400385
386 REPORTER_ASSERT(reporter, expectedMode == actualMode);
387 // TODO: add a check that the returned domain rect is correct
Robert Phillips51e7ca32017-03-27 10:14:08 -0400388 }
389 }
390 }
391 }
392 }
393 }
394 }
395}
396
397DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
398 GrContext* context = ctxInfo.grContext();
399
Greg Daniel4065d452018-11-16 15:43:41 -0500400 proxy_test(reporter, context);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400401}