Home | History | Annotate | Download | only in tests
      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 
     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 
     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 
     64     const SkRect& rect() const { return fRect; }
     65     EdgeType edgeType(Side side) const { return fTypes[side]; }
     66     const char* name() const { return fName; }
     67 
     68 #ifdef SK_DEBUG
     69     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 
     79     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
     89     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
    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 
    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 
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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 
    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 
    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