blob: c7d350e4b9a5dda6da10c07834860dcfa294d877 [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"
14#include "include/gpu/GrSamplerState.h"
15#include "include/gpu/GrTypes.h"
16#include "include/private/GrTextureProxy.h"
17#include "include/private/GrTypesPriv.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040018#include "src/gpu/GrCaps.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/GrContextPriv.h"
20#include "src/gpu/GrProxyProvider.h"
21#include "src/gpu/GrTextureProducer.h"
22#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 Phillips51e7ca32017-03-27 10:14:08 -0400128 int size = isPowerOfTwo ? 128 : 100;
129 SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
130
131 GrSurfaceDesc desc;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400132 desc.fWidth = size;
133 desc.fHeight = size;
Robert Phillips16d8ec62017-07-27 16:16:25 -0400134 desc.fConfig = kRGBA_8888_GrPixelConfig;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400135
Greg Daniel4065d452018-11-16 15:43:41 -0500136 GrBackendFormat format =
Robert Phillips9da87e02019-02-04 13:26:26 -0500137 ctx->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
Greg Daniel4065d452018-11-16 15:43:41 -0500138
Robert Phillips51e7ca32017-03-27 10:14:08 -0400139 static const char* name = "proxy";
140
141 // Proxies are always hard on the left and top but can be bad on the right and bottom
142 rect->set(SkRect::MakeWH(size, size),
143 RectInfo::kHard,
144 RectInfo::kHard,
145 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
146 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
147 name);
148
Greg Daniel4065d452018-11-16 15:43:41 -0500149 return proxyProvider->createProxy(format, desc, kTopLeft_GrSurfaceOrigin, fit,
150 SkBudgeted::kYes);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400151}
152
153static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
154 bool isInsetHard, bool coordsAreLimitedToRect,
155 float insetAmount, float halfFilterWidth) {
156 if (isInsetHard) {
157 if (coordsAreLimitedToRect) {
158 SkASSERT(halfFilterWidth >= 0.0f);
159 if (0.0f == halfFilterWidth) {
160 return RectInfo::kSoft;
161 }
162 }
163
164 if (0.0f == insetAmount && RectInfo::kHard == previous) {
165 return RectInfo::kHard;
166 }
167
168 return RectInfo::kBad;
169 }
170
171 if (RectInfo::kHard == previous) {
172 return RectInfo::kHard;
173 }
174
175 if (coordsAreLimitedToRect) {
176 SkASSERT(halfFilterWidth >= 0.0f);
177 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
178 return RectInfo::kSoft;
179 }
180 }
181
182 return previous;
183}
184
185static const int kInsetLeft_Flag = 0x1;
186static const int kInsetTop_Flag = 0x2;
187static const int kInsetRight_Flag = 0x4;
188static const int kInsetBot_Flag = 0x8;
189
190// If 'isInsetHard' is true we can't sample across the inset boundary.
191// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
192static const SkRect* generic_inset(const RectInfo& enclosing,
193 RectInfo* result,
194 bool isInsetHard,
195 bool areCoordsLimitedToRect,
196 float insetAmount,
197 float halfFilterWidth,
198 uint32_t flags,
199 const char* name) {
200 SkRect newR = enclosing.rect();
201
202 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
203 if (flags & kInsetLeft_Flag) {
204 newR.fLeft += insetAmount;
205 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
206 insetAmount, halfFilterWidth);
207 } else {
208 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
209 0.0f, halfFilterWidth);
210 }
211
212 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
213 if (flags & kInsetTop_Flag) {
214 newR.fTop += insetAmount;
215 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
216 insetAmount, halfFilterWidth);
217 } else {
218 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
219 0.0f, halfFilterWidth);
220 }
221
222 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
223 if (flags & kInsetRight_Flag) {
224 newR.fRight -= insetAmount;
225 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
226 insetAmount, halfFilterWidth);
227 } else {
228 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
229 0.0f, halfFilterWidth);
230 }
231
232 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
233 if (flags & kInsetBot_Flag) {
234 newR.fBottom -= insetAmount;
235 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
236 insetAmount, halfFilterWidth);
237 } else {
238 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
239 0.0f, halfFilterWidth);
240 }
241
242 result->set(newR, left, top, right, bot, name);
243 return &result->rect();
244}
245
246// Make a rect that only touches the enclosing rect on the left.
247static const SkRect* left_only(const RectInfo& enclosing,
248 RectInfo* result,
249 bool isInsetHard,
250 bool areCoordsLimitedToRect,
251 float insetAmount,
252 float halfFilterWidth) {
253 static const char* name = "left";
254 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
255 insetAmount, halfFilterWidth,
256 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
257}
258
259// Make a rect that only touches the enclosing rect on the top.
260static const SkRect* top_only(const RectInfo& enclosing,
261 RectInfo* result,
262 bool isInsetHard,
263 bool areCoordsLimitedToRect,
264 float insetAmount,
265 float halfFilterWidth) {
266 static const char* name = "top";
267 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
268 insetAmount, halfFilterWidth,
269 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
270}
271
272// Make a rect that only touches the enclosing rect on the right.
273static const SkRect* right_only(const RectInfo& enclosing,
274 RectInfo* result,
275 bool isInsetHard,
276 bool areCoordsLimitedToRect,
277 float insetAmount,
278 float halfFilterWidth) {
279 static const char* name = "right";
280 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
281 insetAmount, halfFilterWidth,
282 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
283}
284
285// Make a rect that only touches the enclosing rect on the bottom.
286static const SkRect* bot_only(const RectInfo& enclosing,
287 RectInfo* result,
288 bool isInsetHard,
289 bool areCoordsLimitedToRect,
290 float insetAmount,
291 float halfFilterWidth) {
292 static const char* name = "bot";
293 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
294 insetAmount, halfFilterWidth,
295 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
296}
297
298// Make a rect that is inset all around.
299static const SkRect* full_inset(const RectInfo& enclosing,
300 RectInfo* result,
301 bool isInsetHard,
302 bool areCoordsLimitedToRect,
303 float insetAmount,
304 float halfFilterWidth) {
305 static const char* name = "all";
306 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
307 insetAmount, halfFilterWidth,
308 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
309}
310
Robert Phillips51e7ca32017-03-27 10:14:08 -0400311// Make a rect with no inset. This is only used for constraint rect creation.
312static const SkRect* no_inset(const RectInfo& enclosing,
313 RectInfo* result,
314 bool isInsetHard,
315 bool areCoordsLimitedToRect,
316 float insetAmount,
317 float halfFilterWidth) {
318 static const char* name = "none";
319 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
320 insetAmount, halfFilterWidth, 0, name);
321}
322
Greg Daniel4065d452018-11-16 15:43:41 -0500323static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400324 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
325 SkRect actualDomainRect;
326
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400327 static const GrSamplerState::Filter gModes[] = {
328 GrSamplerState::Filter::kNearest,
329 GrSamplerState::Filter::kBilerp,
330 GrSamplerState::Filter::kMipMap,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400331 };
332
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400333 static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
334 &gModes[2]};
Robert Phillips51e7ca32017-03-27 10:14:08 -0400335
336 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
337
338 for (auto isPowerOfTwoSized : { true, false }) {
339 for (auto isExact : { true, false }) {
340 RectInfo outermost;
341
Greg Daniel4065d452018-11-16 15:43:41 -0500342 sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400343 isExact, &outermost);
344 SkASSERT(outermost.isHardOrBadAllAround());
Brian Salomon4df00922017-09-07 16:34:11 +0000345
Greg Danielc77085d2017-11-01 16:38:48 -0400346 for (auto isConstraintRectHard : { true, false }) {
347 for (auto areCoordsLimitedToConstraintRect : { true, false }) {
348 for (int filterMode = 0; filterMode < 4; ++filterMode) {
349 for (auto constraintRectMaker : { left_only, top_only, right_only,
350 bot_only, full_inset, no_inset }) {
351 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
352 RectInfo constraintRectStorage;
353 const SkRect* constraintRect = (*constraintRectMaker)(
354 outermost,
355 &constraintRectStorage,
356 isConstraintRectHard,
357 areCoordsLimitedToConstraintRect,
358 insetAmt,
359 gHalfFilterWidth[filterMode]);
360 SkASSERT(constraintRect); // always need one of these
361 SkASSERT(outermost.rect().contains(*constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400362
Greg Danielc77085d2017-11-01 16:38:48 -0400363 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
364 *constraintRect,
365 isConstraintRectHard
366 ? GrTextureProducer::kYes_FilterConstraint
367 : GrTextureProducer::kNo_FilterConstraint,
368 areCoordsLimitedToConstraintRect,
369 proxy.get(),
370 gModePtrs[filterMode],
371 &actualDomainRect);
372
373 expectedMode = DomainMode::kNoDomain_DomainMode;
374 if (constraintRectStorage.hasABad()) {
375 if (3 == filterMode) {
376 expectedMode = DomainMode::kTightCopy_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400377 } else {
Greg Danielc77085d2017-11-01 16:38:48 -0400378 expectedMode = DomainMode::kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400379 }
Brian Salomon4df00922017-09-07 16:34:11 +0000380 }
Greg Danielc77085d2017-11-01 16:38:48 -0400381
382 REPORTER_ASSERT(reporter, expectedMode == actualMode);
383 // TODO: add a check that the returned domain rect is correct
Robert Phillips51e7ca32017-03-27 10:14:08 -0400384 }
385 }
386 }
387 }
388 }
389 }
390 }
391}
392
393DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
394 GrContext* context = ctxInfo.grContext();
395
Greg Daniel4065d452018-11-16 15:43:41 -0500396 proxy_test(reporter, context);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400397}