c7ad40f76f
Most of this is (obviously) not necessary to do, but once I started, I figured I'd just get it all. Tools (nanobench, DM, skiaserve), all GMs, benches, and unit tests, plus support code (command line parsing and config stuff). This is almost entirely mechanical. Bug: skia: Change-Id: I209500f8df8c5bd43f8298ff26440d1c4d7425fb Reviewed-on: https://skia-review.googlesource.com/131153 Reviewed-by: Mike Klein <mtklein@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
391 lines
16 KiB
C++
391 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 "SkTypes.h"
|
|
|
|
#include "GrContext.h"
|
|
#include "GrContextFactory.h"
|
|
#include "GrContextPriv.h"
|
|
#include "GrProxyProvider.h"
|
|
#include "GrSamplerState.h"
|
|
#include "GrTextureProducer.h"
|
|
#include "GrTextureProxy.h"
|
|
#include "GrTypes.h"
|
|
#include "GrTypesPriv.h"
|
|
#include "SkRect.h"
|
|
#include "SkRefCnt.h"
|
|
#include "Test.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(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());
|
|
}
|