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