123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- /*
- * 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 "include/core/SkImageInfo.h"
- #include "include/core/SkRect.h"
- #include "include/core/SkRefCnt.h"
- #include "include/core/SkTypes.h"
- #include "include/gpu/GrBackendSurface.h"
- #include "include/gpu/GrContext.h"
- #include "include/gpu/GrSamplerState.h"
- #include "include/gpu/GrTypes.h"
- #include "include/private/GrTypesPriv.h"
- #include "src/gpu/GrCaps.h"
- #include "src/gpu/GrContextPriv.h"
- #include "src/gpu/GrProxyProvider.h"
- #include "src/gpu/GrTextureProducer.h"
- #include "src/gpu/GrTextureProxy.h"
- #include "tests/Test.h"
- #include "tools/gpu/GrContextFactory.h"
- #include <initializer_list>
- // For DetermineDomainMode (in the MDB world) we have 3 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 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 GrSamplerState::Filter* filterModeOrNullForBicubic,
- SkRect* domainRect) {
- return GrTextureProducer::DetermineDomainMode(constraintRect,
- filterConstraint,
- coordsLimitedToConstraintRect,
- proxy,
- filterModeOrNullForBicubic,
- domainRect);
- }
- };
- using DomainMode = GrTextureProducer_TestAccess::DomainMode;
- 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(GrContext* ctx,
- bool isPowerOfTwo,
- bool isExact,
- RectInfo* rect) {
- GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
- int size = isPowerOfTwo ? 128 : 100;
- SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
- GrSurfaceDesc desc;
- desc.fWidth = size;
- desc.fHeight = size;
- desc.fConfig = kRGBA_8888_GrPixelConfig;
- GrBackendFormat format =
- ctx->priv().caps()->getBackendFormatFromColorType(GrColorType::kRGBA_8888);
- 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);
- return proxyProvider->createProxy(format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin,
- fit, SkBudgeted::kYes, GrProtected::kNo);
- }
- 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);
- }
- // 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, GrContext* context) {
- GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
- SkRect actualDomainRect;
- static const GrSamplerState::Filter gModes[] = {
- GrSamplerState::Filter::kNearest,
- GrSamplerState::Filter::kBilerp,
- GrSamplerState::Filter::kMipMap,
- };
- static const GrSamplerState::Filter* 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(context, isPowerOfTwoSized,
- isExact, &outermost);
- SkASSERT(outermost.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)(
- outermost,
- &constraintRectStorage,
- isConstraintRectHard,
- areCoordsLimitedToConstraintRect,
- insetAmt,
- gHalfFilterWidth[filterMode]);
- SkASSERT(constraintRect); // always need one of these
- SkASSERT(outermost.rect().contains(*constraintRect));
- actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
- *constraintRect,
- isConstraintRectHard
- ? GrTextureProducer::kYes_FilterConstraint
- : GrTextureProducer::kNo_FilterConstraint,
- areCoordsLimitedToConstraintRect,
- proxy.get(),
- 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);
- }
|