• 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 "SkTypes.h"
9 
10 #include "GrContext.h"
11 #include "GrContextFactory.h"
12 #include "GrContextPriv.h"
13 #include "GrProxyProvider.h"
14 #include "GrSamplerState.h"
15 #include "GrTextureProducer.h"
16 #include "GrTextureProxy.h"
17 #include "GrTypes.h"
18 #include "GrTypesPriv.h"
19 #include "SkRect.h"
20 #include "SkRefCnt.h"
21 #include "Test.h"
22 
23 #include <initializer_list>
24 
25 // For DetermineDomainMode (in the MDB world) we have 3 rects:
26 //      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
28 //      3) the constraint rect, which can optionally be hard or soft
29 // This test "fuzzes" all the combinations of these rects.
30 class GrTextureProducer_TestAccess {
31 public:
32     using DomainMode = GrTextureProducer::DomainMode;
33 
DetermineDomainMode(const SkRect & constraintRect,GrTextureProducer::FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,GrTextureProxy * proxy,const GrSamplerState::Filter * filterModeOrNullForBicubic,SkRect * domainRect)34     static DomainMode DetermineDomainMode(const SkRect& constraintRect,
35                                           GrTextureProducer::FilterConstraint filterConstraint,
36                                           bool coordsLimitedToConstraintRect,
37                                           GrTextureProxy* proxy,
38                                           const GrSamplerState::Filter* filterModeOrNullForBicubic,
39                                           SkRect* domainRect) {
40         return GrTextureProducer::DetermineDomainMode(constraintRect,
41                                                       filterConstraint,
42                                                       coordsLimitedToConstraintRect,
43                                                       proxy,
44                                                       filterModeOrNullForBicubic,
45                                                       domainRect);
46     }
47 };
48 
49 using DomainMode = GrTextureProducer_TestAccess::DomainMode;
50 
51 class RectInfo {
52 public:
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 
set(const SkRect & rect,EdgeType left,EdgeType top,EdgeType right,EdgeType bot,const char * name)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 
rect() const71     const SkRect& rect() const { return fRect; }
edgeType(Side side) const72     EdgeType edgeType(Side side) const { return fTypes[side]; }
name() const73     const char* name() const { return fName; }
74 
75 #ifdef SK_DEBUG
isHardOrBadAllAround() const76     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 
hasABad() const86     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
print(const char * label) const96     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 
105 private:
106 #ifdef SK_DEBUG
ToStr(EdgeType type)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 
create_proxy(GrContext * ctx,bool isPowerOfTwo,bool isExact,RectInfo * rect)121 static sk_sp<GrTextureProxy> create_proxy(GrContext* ctx,
122                                           bool isPowerOfTwo,
123                                           bool isExact,
124                                           RectInfo* rect) {
125     GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
126     int size = isPowerOfTwo ? 128 : 100;
127     SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
128 
129     GrSurfaceDesc desc;
130     desc.fWidth = size;
131     desc.fHeight = size;
132     desc.fConfig = kRGBA_8888_GrPixelConfig;
133 
134     GrBackendFormat format =
135             ctx->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
136 
137     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 
147     return proxyProvider->createProxy(format, desc, kTopLeft_GrSurfaceOrigin, fit,
148                                       SkBudgeted::kYes);
149 }
150 
compute_inset_edgetype(RectInfo::EdgeType previous,bool isInsetHard,bool coordsAreLimitedToRect,float insetAmount,float halfFilterWidth)151 static 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 
183 static const int kInsetLeft_Flag  = 0x1;
184 static const int kInsetTop_Flag   = 0x2;
185 static const int kInsetRight_Flag = 0x4;
186 static 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.
generic_inset(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth,uint32_t flags,const char * name)190 static 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.
left_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)245 static 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.
top_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)258 static 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.
right_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)271 static 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.
bot_only(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)284 static 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.
full_inset(const RectInfo & enclosing,RectInfo * result,bool isInsetHard,bool areCoordsLimitedToRect,float insetAmount,float halfFilterWidth)297 static 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 
309 // 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)310 static 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 
proxy_test(skiatest::Reporter * reporter,GrContext * context)321 static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
322     GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
323     SkRect actualDomainRect;
324 
325     static const GrSamplerState::Filter gModes[] = {
326             GrSamplerState::Filter::kNearest,
327             GrSamplerState::Filter::kBilerp,
328             GrSamplerState::Filter::kMipMap,
329     };
330 
331     static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
332                                                         &gModes[2]};
333 
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 
340             sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
341                                                        isExact, &outermost);
342             SkASSERT(outermost.isHardOrBadAllAround());
343 
344             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));
360 
361                                 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;
375                                     } else {
376                                         expectedMode = DomainMode::kDomain_DomainMode;
377                                     }
378                                 }
379 
380                                 REPORTER_ASSERT(reporter, expectedMode == actualMode);
381                                 // TODO: add a check that the returned domain rect is correct
382                             }
383                         }
384                     }
385                 }
386             }
387         }
388     }
389 }
390 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest,reporter,ctxInfo)391 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
392     GrContext* context = ctxInfo.grContext();
393 
394     proxy_test(reporter, context);
395 }
396