skia2/tests/DetermineDomainModeTest.cpp
Brian Salomon 2a4f983c94 Remove GrSurfaceOrigin from GrSurfaceDesc.
This field has no interpretation at the GrTexture/GrGpu as the orientation is
handled at the GrSurfaceProxy level.

This change requires GrGpu to accept a GrSurfaceOrigin when creating a texture with initial data. The origin refers to the texel data to be uploaded. Longer term the plan is to remove this and require the data to be kTopLeft. Additionally, kBottomLeft will only be allowed for wrapped texture/RTs as this evolves.

Change-Id: I7d25b0199aafd9bf3b74c39b2cae451acadcd772
Reviewed-on: https://skia-review.googlesource.com/111806
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
2018-03-05 18:50:25 +00:00

386 lines
16 KiB
C++

/*
* 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 "GrContextPriv.h"
#include "GrProxyProvider.h"
#include "GrSurfaceProxy.h"
#include "GrTextureProducer.h"
#include "GrTextureProxy.h"
// 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(GrProxyProvider* proxyProvider,
bool isPowerOfTwo,
bool isExact,
RectInfo* rect) {
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;
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(desc, kTopLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes);
}
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, GrProxyProvider* proxyProvider) {
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(proxyProvider, 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->contextPriv().proxyProvider());
}
#endif