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