4-at-a-time SkPMColor -> SkPMFloat API.

Please see if this looks usable.  It may even give a perf boost if you use it, even without custom implementations for each instruction set.

I've been trying this morning to beat this naive loop implementation, but so far no luck with either _SSE2.h or _SSSE3.h.  It's possible this is an artifact of the microbenchmark, because we're not doing anything between the conversions.  I'd like to see how this fits into real code, what assembly's generated, what the hot spots are, etc.

I've updated the tests to test these new APIs, and splintered off a pair of new benchmarks that use the new APIs.  This required some minor rejiggering in the benches.

BUG=skia:

Review URL: https://codereview.chromium.org/978213003
This commit is contained in:
mtklein 2015-03-05 11:31:59 -08:00 committed by Commit bot
parent 7750c6fa99
commit 548bf38b28
3 changed files with 81 additions and 12 deletions

View File

@ -12,10 +12,21 @@ static uint32_t lcg_rand(uint32_t* seed) {
return *seed;
}
// I'm having better luck getting these to constant-propagate away as template parameters.
template <bool kClamp, bool kWide>
struct PMFloatBench : public Benchmark {
explicit PMFloatBench(bool clamp) : fClamp(clamp) {}
PMFloatBench() {}
const char* onGetName() SK_OVERRIDE { return fClamp ? "SkPMFloat_clamp" : "SkPMFloat_get"; }
const char* onGetName() SK_OVERRIDE {
switch (kClamp << 1 | kWide) {
case 0: return "SkPMFloat_get_1x";
case 1: return "SkPMFloat_get_4x";
case 2: return "SkPMFloat_clamp_1x";
case 3: return "SkPMFloat_clamp_4x";
}
SkFAIL("unreachable");
return "oh bother";
}
bool isSuitableFor(Backend backend) SK_OVERRIDE { return backend == kNonRendering_Backend; }
void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
@ -23,21 +34,47 @@ struct PMFloatBench : public Benchmark {
uint32_t junk = 0;
uint32_t seed = 0;
for (int i = 0; i < loops; i++) {
SkPMColor colors[4];
#ifdef SK_DEBUG
// Our SkASSERTs will remind us that it's technically required that we premultiply.
SkPMColor c = SkPreMultiplyColor(lcg_rand(&seed));
for (int i = 0; i < 4; i++) {
// Our SkASSERTs will remind us that it's technically required that we premultiply.
colors[i] = SkPreMultiplyColor(lcg_rand(&seed));
}
#else
// But it's a lot faster not to, and this code won't really mind the non-PM colors.
SkPMColor c = lcg_rand(&seed);
(void)lcg_rand(&seed);
colors[0] = seed + 0;
colors[1] = seed + 1;
colors[2] = seed + 2;
colors[3] = seed + 3;
#endif
SkPMFloat pmf = SkPMFloat::FromPMColor(c);
SkPMColor back = fClamp ? pmf.clamped() : pmf.get();
junk ^= back;
SkPMFloat floats[4];
if (kWide) {
SkPMFloat::From4PMColors(floats, colors);
} else {
for (int i = 0; i < 4; i++) {
floats[i] = SkPMFloat::FromPMColor(colors[i]);
}
}
SkPMColor back[4];
switch (kClamp << 1 | kWide) {
case 0: for (int i = 0; i < 4; i++) { back[i] = floats[i].get(); } break;
case 1: SkPMFloat::To4PMColors(back, floats); break;
case 2: for (int i = 0; i < 4; i++) { back[i] = floats[i].clamped(); } break;
case 3: SkPMFloat::ClampTo4PMColors(back, floats); break;
}
for (int i = 0; i < 4; i++) {
junk ^= back[i];
}
}
blackhole ^= junk;
}
bool fClamp;
};
DEF_BENCH(return new PMFloatBench( true);)
DEF_BENCH(return new PMFloatBench(false);)
// Extra () help DEF_BENCH not get confused by the comma inside the <>.
DEF_BENCH(return (new PMFloatBench< true, true>);)
DEF_BENCH(return (new PMFloatBench<false, true>);)
DEF_BENCH(return (new PMFloatBench< true, false>);)
DEF_BENCH(return (new PMFloatBench<false, false>);)

View File

@ -12,6 +12,12 @@ public:
static SkPMFloat FromPMColor(SkPMColor c) { return SkPMFloat(c); }
static SkPMFloat FromARGB(float a, float r, float g, float b) { return SkPMFloat(a,r,g,b); }
// May be more efficient than one at a time. No special alignment assumed for SkPMColors.
static void From4PMColors(SkPMFloat floats[4], const SkPMColor colors[4]) {
// TODO: specialize
for (int i = 0; i < 4; i++) { floats[i] = FromPMColor(colors[i]); }
}
explicit SkPMFloat(SkPMColor);
SkPMFloat(float a, float r, float g, float b) {
// TODO: faster when specialized?
@ -44,6 +50,16 @@ public:
SkPMColor get() const; // May SkASSERT(this->isValid()). Some implementations may clamp.
SkPMColor clamped() const; // Will clamp all values to [0, 255]. Then may assert isValid().
// 4-at-a-time versions of get() and clamped(). Like From4PMColors(), no alignment assumed.
static void To4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
// TODO: specialize
for (int i = 0; i < 4; i++) { colors[i] = floats[i].get(); }
}
static void ClampTo4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
// TODO: specialize
for (int i = 0; i < 4; i++) { colors[i] = floats[i].clamped(); }
}
bool isValid() const {
return this->a() >= 0 && this->a() <= 255
&& this->r() >= 0 && this->r() <= this->a()

View File

@ -29,4 +29,20 @@ DEF_TEST(SkPMFloat, r) {
REPORTER_ASSERT(r, SkScalarNearlyEqual(38.25f, scaled.r()));
REPORTER_ASSERT(r, SkScalarNearlyEqual( 0.25f, scaled.g()));
REPORTER_ASSERT(r, SkScalarNearlyEqual( 0.00f, scaled.b()));
// Test 4-at-a-time conversions.
SkPMColor colors[4] = { 0xFF000000, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF };
SkPMFloat floats[4];
SkPMFloat::From4PMColors(floats, colors);
SkPMColor back[4];
SkPMFloat::To4PMColors(back, floats);
for (int i = 0; i < 4; i++) {
REPORTER_ASSERT(r, back[i] == colors[i]);
}
SkPMFloat::ClampTo4PMColors(back, floats);
for (int i = 0; i < 4; i++) {
REPORTER_ASSERT(r, back[i] == colors[i]);
}
}