blob: 907e535a9e7832f3e5ff0730ce664942373aff07 [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
Greg Daniel4065d452018-11-16 15:43:41 -0500121static sk_sp<GrTextureProxy> create_proxy(GrContext* ctx,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400122 bool isPowerOfTwo,
123 bool isExact,
124 RectInfo* rect) {
Greg Daniel4065d452018-11-16 15:43:41 -0500125 GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
Robert Phillips51e7ca32017-03-27 10:14:08 -0400126 int size = isPowerOfTwo ? 128 : 100;
127 SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
128
129 GrSurfaceDesc desc;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400130 desc.fWidth = size;
131 desc.fHeight = size;
Robert Phillips16d8ec62017-07-27 16:16:25 -0400132 desc.fConfig = kRGBA_8888_GrPixelConfig;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400133
Greg Daniel4065d452018-11-16 15:43:41 -0500134 GrBackendFormat format =
135 ctx->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
136
Robert Phillips51e7ca32017-03-27 10:14:08 -0400137 static const char* name = "proxy";
138
139 // Proxies are always hard on the left and top but can be bad on the right and bottom
140 rect->set(SkRect::MakeWH(size, size),
141 RectInfo::kHard,
142 RectInfo::kHard,
143 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
144 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
145 name);
146
Greg Daniel4065d452018-11-16 15:43:41 -0500147 return proxyProvider->createProxy(format, desc, kTopLeft_GrSurfaceOrigin, fit,
148 SkBudgeted::kYes);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400149}
150
151static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
152 bool isInsetHard, bool coordsAreLimitedToRect,
153 float insetAmount, float halfFilterWidth) {
154 if (isInsetHard) {
155 if (coordsAreLimitedToRect) {
156 SkASSERT(halfFilterWidth >= 0.0f);
157 if (0.0f == halfFilterWidth) {
158 return RectInfo::kSoft;
159 }
160 }
161
162 if (0.0f == insetAmount && RectInfo::kHard == previous) {
163 return RectInfo::kHard;
164 }
165
166 return RectInfo::kBad;
167 }
168
169 if (RectInfo::kHard == previous) {
170 return RectInfo::kHard;
171 }
172
173 if (coordsAreLimitedToRect) {
174 SkASSERT(halfFilterWidth >= 0.0f);
175 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
176 return RectInfo::kSoft;
177 }
178 }
179
180 return previous;
181}
182
183static const int kInsetLeft_Flag = 0x1;
184static const int kInsetTop_Flag = 0x2;
185static const int kInsetRight_Flag = 0x4;
186static const int kInsetBot_Flag = 0x8;
187
188// If 'isInsetHard' is true we can't sample across the inset boundary.
189// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
190static const SkRect* generic_inset(const RectInfo& enclosing,
191 RectInfo* result,
192 bool isInsetHard,
193 bool areCoordsLimitedToRect,
194 float insetAmount,
195 float halfFilterWidth,
196 uint32_t flags,
197 const char* name) {
198 SkRect newR = enclosing.rect();
199
200 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
201 if (flags & kInsetLeft_Flag) {
202 newR.fLeft += insetAmount;
203 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
204 insetAmount, halfFilterWidth);
205 } else {
206 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
207 0.0f, halfFilterWidth);
208 }
209
210 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
211 if (flags & kInsetTop_Flag) {
212 newR.fTop += insetAmount;
213 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
214 insetAmount, halfFilterWidth);
215 } else {
216 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
217 0.0f, halfFilterWidth);
218 }
219
220 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
221 if (flags & kInsetRight_Flag) {
222 newR.fRight -= insetAmount;
223 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
224 insetAmount, halfFilterWidth);
225 } else {
226 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
227 0.0f, halfFilterWidth);
228 }
229
230 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
231 if (flags & kInsetBot_Flag) {
232 newR.fBottom -= insetAmount;
233 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
234 insetAmount, halfFilterWidth);
235 } else {
236 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
237 0.0f, halfFilterWidth);
238 }
239
240 result->set(newR, left, top, right, bot, name);
241 return &result->rect();
242}
243
244// Make a rect that only touches the enclosing rect on the left.
245static const SkRect* left_only(const RectInfo& enclosing,
246 RectInfo* result,
247 bool isInsetHard,
248 bool areCoordsLimitedToRect,
249 float insetAmount,
250 float halfFilterWidth) {
251 static const char* name = "left";
252 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
253 insetAmount, halfFilterWidth,
254 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
255}
256
257// Make a rect that only touches the enclosing rect on the top.
258static const SkRect* top_only(const RectInfo& enclosing,
259 RectInfo* result,
260 bool isInsetHard,
261 bool areCoordsLimitedToRect,
262 float insetAmount,
263 float halfFilterWidth) {
264 static const char* name = "top";
265 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
266 insetAmount, halfFilterWidth,
267 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
268}
269
270// Make a rect that only touches the enclosing rect on the right.
271static const SkRect* right_only(const RectInfo& enclosing,
272 RectInfo* result,
273 bool isInsetHard,
274 bool areCoordsLimitedToRect,
275 float insetAmount,
276 float halfFilterWidth) {
277 static const char* name = "right";
278 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
279 insetAmount, halfFilterWidth,
280 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
281}
282
283// Make a rect that only touches the enclosing rect on the bottom.
284static const SkRect* bot_only(const RectInfo& enclosing,
285 RectInfo* result,
286 bool isInsetHard,
287 bool areCoordsLimitedToRect,
288 float insetAmount,
289 float halfFilterWidth) {
290 static const char* name = "bot";
291 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
292 insetAmount, halfFilterWidth,
293 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
294}
295
296// Make a rect that is inset all around.
297static const SkRect* full_inset(const RectInfo& enclosing,
298 RectInfo* result,
299 bool isInsetHard,
300 bool areCoordsLimitedToRect,
301 float insetAmount,
302 float halfFilterWidth) {
303 static const char* name = "all";
304 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
305 insetAmount, halfFilterWidth,
306 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
307}
308
Robert Phillips51e7ca32017-03-27 10:14:08 -0400309// Make a rect with no inset. This is only used for constraint rect creation.
310static const SkRect* no_inset(const RectInfo& enclosing,
311 RectInfo* result,
312 bool isInsetHard,
313 bool areCoordsLimitedToRect,
314 float insetAmount,
315 float halfFilterWidth) {
316 static const char* name = "none";
317 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
318 insetAmount, halfFilterWidth, 0, name);
319}
320
Greg Daniel4065d452018-11-16 15:43:41 -0500321static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400322 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
323 SkRect actualDomainRect;
324
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400325 static const GrSamplerState::Filter gModes[] = {
326 GrSamplerState::Filter::kNearest,
327 GrSamplerState::Filter::kBilerp,
328 GrSamplerState::Filter::kMipMap,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400329 };
330
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400331 static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
332 &gModes[2]};
Robert Phillips51e7ca32017-03-27 10:14:08 -0400333
334 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
335
336 for (auto isPowerOfTwoSized : { true, false }) {
337 for (auto isExact : { true, false }) {
338 RectInfo outermost;
339
Greg Daniel4065d452018-11-16 15:43:41 -0500340 sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
Robert Phillips51e7ca32017-03-27 10:14:08 -0400341 isExact, &outermost);
342 SkASSERT(outermost.isHardOrBadAllAround());
Brian Salomon4df00922017-09-07 16:34:11 +0000343
Greg Danielc77085d2017-11-01 16:38:48 -0400344 for (auto isConstraintRectHard : { true, false }) {
345 for (auto areCoordsLimitedToConstraintRect : { true, false }) {
346 for (int filterMode = 0; filterMode < 4; ++filterMode) {
347 for (auto constraintRectMaker : { left_only, top_only, right_only,
348 bot_only, full_inset, no_inset }) {
349 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
350 RectInfo constraintRectStorage;
351 const SkRect* constraintRect = (*constraintRectMaker)(
352 outermost,
353 &constraintRectStorage,
354 isConstraintRectHard,
355 areCoordsLimitedToConstraintRect,
356 insetAmt,
357 gHalfFilterWidth[filterMode]);
358 SkASSERT(constraintRect); // always need one of these
359 SkASSERT(outermost.rect().contains(*constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400360
Greg Danielc77085d2017-11-01 16:38:48 -0400361 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
362 *constraintRect,
363 isConstraintRectHard
364 ? GrTextureProducer::kYes_FilterConstraint
365 : GrTextureProducer::kNo_FilterConstraint,
366 areCoordsLimitedToConstraintRect,
367 proxy.get(),
368 gModePtrs[filterMode],
369 &actualDomainRect);
370
371 expectedMode = DomainMode::kNoDomain_DomainMode;
372 if (constraintRectStorage.hasABad()) {
373 if (3 == filterMode) {
374 expectedMode = DomainMode::kTightCopy_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400375 } else {
Greg Danielc77085d2017-11-01 16:38:48 -0400376 expectedMode = DomainMode::kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400377 }
Brian Salomon4df00922017-09-07 16:34:11 +0000378 }
Greg Danielc77085d2017-11-01 16:38:48 -0400379
380 REPORTER_ASSERT(reporter, expectedMode == actualMode);
381 // TODO: add a check that the returned domain rect is correct
Robert Phillips51e7ca32017-03-27 10:14:08 -0400382 }
383 }
384 }
385 }
386 }
387 }
388 }
389}
390
391DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
392 GrContext* context = ctxInfo.grContext();
393
Greg Daniel4065d452018-11-16 15:43:41 -0500394 proxy_test(reporter, context);
Robert Phillips51e7ca32017-03-27 10:14:08 -0400395}