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