diff options
author | Robert Phillips <robertphillips@google.com> | 2017-03-27 10:14:08 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-03-27 14:57:56 +0000 |
commit | 51e7ca31cedc821d2317930d91c5a7dd10cb8735 (patch) | |
tree | 5663cc073e3a769953cd9482096894000a1523db /tests/DetermineDomainModeTest.cpp | |
parent | 59eed0db9909c4a097f5929e1a12bc53552ac6fb (diff) |
Add new proxy-based DetermineDomainMode w/ test
split out of: https://skia-review.googlesource.com/c/8823/ (Remove GrFragmentProcessor-derived class' GrTexture-based ctors)
Change-Id: Ibf54da589710382254b416e4801d83e9f5663f44
Reviewed-on: https://skia-review.googlesource.com/10180
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'tests/DetermineDomainModeTest.cpp')
-rw-r--r-- | tests/DetermineDomainModeTest.cpp | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/tests/DetermineDomainModeTest.cpp b/tests/DetermineDomainModeTest.cpp new file mode 100644 index 0000000000..add746fec5 --- /dev/null +++ b/tests/DetermineDomainModeTest.cpp @@ -0,0 +1,446 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrSurfaceProxy.h" +#include "GrTextureProducer.h" +#include "GrTextureProxy.h" + +// For DetermineDomainMode (in the MDB world) we have 4 rects: +// 1) the final instantiated backing storage (i.e., the actual GrTexture's extent) +// 2) the proxy's extent, which may or may not match the GrTexture's extent +// 3) the content rect, which can be a subset of the proxy's extent or null +// 4) the constraint rect, which can optionally be hard or soft +// This test "fuzzes" all the combinations of these rects. +class GrTextureProducer_TestAccess { +public: + using DomainMode = GrTextureProducer::DomainMode; + + static DomainMode DetermineDomainMode( + const SkRect& constraintRect, + GrTextureProducer::FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + GrTextureProxy* proxy, + const SkIRect* textureContentArea, + const GrSamplerParams::FilterMode* filterModeOrNullForBicubic, + SkRect* domainRect) { + return GrTextureProducer::DetermineDomainMode(constraintRect, + filterConstraint, + coordsLimitedToConstraintRect, + proxy, + textureContentArea, + filterModeOrNullForBicubic, + domainRect); + } +}; + +using DomainMode = GrTextureProducer_TestAccess::DomainMode; + +#ifdef SK_DEBUG +static bool is_irect(const SkRect& r) { + return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) && + SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom); +} +#endif + +static SkIRect to_irect(const SkRect& r) { + SkASSERT(is_irect(r)); + return SkIRect::MakeLTRB(SkScalarRoundToInt(r.fLeft), + SkScalarRoundToInt(r.fTop), + SkScalarRoundToInt(r.fRight), + SkScalarRoundToInt(r.fBottom)); +} + + +class RectInfo { +public: + enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 }; + + enum EdgeType { + kSoft = 0, // there is data on the other side of this edge that we are allowed to sample + kHard = 1, // the backing resource ends at this edge + kBad = 2 // we can't sample across this edge + }; + + void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot, + const char* name) { + fRect = rect; + fTypes[kLeft] = left; + fTypes[kTop] = top; + fTypes[kRight] = right; + fTypes[kBot] = bot; + fName = name; + } + + const SkRect& rect() const { return fRect; } + EdgeType edgeType(Side side) const { return fTypes[side]; } + const char* name() const { return fName; } + +#ifdef SK_DEBUG + bool isHardOrBadAllAround() const { + for (int i = 0; i < 4; ++i) { + if (kHard != fTypes[i] && kBad != fTypes[i]) { + return false; + } + } + return true; + } +#endif + + bool hasABad() const { + for (int i = 0; i < 4; ++i) { + if (kBad == fTypes[i]) { + return true; + } + } + return false; + } + +#ifdef SK_DEBUG + void print(const char* label) const { + SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n", + label, fName, + fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, + ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]), + ToStr(fTypes[kRight]), ToStr(fTypes[kBot])); + } +#endif + +private: +#ifdef SK_DEBUG + static const char* ToStr(EdgeType type) { + static const char* names[] = { "soft", "hard", "bad" }; + return names[type]; + } +#endif + + RectInfo operator=(const RectInfo& other); // disallow + + SkRect fRect; + EdgeType fTypes[4]; + const char* fName; + +}; + +static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider, + bool isPowerOfTwo, + bool isExact, + RectInfo* rect) { + int size = isPowerOfTwo ? 128 : 100; + SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox; + + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fWidth = size; + desc.fHeight = size; + + static const char* name = "proxy"; + + // Proxies are always hard on the left and top but can be bad on the right and bottom + rect->set(SkRect::MakeWH(size, size), + RectInfo::kHard, + RectInfo::kHard, + (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, + (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, + name); + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider, + desc, fit, + SkBudgeted::kYes); + return proxy; +} + +static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous, + bool isInsetHard, bool coordsAreLimitedToRect, + float insetAmount, float halfFilterWidth) { + if (isInsetHard) { + if (coordsAreLimitedToRect) { + SkASSERT(halfFilterWidth >= 0.0f); + if (0.0f == halfFilterWidth) { + return RectInfo::kSoft; + } + } + + if (0.0f == insetAmount && RectInfo::kHard == previous) { + return RectInfo::kHard; + } + + return RectInfo::kBad; + } + + if (RectInfo::kHard == previous) { + return RectInfo::kHard; + } + + if (coordsAreLimitedToRect) { + SkASSERT(halfFilterWidth >= 0.0f); + if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) { + return RectInfo::kSoft; + } + } + + return previous; +} + +static const int kInsetLeft_Flag = 0x1; +static const int kInsetTop_Flag = 0x2; +static const int kInsetRight_Flag = 0x4; +static const int kInsetBot_Flag = 0x8; + +// If 'isInsetHard' is true we can't sample across the inset boundary. +// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset. +static const SkRect* generic_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth, + uint32_t flags, + const char* name) { + SkRect newR = enclosing.rect(); + + RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft); + if (flags & kInsetLeft_Flag) { + newR.fLeft += insetAmount; + left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop); + if (flags & kInsetTop_Flag) { + newR.fTop += insetAmount; + top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight); + if (flags & kInsetRight_Flag) { + newR.fRight -= insetAmount; + right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot); + if (flags & kInsetBot_Flag) { + newR.fBottom -= insetAmount; + bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + result->set(newR, left, top, right, bot, name); + return &result->rect(); +} + +// Make a rect that only touches the enclosing rect on the left. +static const SkRect* left_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "left"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the top. +static const SkRect* top_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "top"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the right. +static const SkRect* right_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "right"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the bottom. +static const SkRect* bot_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "bot"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name); +} + +// Make a rect that is inset all around. +static const SkRect* full_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "all"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// This is only used for content rect creation. We ensure 'result' is correct but +// return null to indicate no content area (other than what the proxy specifies). +static const SkRect* null_rect(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "null"; + generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, 0, name); + return nullptr; +} + +// Make a rect with no inset. This is only used for constraint rect creation. +static const SkRect* no_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "none"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, 0, name); +} + +static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) { + GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode; + SkRect actualDomainRect; + + static const GrSamplerParams::FilterMode gModes[] = { + GrSamplerParams::kNone_FilterMode, + GrSamplerParams::kBilerp_FilterMode, + GrSamplerParams::kMipMap_FilterMode, + }; + + static const GrSamplerParams::FilterMode* gModePtrs[] = { + &gModes[0], &gModes[1], nullptr, &gModes[2] + }; + + static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f }; + + for (auto isPowerOfTwoSized : { true, false }) { + for (auto isExact : { true, false }) { + RectInfo outermost; + + sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized, + isExact, &outermost); + SkASSERT(outermost.isHardOrBadAllAround()); + + for (auto contentRectMaker : { left_only, top_only, right_only, + bot_only, full_inset, null_rect}) { + RectInfo contentRectStorage; + const SkRect* contentRect = (*contentRectMaker)(outermost, + &contentRectStorage, + true, false, 5.0f, -1.0f); + if (contentRect) { + // We only have content rects if they actually reduce the extent of the content + SkASSERT(!contentRect->contains(outermost.rect())); + SkASSERT(outermost.rect().contains(*contentRect)); + SkASSERT(is_irect(*contentRect)); + } + SkASSERT(contentRectStorage.isHardOrBadAllAround()); + + for (auto isConstraintRectHard : { true, false }) { + for (auto areCoordsLimitedToConstraintRect : { true, false }) { + for (int filterMode = 0; filterMode < 4; ++filterMode) { + for (auto constraintRectMaker : { left_only, top_only, right_only, + bot_only, full_inset, no_inset }) { + for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) { + RectInfo constraintRectStorage; + const SkRect* constraintRect = (*constraintRectMaker)( + contentRect ? contentRectStorage : outermost, + &constraintRectStorage, + isConstraintRectHard, + areCoordsLimitedToConstraintRect, + insetAmt, + gHalfFilterWidth[filterMode]); + SkASSERT(constraintRect); // always need one of these + if (contentRect) { + SkASSERT(contentRect->contains(*constraintRect)); + } else { + SkASSERT(outermost.rect().contains(*constraintRect)); + } + + SkIRect contentIRect; + if (contentRect) { + contentIRect = to_irect(*contentRect); + } + + actualMode = GrTextureProducer_TestAccess::DetermineDomainMode( + *constraintRect, + isConstraintRectHard + ? GrTextureProducer::kYes_FilterConstraint + : GrTextureProducer::kNo_FilterConstraint, + areCoordsLimitedToConstraintRect, + proxy.get(), + contentRect ? &contentIRect : nullptr, + gModePtrs[filterMode], + &actualDomainRect); + + expectedMode = DomainMode::kNoDomain_DomainMode; + if (constraintRectStorage.hasABad()) { + if (3 == filterMode) { + expectedMode = DomainMode::kTightCopy_DomainMode; + } else { + expectedMode = DomainMode::kDomain_DomainMode; + } + } + + REPORTER_ASSERT(reporter, expectedMode == actualMode); + // TODO: add a check that the returned domain rect is correct + } + } + } + } + } + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + proxy_test(reporter, context->resourceProvider()); +} + +#endif |