new ideas for bilerp/bicubic?

Maybe long term best way to go is just bilerp and bicubic stages that do
everything internally, even maybe creating source x and y from dx,dy and
a matrix?

We'll end up with two copies of gather code, but that's not a big deal
for anything but code size... it's easy to factor out in C++.  I think
all the branches on uniforms like SkColorType and SkTileMode should be
more than made up for by the fused stages.

The clamp/clamp/8888 stages are still a little faster, though it's maybe
possible to share code with them.

Guard lowp bilinear stage like lowp bilerp_clamp_8888,
for same reason... layout test rebaselines.

Bug: skia:9323
Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
Change-Id: I2cd4176e4e1892eb6559064a09fb028f616a91d3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/234379
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Mike Klein 2019-08-13 12:22:17 -04:00 committed by Skia Commit-Bot
parent feacb0fb34
commit 010056244d
3 changed files with 192 additions and 2 deletions

View File

@ -10,6 +10,7 @@
#include "include/core/SkColor.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypes.h"
#include "include/private/SkNx.h"
#include "include/private/SkTArray.h"
@ -78,6 +79,7 @@
M(decal_x) M(decal_y) M(decal_x_and_y) \
M(check_decal_mask) \
M(negate_x) \
M(bilinear) M(bicubic) \
M(bilinear_nx) M(bilinear_px) M(bilinear_ny) M(bilinear_py) \
M(bicubic_n3x) M(bicubic_n1x) M(bicubic_p1x) M(bicubic_p3x) \
M(bicubic_n3y) M(bicubic_n1y) M(bicubic_p1y) M(bicubic_p3y) \
@ -141,6 +143,12 @@ struct SkRasterPipeline_DecalTileCtx {
float limit_y;
};
struct SkRasterPipeline_SamplerCtx2 : public SkRasterPipeline_GatherCtx {
SkColorType ct;
SkTileMode tileX, tileY;
float invWidth, invHeight;
};
struct SkRasterPipeline_CallbackCtx {
void (*fn)(SkRasterPipeline_CallbackCtx* self, int active_pixels/*<= SkRasterPipeline_kMaxStride*/);
@ -195,8 +203,6 @@ struct SkRasterPipeline_EmbossCtx {
add;
};
class SkRasterPipeline {
public:
explicit SkRasterPipeline(SkArenaAlloc*);

View File

@ -2609,6 +2609,80 @@ STAGE(gauss_a_to_rgba, Ctx::None) {
b = a;
}
SI F tile(F v, SkTileMode mode, float limit, float invLimit) {
// The ix_and_ptr() calls in sample() will clamp tile()'s output, so no need to clamp here.
switch (mode) {
case SkTileMode::kDecal: // TODO, for now fallthrough to clamp
case SkTileMode::kClamp: return v;
case SkTileMode::kRepeat: return v - floor_(v*invLimit)*limit;
case SkTileMode::kMirror:
return abs_( (v-limit) - (limit+limit)*floor_((v-limit)*(invLimit*0.5f)) - limit );
}
SkUNREACHABLE;
}
SI void sample(const SkRasterPipeline_SamplerCtx2* ctx, F x, F y,
F* r, F* g, F* b, F* a) {
x = tile(x, ctx->tileX, ctx->width , ctx->invWidth );
y = tile(y, ctx->tileY, ctx->height, ctx->invHeight);
switch (ctx->ct) {
default: *r = *g = *b = *a = 0; // TODO
break;
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType: {
const uint32_t* ptr;
U32 ix = ix_and_ptr(&ptr, ctx, x,y);
from_8888(gather(ptr, ix), r,g,b,a);
if (ctx->ct == kBGRA_8888_SkColorType) {
std::swap(*r,*b);
}
} break;
}
}
template <int D>
SI void sampler(const SkRasterPipeline_SamplerCtx2* ctx,
F cx, F cy, const F (&wx)[D], const F (&wy)[D],
F* r, F* g, F* b, F* a) {
float start = -0.5f*(D-1);
*r = *g = *b = *a = 0;
F y = cy + start;
for (int j = 0; j < D; j++, y += 1.0f) {
F x = cx + start;
for (int i = 0; i < D; i++, x += 1.0f) {
F R,G,B,A;
sample(ctx, x,y, &R,&G,&B,&A);
F w = wx[i] * wy[j];
*r = mad(w,R,*r);
*g = mad(w,G,*g);
*b = mad(w,B,*b);
*a = mad(w,A,*a);
}
}
}
STAGE(bilinear, const SkRasterPipeline_SamplerCtx2* ctx) {
F x = r, fx = fract(x + 0.5f),
y = g, fy = fract(y + 0.5f);
const F wx[] = {1.0f - fx, fx};
const F wy[] = {1.0f - fy, fy};
sampler(ctx, x,y, wx,wy, &r,&g,&b,&a);
}
STAGE(bicubic, SkRasterPipeline_SamplerCtx2* ctx) {
F x = r, fx = fract(x + 0.5f),
y = g, fy = fract(y + 0.5f);
const F wx[] = { bicubic_far(1-fx), bicubic_near(1-fx), bicubic_near(fx), bicubic_far(fx) };
const F wy[] = { bicubic_far(1-fy), bicubic_near(1-fy), bicubic_near(fy), bicubic_far(fy) };
sampler(ctx, x,y, wx,wy, &r,&g,&b,&a);
}
// A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling.
STAGE(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) {
// (cx,cy) are the center of our sample.
@ -3830,6 +3904,7 @@ STAGE_PP(srcover_rgba_8888, const SkRasterPipeline_MemoryCtx* ctx) {
#if defined(SK_DISABLE_LOWP_BILERP_CLAMP_CLAMP_STAGE)
static void(*bilerp_clamp_8888)(void) = nullptr;
static void(*bilinear)(void) = nullptr;
#else
STAGE_GP(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) {
// (cx,cy) are the center of our sample.
@ -3893,6 +3968,82 @@ STAGE_GP(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) {
b = (b + bias/2) / bias;
a = (a + bias/2) / bias;
}
// TODO: lowp::tile() is identical to the highp tile()... share?
SI F tile(F v, SkTileMode mode, float limit, float invLimit) {
// After ix_and_ptr() will clamp the output of tile(), so we need not clamp here.
switch (mode) {
case SkTileMode::kDecal: // TODO, for now fallthrough to clamp
case SkTileMode::kClamp: return v;
case SkTileMode::kRepeat: return v - floor_(v*invLimit)*limit;
case SkTileMode::kMirror:
return abs_( (v-limit) - (limit+limit)*floor_((v-limit)*(invLimit*0.5f)) - limit );
}
SkUNREACHABLE;
}
SI void sample(const SkRasterPipeline_SamplerCtx2* ctx, F x, F y,
U16* r, U16* g, U16* b, U16* a) {
x = tile(x, ctx->tileX, ctx->width , ctx->invWidth );
y = tile(y, ctx->tileY, ctx->height, ctx->invHeight);
switch (ctx->ct) {
default: *r = *g = *b = *a = 0; // TODO
break;
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType: {
const uint32_t* ptr;
U32 ix = ix_and_ptr(&ptr, ctx, x,y);
from_8888(gather<U32>(ptr, ix), r,g,b,a);
if (ctx->ct == kBGRA_8888_SkColorType) {
std::swap(*r,*b);
}
} break;
}
}
template <int D>
SI void sampler(const SkRasterPipeline_SamplerCtx2* ctx,
F cx, F cy, const F (&wx)[D], const F (&wy)[D],
U16* r, U16* g, U16* b, U16* a) {
float start = -0.5f*(D-1);
const uint16_t bias = 256;
U16 remaining = bias;
*r = *g = *b = *a = 0;
F y = cy + start;
for (int j = 0; j < D; j++, y += 1.0f) {
F x = cx + start;
for (int i = 0; i < D; i++, x += 1.0f) {
U16 R,G,B,A;
sample(ctx, x,y, &R,&G,&B,&A);
U16 w = (i == D-1 && j == D-1) ? remaining
: cast<U16>(wx[i]*wy[j]*bias);
remaining -= w;
*r += w*R;
*g += w*G;
*b += w*B;
*a += w*A;
}
}
*r = (*r + bias/2) / bias;
*g = (*g + bias/2) / bias;
*b = (*b + bias/2) / bias;
*a = (*a + bias/2) / bias;
}
STAGE_GP(bilinear, const SkRasterPipeline_SamplerCtx2* ctx) {
F fx = fract(x + 0.5f),
fy = fract(y + 0.5f);
const F wx[] = {1.0f - fx, fx};
const F wy[] = {1.0f - fy, fy};
sampler(ctx, x,y, wx,wy, &r,&g,&b,&a);
}
#endif
// ~~~~~~ GrSwizzle stage ~~~~~~ //
@ -3974,6 +4125,7 @@ STAGE_PP(swizzle, void* ctx) {
NOT_IMPLEMENTED(mirror_y) // TODO
NOT_IMPLEMENTED(repeat_y) // TODO
NOT_IMPLEMENTED(negate_x)
NOT_IMPLEMENTED(bicubic) // TODO if I can figure out negative weights
NOT_IMPLEMENTED(bicubic_clamp_8888)
NOT_IMPLEMENTED(bilinear_nx) // TODO
NOT_IMPLEMENTED(bilinear_ny) // TODO

View File

@ -452,6 +452,22 @@ bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
}
return append_misc();
}
if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
&& quality == kLow_SkFilterQuality
&& fTileModeX != SkTileMode::kDecal // TODO decal too?
&& fTileModeY != SkTileMode::kDecal) {
auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
*(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
ctx->ct = ct;
ctx->tileX = fTileModeX;
ctx->tileY = fTileModeY;
ctx->invWidth = 1.0f / ctx->width;
ctx->invHeight = 1.0f / ctx->height;
p->append(SkRasterPipeline::bilinear, ctx);
return append_misc();
}
if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
&& quality == kHigh_SkFilterQuality
@ -463,6 +479,22 @@ bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
}
return append_misc();
}
if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) // TODO: all formats
&& quality == kHigh_SkFilterQuality
&& fTileModeX != SkTileMode::kDecal // TODO decal too?
&& fTileModeY != SkTileMode::kDecal) {
auto ctx = alloc->make<SkRasterPipeline_SamplerCtx2>();
*(SkRasterPipeline_GatherCtx*)(ctx) = *gather;
ctx->ct = ct;
ctx->tileX = fTileModeX;
ctx->tileY = fTileModeY;
ctx->invWidth = 1.0f / ctx->width;
ctx->invHeight = 1.0f / ctx->height;
p->append(SkRasterPipeline::bicubic, ctx);
return append_misc();
}
SkRasterPipeline_SamplerCtx* sampler = nullptr;
if (quality != kNone_SkFilterQuality) {