skia2/bench/SkBlend_optsBench.cpp
Brian Osman 61624f0c71 Plumb dst color space in many places, rather than "mode"
This is less to type in most cases, and gives us more information
(for things like picture-backed images, where we need to know all
about the destination surface).

Additionally, strip out the plumbing entirely for bitmap sources,
where we don't need to know anything.

BUG=skia:

Change-Id: I4deff6c7c345fcf62eb08b2aff0560adae4313da
Reviewed-on: https://skia-review.googlesource.com/5748
Reviewed-by: Mike Klein <mtklein@chromium.org>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2016-12-09 20:31:23 +00:00

203 lines
6.1 KiB
C++

/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <tuple>
#include "Benchmark.h"
#include "Resources.h"
#include "SkCpu.h"
#include "SkImage.h"
#include "SkImage_Base.h"
#include "SkNx.h"
#include "SkOpts.h"
#include "SkPM4fPriv.h"
#include "SkString.h"
#define INNER_LOOPS 10
static inline void brute_srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
auto d = Sk4f_fromS32(*dst),
s = Sk4f_fromS32( src);
*dst = Sk4f_toS32(s + d * (1.0f - s[3]));
}
static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
if (src >= 0xFF000000) {
*dst = src;
return;
}
brute_srcover_srgb_srgb_1(dst, src);
}
static void brute_force_srcover_srgb_srgb(
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
while (ndst > 0) {
int n = SkTMin(ndst, nsrc);
for (int i = 0; i < n; i++) {
brute_srcover_srgb_srgb_1(dst++, src[i]);
}
ndst -= n;
}
}
static void trivial_srcover_srgb_srgb(
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
while (ndst > 0) {
int n = SkTMin(ndst, nsrc);
for (int i = 0; i < n; i++) {
srcover_srgb_srgb_1(dst++, src[i]);
}
ndst -= n;
}
}
static void best_non_simd_srcover_srgb_srgb(
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
uint64_t* ddst = reinterpret_cast<uint64_t*>(dst);
auto srcover_srgb_srgb_2 = [](uint32_t* dst, const uint32_t* src) {
srcover_srgb_srgb_1(dst++, *src++);
srcover_srgb_srgb_1(dst, *src);
};
while (ndst >0) {
int count = SkTMin(ndst, nsrc);
ndst -= count;
const uint64_t* dsrc = reinterpret_cast<const uint64_t*>(src);
const uint64_t* end = dsrc + (count >> 1);
do {
if ((~*dsrc & 0xFF000000FF000000) == 0) {
do {
*ddst++ = *dsrc++;
} while (dsrc < end && (~*dsrc & 0xFF000000FF000000) == 0);
} else if ((*dsrc & 0xFF000000FF000000) == 0) {
do {
dsrc++;
ddst++;
} while (dsrc < end && (*dsrc & 0xFF000000FF000000) == 0);
} else {
srcover_srgb_srgb_2(reinterpret_cast<uint32_t*>(ddst++),
reinterpret_cast<const uint32_t*>(dsrc++));
}
} while (dsrc < end);
if ((count & 1) != 0) {
uint32_t s1;
memcpy(&s1, dsrc, 4);
srcover_srgb_srgb_1(reinterpret_cast<uint32_t*>(ddst), s1);
}
}
}
class SrcOverVSkOptsBruteForce {
public:
static SkString Name() { return SkString{"VSkOptsBruteForce"}; }
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
brute_force_srcover_srgb_srgb(dst, src, count, count);
}
};
class SrcOverVSkOptsTrivial {
public:
static SkString Name() { return SkString{"VSkOptsTrivial"}; }
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
trivial_srcover_srgb_srgb(dst, src, count, count);
}
};
class SrcOverVSkOptsNonSimdCore {
public:
static SkString Name() { return SkString{"VSkOptsNonSimdCore"}; }
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
best_non_simd_srcover_srgb_srgb(dst, src, count, count);
}
};
class SrcOverVSkOptsDefault {
public:
static SkString Name() { return SkString{"VSkOptsDefault"}; }
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
SkOpts::srcover_srgb_srgb(dst, src, count, count);
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Blender>
class LinearSrcOverBench : public Benchmark {
public:
LinearSrcOverBench(const char* fileName) : fFileName(fileName) {
fName = "LinearSrcOver_";
fName.append(fileName);
fName.append(Blender::Name());
}
protected:
bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
const char* onGetName() override { return fName.c_str(); }
void onPreDraw(SkCanvas*) override {
if (!fPixmap.addr()) {
sk_sp<SkImage> image = GetResourceAsImage(fFileName.c_str());
SkBitmap bm;
SkColorSpace* legacyColorSpace = nullptr;
if (!as_IB(image)->getROPixels(&bm, legacyColorSpace)) {
SkFAIL("Could not read resource");
}
bm.peekPixels(&fPixmap);
fCount = fPixmap.rowBytesAsPixels();
fDst.reset(fCount);
sk_bzero(fDst.get(), fPixmap.rowBytes());
}
}
void onDraw(int loops, SkCanvas*) override {
SkASSERT(fPixmap.colorType() == kN32_SkColorType);
const int width = fPixmap.rowBytesAsPixels();
for (int i = 0; i < loops * INNER_LOOPS; ++i) {
const uint32_t* src = fPixmap.addr32();
for (int y = 0; y < fPixmap.height(); y++) {
Blender::BlendN(fDst.get(), src, width);
src += width;
}
}
}
void onPostDraw(SkCanvas*) override {
// Make sure the compiler does not optimize away the operation.
volatile uint32_t v = 0;
for (int i = 0; i < fCount; i++) {
v ^= fDst[i];
}
}
private:
int fCount;
SkAutoTArray<uint32_t> fDst;
SkString fFileName;
SkString fName;
SkPixmap fPixmap;
typedef Benchmark INHERITED;
};
#define BENCHES(fileName) \
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsBruteForce>(fileName); ) \
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsTrivial>(fileName); ) \
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsNonSimdCore>(fileName); ) \
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsDefault>(fileName); )
BENCHES("yellow_rose.png")
BENCHES("baby_tux.png")
BENCHES("plane.png")
BENCHES("mandrill_512.png")
BENCHES("iconstrip.png")