blob: 62fc973135f90ce3ca3c3cedcc8de09f42715772 [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
Robert Phillips0a15cc62019-07-30 12:49:10 -0400133 GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
134 GrRenderable::kNo);
Greg Daniel4065d452018-11-16 15:43:41 -0500135
Robert Phillips51e7ca32017-03-27 10:14:08 -0400136 static const char* name = "proxy";
137
138 // Proxies are always hard on the left and top but can be bad on the right and bottom
139 rect->set(SkRect::MakeWH(size, size),
140 RectInfo::kHard,
141 RectInfo::kHard,
142 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
143 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
144 name);
145
Greg Daniel47c20e82020-01-21 14:29:57 -0500146 GrSwizzle swizzle = caps->getReadSwizzle(format, GrColorType::kRGBA_8888);
147
Brian Salomona56a7462020-02-07 14:17:25 -0500148 return proxyProvider->createProxy(format, {size, size}, swizzle, GrRenderable::kNo, 1,
Greg Daniel3a365112020-02-14 10:47:18 -0500149 GrMipMapped::kNo, fit, SkBudgeted::kYes, GrProtected::kNo);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400150}
151
152static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
153 bool isInsetHard, bool coordsAreLimitedToRect,
154 float insetAmount, float halfFilterWidth) {
155 if (isInsetHard) {
156 if (coordsAreLimitedToRect) {
157 SkASSERT(halfFilterWidth >= 0.0f);
158 if (0.0f == halfFilterWidth) {
159 return RectInfo::kSoft;
160 }
161 }
162
163 if (0.0f == insetAmount && RectInfo::kHard == previous) {
164 return RectInfo::kHard;
165 }
166
167 return RectInfo::kBad;
168 }
169
170 if (RectInfo::kHard == previous) {
171 return RectInfo::kHard;
172 }
173
174 if (coordsAreLimitedToRect) {
175 SkASSERT(halfFilterWidth >= 0.0f);
176 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
177 return RectInfo::kSoft;
178 }
179 }
180
181 return previous;
182}
183
184static const int kInsetLeft_Flag = 0x1;
185static const int kInsetTop_Flag = 0x2;
186static const int kInsetRight_Flag = 0x4;
187static const int kInsetBot_Flag = 0x8;
188
189// If 'isInsetHard' is true we can't sample across the inset boundary.
190// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
191static const SkRect* generic_inset(const RectInfo& enclosing,
192 RectInfo* result,
193 bool isInsetHard,
194 bool areCoordsLimitedToRect,
195 float insetAmount,
196 float halfFilterWidth,
197 uint32_t flags,
198 const char* name) {
199 SkRect newR = enclosing.rect();
200
201 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
202 if (flags & kInsetLeft_Flag) {
203 newR.fLeft += insetAmount;
204 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
205 insetAmount, halfFilterWidth);
206 } else {
207 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
208 0.0f, halfFilterWidth);
209 }
210
211 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
212 if (flags & kInsetTop_Flag) {
213 newR.fTop += insetAmount;
214 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
215 insetAmount, halfFilterWidth);
216 } else {
217 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
218 0.0f, halfFilterWidth);
219 }
220
221 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
222 if (flags & kInsetRight_Flag) {
223 newR.fRight -= insetAmount;
224 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
225 insetAmount, halfFilterWidth);
226 } else {
227 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
228 0.0f, halfFilterWidth);
229 }
230
231 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
232 if (flags & kInsetBot_Flag) {
233 newR.fBottom -= insetAmount;
234 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
235 insetAmount, halfFilterWidth);
236 } else {
237 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
238 0.0f, halfFilterWidth);
239 }
240
241 result->set(newR, left, top, right, bot, name);
242 return &result->rect();
243}
244
245// Make a rect that only touches the enclosing rect on the left.
246static const SkRect* left_only(const RectInfo& enclosing,
247 RectInfo* result,
248 bool isInsetHard,
249 bool areCoordsLimitedToRect,
250 float insetAmount,
251 float halfFilterWidth) {
252 static const char* name = "left";
253 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
254 insetAmount, halfFilterWidth,
255 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
256}
257
258// Make a rect that only touches the enclosing rect on the top.
259static const SkRect* top_only(const RectInfo& enclosing,
260 RectInfo* result,
261 bool isInsetHard,
262 bool areCoordsLimitedToRect,
263 float insetAmount,
264 float halfFilterWidth) {
265 static const char* name = "top";
266 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
267 insetAmount, halfFilterWidth,
268 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
269}
270
271// Make a rect that only touches the enclosing rect on the right.
272static const SkRect* right_only(const RectInfo& enclosing,
273 RectInfo* result,
274 bool isInsetHard,
275 bool areCoordsLimitedToRect,
276 float insetAmount,
277 float halfFilterWidth) {
278 static const char* name = "right";
279 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
280 insetAmount, halfFilterWidth,
281 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
282}
283
284// Make a rect that only touches the enclosing rect on the bottom.
285static const SkRect* bot_only(const RectInfo& enclosing,
286 RectInfo* result,
287 bool isInsetHard,
288 bool areCoordsLimitedToRect,
289 float insetAmount,
290 float halfFilterWidth) {
291 static const char* name = "bot";
292 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
293 insetAmount, halfFilterWidth,
294 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
295}
296
297// Make a rect that is inset all around.
298static const SkRect* full_inset(const RectInfo& enclosing,
299 RectInfo* result,
300 bool isInsetHard,
301 bool areCoordsLimitedToRect,
302 float insetAmount,
303 float halfFilterWidth) {
304 static const char* name = "all";
305 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
306 insetAmount, halfFilterWidth,
307 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
308}
309
Robert Phillips51e7ca32017-03-27 10:14:08 -0400310// Make a rect with no inset. This is only used for constraint rect creation.
311static const SkRect* no_inset(const RectInfo& enclosing,
312 RectInfo* result,
313 bool isInsetHard,
314 bool areCoordsLimitedToRect,
315 float insetAmount,
316 float halfFilterWidth) {
317 static const char* name = "none";
318 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
319 insetAmount, halfFilterWidth, 0, name);
320}
321
Greg Daniel4065d452018-11-16 15:43:41 -0500322static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400323 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
324 SkRect actualDomainRect;
325
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400326 static const GrSamplerState::Filter gModes[] = {
327 GrSamplerState::Filter::kNearest,
328 GrSamplerState::Filter::kBilerp,
329 GrSamplerState::Filter::kMipMap,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400330 };
331
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400332 static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
333 &gModes[2]};
Robert Phillips51e7ca32017-03-27 10:14:08 -0400334
335 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
336
337 for (auto isPowerOfTwoSized : { true, false }) {
338 for (auto isExact : { true, false }) {
339 RectInfo outermost;
340
Greg Daniel4065d452018-11-16 15:43:41 -0500341 sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400342 isExact, &outermost);
343 SkASSERT(outermost.isHardOrBadAllAround());
Brian Salomon4df00922017-09-07 16:34:11 +0000344
Greg Danielc77085d2017-11-01 16:38:48 -0400345 for (auto isConstraintRectHard : { true, false }) {
346 for (auto areCoordsLimitedToConstraintRect : { true, false }) {
347 for (int filterMode = 0; filterMode < 4; ++filterMode) {
348 for (auto constraintRectMaker : { left_only, top_only, right_only,
349 bot_only, full_inset, no_inset }) {
350 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
351 RectInfo constraintRectStorage;
352 const SkRect* constraintRect = (*constraintRectMaker)(
353 outermost,
354 &constraintRectStorage,
355 isConstraintRectHard,
356 areCoordsLimitedToConstraintRect,
357 insetAmt,
358 gHalfFilterWidth[filterMode]);
359 SkASSERT(constraintRect); // always need one of these
360 SkASSERT(outermost.rect().contains(*constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400361
Greg Danielc77085d2017-11-01 16:38:48 -0400362 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
363 *constraintRect,
364 isConstraintRectHard
365 ? GrTextureProducer::kYes_FilterConstraint
366 : GrTextureProducer::kNo_FilterConstraint,
367 areCoordsLimitedToConstraintRect,
368 proxy.get(),
369 gModePtrs[filterMode],
370 &actualDomainRect);
371
372 expectedMode = DomainMode::kNoDomain_DomainMode;
373 if (constraintRectStorage.hasABad()) {
374 if (3 == filterMode) {
375 expectedMode = DomainMode::kTightCopy_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400376 } else {
Greg Danielc77085d2017-11-01 16:38:48 -0400377 expectedMode = DomainMode::kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400378 }
Brian Salomon4df00922017-09-07 16:34:11 +0000379 }
Greg Danielc77085d2017-11-01 16:38:48 -0400380
381 REPORTER_ASSERT(reporter, expectedMode == actualMode);
382 // TODO: add a check that the returned domain rect is correct
Robert Phillips51e7ca32017-03-27 10:14:08 -0400383 }
384 }
385 }
386 }
387 }
388 }
389 }
390}
391
392DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
393 GrContext* context = ctxInfo.grContext();
394
Greg Daniel4065d452018-11-16 15:43:41 -0500395 proxy_test(reporter, context);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400396}