• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "Test.h"
9 
10 #if SK_SUPPORT_GPU
11 
12 #include "GrSurfaceProxy.h"
13 #include "GrTextureProducer.h"
14 #include "GrTextureProxy.h"
15 
16 // For DetermineDomainMode (in the MDB world) we have 4 rects:
17 //      1) the final instantiated backing storage (i.e., the actual GrTexture's extent)
18 //      2) the proxy's extent, which may or may not match the GrTexture's extent
19 //      3) the content rect, which can be a subset of the proxy's extent or null
20 //      4) the constraint rect, which can optionally be hard or soft
21 // This test "fuzzes" all the combinations of these rects.
22 class GrTextureProducer_TestAccess {
23 public:
24     using DomainMode = GrTextureProducer::DomainMode;
25 
DetermineDomainMode(const SkRect & constraintRect,GrTextureProducer::FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,GrTextureProxy * proxy,const SkIRect * textureContentArea,const GrSamplerParams::FilterMode * filterModeOrNullForBicubic,SkRect * domainRect)26     static DomainMode DetermineDomainMode(
27                                 const SkRect& constraintRect,
28                                 GrTextureProducer::FilterConstraint filterConstraint,
29                                 bool coordsLimitedToConstraintRect,
30                                 GrTextureProxy* proxy,
31                                 const SkIRect* textureContentArea,
32                                 const GrSamplerParams::FilterMode* filterModeOrNullForBicubic,
33                                 SkRect* domainRect) {
34         return GrTextureProducer::DetermineDomainMode(constraintRect,
35                                                       filterConstraint,
36                                                       coordsLimitedToConstraintRect,
37                                                       proxy,
38                                                       textureContentArea,
39                                                       filterModeOrNullForBicubic,
40                                                       domainRect);
41     }
42 };
43 
44 using DomainMode = GrTextureProducer_TestAccess::DomainMode;
45 
46 #ifdef SK_DEBUG
is_irect(const SkRect & r)47 static bool is_irect(const SkRect& r) {
48     return SkScalarIsInt(r.fLeft)  && SkScalarIsInt(r.fTop) &&
49            SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
50 }
51 #endif
52 
to_irect(const SkRect & r)53 static SkIRect to_irect(const SkRect& r) {
54     SkASSERT(is_irect(r));
55     return SkIRect::MakeLTRB(SkScalarRoundToInt(r.fLeft),
56                              SkScalarRoundToInt(r.fTop),
57                              SkScalarRoundToInt(r.fRight),
58                              SkScalarRoundToInt(r.fBottom));
59 }
60 
61 
62 class RectInfo {
63 public:
64     enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 };
65 
66     enum EdgeType {
67         kSoft = 0,   // there is data on the other side of this edge that we are allowed to sample
68         kHard = 1,   // the backing resource ends at this edge
69         kBad  = 2    // we can't sample across this edge
70     };
71 
set(const SkRect & rect,EdgeType left,EdgeType top,EdgeType right,EdgeType bot,const char * name)72     void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot,
73              const char* name) {
74         fRect = rect;
75         fTypes[kLeft]  = left;
76         fTypes[kTop]   = top;
77         fTypes[kRight] = right;
78         fTypes[kBot]   = bot;
79         fName = name;
80     }
81 
rect() const82     const SkRect& rect() const { return fRect; }
edgeType(Side side) const83     EdgeType edgeType(Side side) const { return fTypes[side]; }
name() const84     const char* name() const { return fName; }
85 
86 #ifdef SK_DEBUG
isHardOrBadAllAround() const87     bool isHardOrBadAllAround() const {
88         for (int i = 0; i < 4; ++i) {
89             if (kHard != fTypes[i] && kBad != fTypes[i]) {
90                 return false;
91             }
92         }
93         return true;
94     }
95 #endif
96 
hasABad() const97     bool hasABad() const {
98         for (int i = 0; i < 4; ++i) {
99             if (kBad == fTypes[i]) {
100                 return true;
101             }
102         }
103         return false;
104     }
105 
106 #ifdef SK_DEBUG
print(const char * label) const107     void print(const char* label) const {
108         SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n",
109                  label, fName,
110                  fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
111                  ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]),
112                  ToStr(fTypes[kRight]), ToStr(fTypes[kBot]));
113     }
114 #endif
115 
116 private:
117 #ifdef SK_DEBUG
ToStr(EdgeType type)118     static const char* ToStr(EdgeType type) {
119         static const char* names[] = { "soft", "hard", "bad" };
120         return names[type];
121     }
122 #endif
123 
124     RectInfo operator=(const RectInfo& other); // disallow
125 
126     SkRect      fRect;
127     EdgeType    fTypes[4];
128     const char* fName;
129 
130 };
131 
create_proxy(GrResourceProvider * resourceProvider,bool isPowerOfTwo,bool isExact,RectInfo * rect)132 static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider,
133                                           bool isPowerOfTwo,
134                                           bool isExact,
135                                           RectInfo* rect) {
136     int size = isPowerOfTwo ? 128 : 100;
137     SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
138 
139     GrSurfaceDesc desc;
140     desc.fConfig = kRGBA_8888_GrPixelConfig;
141     desc.fWidth = size;
142     desc.fHeight = size;
143 
144     static const char* name = "proxy";
145 
146     // Proxies are always hard on the left and top but can be bad on the right and bottom
147     rect->set(SkRect::MakeWH(size, size),
148               RectInfo::kHard,
149               RectInfo::kHard,
150               (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
151               (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
152               name);
153 
154     sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider,
155                                                                desc, fit,
156                                                                SkBudgeted::kYes);
157     return proxy;
158 }
159 
compute_inset_edgetype(RectInfo::EdgeType previous,bool isInsetHard,bool coordsAreLimitedToRect,float insetAmount,float halfFilterWidth)160 static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
161                                                  bool isInsetHard, bool coordsAreLimitedToRect,
162                                                  float insetAmount, float halfFilterWidth) {
163     if (isInsetHard) {
164         if (coordsAreLimitedToRect) {
165             SkASSERT(halfFilterWidth >= 0.0f);
166             if (0.0f == halfFilterWidth) {
167                 return RectInfo::kSoft;
168             }
169         }
170 
171         if (0.0f == insetAmount && RectInfo::kHard == previous) {
172             return RectInfo::kHard;
173         }
174 
175         return RectInfo::kBad;
176     }
177 
178     if (RectInfo::kHard == previous) {
179         return RectInfo::kHard;
180     }
181 
182     if (coordsAreLimitedToRect) {
183         SkASSERT(halfFilterWidth >= 0.0f);
184         if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
185             return RectInfo::kSoft;
186         }
187     }
188 
189     return previous;
190 }
191 
192 static const int kInsetLeft_Flag  = 0x1;
193 static const int kInsetTop_Flag   = 0x2;
194 static const int kInsetRight_Flag = 0x4;
195 static const int kInsetBot_Flag   = 0x8;
196 
197 // If 'isInsetHard' is true we can't sample across the inset boundary.
198 // If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
generic_inset(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth,uint32_t flags,const char * name)199 static const SkRect* generic_inset(const RectInfo& enclosing,
200                                    RectInfo* result,
201                                    bool isInsetHard,
202                                    bool areCoordsLimitedToRect,
203                                    float insetAmount,
204                                    float halfFilterWidth,
205                                    uint32_t flags,
206                                    const char* name) {
207     SkRect newR = enclosing.rect();
208 
209     RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
210     if (flags & kInsetLeft_Flag) {
211         newR.fLeft += insetAmount;
212         left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
213                                       insetAmount, halfFilterWidth);
214     } else {
215         left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
216                                       0.0f, halfFilterWidth);
217     }
218 
219     RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
220     if (flags & kInsetTop_Flag) {
221         newR.fTop += insetAmount;
222         top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
223                                      insetAmount, halfFilterWidth);
224     } else {
225         top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
226                                      0.0f, halfFilterWidth);
227     }
228 
229     RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
230     if (flags & kInsetRight_Flag) {
231         newR.fRight -= insetAmount;
232         right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
233                                        insetAmount, halfFilterWidth);
234     } else {
235         right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
236                                        0.0f, halfFilterWidth);
237     }
238 
239     RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
240     if (flags & kInsetBot_Flag) {
241         newR.fBottom -= insetAmount;
242         bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
243                                      insetAmount, halfFilterWidth);
244     } else {
245         bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
246                                      0.0f, halfFilterWidth);
247     }
248 
249     result->set(newR, left, top, right, bot, name);
250     return &result->rect();
251 }
252 
253 // Make a rect that only touches the enclosing rect on the left.
left_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)254 static const SkRect* left_only(const RectInfo& enclosing,
255                                RectInfo* result,
256                                bool isInsetHard,
257                                bool areCoordsLimitedToRect,
258                                float insetAmount,
259                                float halfFilterWidth) {
260     static const char* name = "left";
261     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
262                          insetAmount, halfFilterWidth,
263                          kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
264 }
265 
266 // Make a rect that only touches the enclosing rect on the top.
top_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)267 static const SkRect* top_only(const RectInfo& enclosing,
268                                RectInfo* result,
269                                bool isInsetHard,
270                                bool areCoordsLimitedToRect,
271                                float insetAmount,
272                                float halfFilterWidth) {
273     static const char* name = "top";
274     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
275                          insetAmount, halfFilterWidth,
276                          kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
277 }
278 
279 // Make a rect that only touches the enclosing rect on the right.
right_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)280 static const SkRect* right_only(const RectInfo& enclosing,
281                                 RectInfo* result,
282                                 bool isInsetHard,
283                                 bool areCoordsLimitedToRect,
284                                 float insetAmount,
285                                 float halfFilterWidth) {
286     static const char* name = "right";
287     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
288                          insetAmount, halfFilterWidth,
289                          kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
290 }
291 
292 // Make a rect that only touches the enclosing rect on the bottom.
bot_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)293 static const SkRect* bot_only(const RectInfo& enclosing,
294                               RectInfo* result,
295                               bool isInsetHard,
296                               bool areCoordsLimitedToRect,
297                               float insetAmount,
298                               float halfFilterWidth) {
299     static const char* name = "bot";
300     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
301                          insetAmount, halfFilterWidth,
302                          kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
303 }
304 
305 // Make a rect that is inset all around.
full_inset(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)306 static const SkRect* full_inset(const RectInfo& enclosing,
307                                 RectInfo* result,
308                                 bool isInsetHard,
309                                 bool areCoordsLimitedToRect,
310                                 float insetAmount,
311                                 float halfFilterWidth) {
312     static const char* name = "all";
313     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
314                          insetAmount, halfFilterWidth,
315                          kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
316 }
317 
318 // This is only used for content rect creation. We ensure 'result' is correct but
319 // return null to indicate no content area (other than what the proxy specifies).
null_rect(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)320 static const SkRect* null_rect(const RectInfo& enclosing,
321                                RectInfo* result,
322                                bool isInsetHard,
323                                bool areCoordsLimitedToRect,
324                                float insetAmount,
325                                float halfFilterWidth) {
326     static const char* name = "null";
327     generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
328                   insetAmount, halfFilterWidth, 0, name);
329     return nullptr;
330 }
331 
332 // Make a rect with no inset. This is only used for constraint rect creation.
no_inset(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)333 static const SkRect* no_inset(const RectInfo& enclosing,
334                               RectInfo* result,
335                               bool isInsetHard,
336                               bool areCoordsLimitedToRect,
337                               float insetAmount,
338                               float halfFilterWidth) {
339     static const char* name = "none";
340     return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
341                          insetAmount, halfFilterWidth, 0, name);
342 }
343 
proxy_test(skiatest::Reporter * reporter,GrResourceProvider * resourceProvider)344 static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) {
345     GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
346     SkRect actualDomainRect;
347 
348     static const GrSamplerParams::FilterMode gModes[] = {
349         GrSamplerParams::kNone_FilterMode,
350         GrSamplerParams::kBilerp_FilterMode,
351         GrSamplerParams::kMipMap_FilterMode,
352     };
353 
354     static const GrSamplerParams::FilterMode* gModePtrs[] = {
355         &gModes[0], &gModes[1], nullptr, &gModes[2]
356     };
357 
358     static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
359 
360     for (auto isPowerOfTwoSized : { true, false }) {
361         for (auto isExact : { true, false }) {
362             RectInfo outermost;
363 
364             sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized,
365                                                        isExact, &outermost);
366             SkASSERT(outermost.isHardOrBadAllAround());
367 
368             for (auto contentRectMaker : { left_only, top_only, right_only,
369                                            bot_only, full_inset, null_rect}) {
370                 RectInfo contentRectStorage;
371                 const SkRect* contentRect = (*contentRectMaker)(outermost,
372                                                                 &contentRectStorage,
373                                                                 true, false, 5.0f, -1.0f);
374                 if (contentRect) {
375                     // We only have content rects if they actually reduce the extent of the content
376                     SkASSERT(!contentRect->contains(outermost.rect()));
377                     SkASSERT(outermost.rect().contains(*contentRect));
378                     SkASSERT(is_irect(*contentRect));
379                 }
380                 SkASSERT(contentRectStorage.isHardOrBadAllAround());
381 
382                 for (auto isConstraintRectHard : { true, false }) {
383                     for (auto areCoordsLimitedToConstraintRect : { true, false }) {
384                         for (int filterMode = 0; filterMode < 4; ++filterMode) {
385                             for (auto constraintRectMaker : { left_only, top_only, right_only,
386                                                               bot_only, full_inset, no_inset }) {
387                                 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
388                                     RectInfo constraintRectStorage;
389                                     const SkRect* constraintRect = (*constraintRectMaker)(
390                                                     contentRect ? contentRectStorage : outermost,
391                                                     &constraintRectStorage,
392                                                     isConstraintRectHard,
393                                                     areCoordsLimitedToConstraintRect,
394                                                     insetAmt,
395                                                     gHalfFilterWidth[filterMode]);
396                                     SkASSERT(constraintRect); // always need one of these
397                                     if (contentRect) {
398                                         SkASSERT(contentRect->contains(*constraintRect));
399                                     } else {
400                                         SkASSERT(outermost.rect().contains(*constraintRect));
401                                     }
402 
403                                     SkIRect contentIRect;
404                                     if (contentRect) {
405                                         contentIRect = to_irect(*contentRect);
406                                     }
407 
408                                     actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
409                                                     *constraintRect,
410                                                     isConstraintRectHard
411                                                         ? GrTextureProducer::kYes_FilterConstraint
412                                                         : GrTextureProducer::kNo_FilterConstraint,
413                                                     areCoordsLimitedToConstraintRect,
414                                                     proxy.get(),
415                                                     contentRect ? &contentIRect : nullptr,
416                                                     gModePtrs[filterMode],
417                                                     &actualDomainRect);
418 
419                                     expectedMode = DomainMode::kNoDomain_DomainMode;
420                                     if (constraintRectStorage.hasABad()) {
421                                         if (3 == filterMode) {
422                                             expectedMode = DomainMode::kTightCopy_DomainMode;
423                                         } else {
424                                             expectedMode = DomainMode::kDomain_DomainMode;
425                                         }
426                                     }
427 
428                                     REPORTER_ASSERT(reporter, expectedMode == actualMode);
429                                     // TODO: add a check that the returned domain rect is correct
430                                 }
431                             }
432                         }
433                     }
434                 }
435             }
436         }
437     }
438 }
439 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest,reporter,ctxInfo)440 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
441     GrContext* context = ctxInfo.grContext();
442 
443     proxy_test(reporter, context->resourceProvider());
444 }
445 
446 #endif
447