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