8bbcd5aab8
We used to build src and dst transfer fn tables every time a new xform was created with linear src and dst. Now we don't compute them because we don't need them. This will make SkColorSpaceXform a far better option for any xforms with float or half-float inputs or outputs, particularly on a small number of pixels. This CL also moves SkColorSpaceXform closer to what I anticipate will be the eventual 'API design'. I think apply() will want to take a SrcColorType enum (not created yet because it's not necessary yet) and a DstColorType enum (still using SkColorType because there's not yet a reason not to). Performance changes: toSRGB 341us -> 366us to2Dot2 404us -> 403us toF16 318us -> 304us There's no reason for toSRGB or to2Dot2 to change. The refactor seems to have caused the compiler to order the instructions a little differently... This is something to come back to if we need to squeeze more performance out of sRGB. For now, let's not be held up by something we don't control. F16 likely improves because we are no longer (unnecessarily) building the linear tables. Code size gets a little bigger. Measuring SkColorSpaceXform size as a percentage of src/ size, we go from 0.8% to 1.4%. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2335723002 Review-Url: https://codereview.chromium.org/2335723002
214 lines
6.5 KiB
C++
214 lines
6.5 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 "ColorCodecBench.h"
|
|
#include "Resources.h"
|
|
#include "SkCodec.h"
|
|
#include "SkColorSpaceXform.h"
|
|
#include "SkCommandLineFlags.h"
|
|
|
|
#if defined(SK_TEST_QCMS)
|
|
DEFINE_bool(qcms, false, "Bench qcms color conversion");
|
|
#endif
|
|
DEFINE_bool(xform_only, false, "Only time the color xform, do not include the decode time");
|
|
DEFINE_bool(srgb, false, "Convert to srgb dst space");
|
|
DEFINE_bool(half, false, "Convert to half floats");
|
|
|
|
ColorCodecBench::ColorCodecBench(const char* name, sk_sp<SkData> encoded)
|
|
: fEncoded(std::move(encoded))
|
|
#if defined(SK_TEST_QCMS)
|
|
, fDstSpaceQCMS(nullptr)
|
|
#endif
|
|
{
|
|
fName.appendf("Color%s", FLAGS_xform_only ? "Xform" : "Codec");
|
|
#if defined(SK_TEST_QCMS)
|
|
fName.appendf("%s", FLAGS_qcms ? "QCMS" : "");
|
|
#endif
|
|
fName.appendf("_%s", name);
|
|
}
|
|
|
|
const char* ColorCodecBench::onGetName() {
|
|
return fName.c_str();
|
|
}
|
|
|
|
bool ColorCodecBench::isSuitableFor(Backend backend) {
|
|
return kNonRendering_Backend == backend;
|
|
}
|
|
|
|
void ColorCodecBench::decodeAndXform() {
|
|
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fEncoded));
|
|
SkASSERT(codec);
|
|
|
|
#ifdef SK_DEBUG
|
|
SkCodec::Result result =
|
|
#endif
|
|
codec->getPixels(fDstInfo, fDst.get(), fDstInfo.minRowBytes());
|
|
SkASSERT(SkCodec::kSuccess == result);
|
|
}
|
|
|
|
#if defined(SK_TEST_QCMS)
|
|
void ColorCodecBench::decodeAndXformQCMS() {
|
|
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fEncoded));
|
|
#ifdef SK_DEBUG
|
|
const SkCodec::Result result =
|
|
#endif
|
|
codec->startScanlineDecode(fSrcInfo);
|
|
SkASSERT(SkCodec::kSuccess == result);
|
|
|
|
SkAutoTCallVProc<qcms_profile, qcms_profile_release>
|
|
srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size()));
|
|
SkASSERT(srcSpace);
|
|
|
|
SkAutoTCallVProc<qcms_transform, qcms_transform_release>
|
|
transform (qcms_transform_create(srcSpace, QCMS_DATA_RGBA_8, fDstSpaceQCMS.get(),
|
|
QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL));
|
|
SkASSERT(transform);
|
|
|
|
#ifdef SK_PMCOLOR_IS_RGBA
|
|
qcms_output_type outType = QCMS_OUTPUT_RGBX;
|
|
#else
|
|
qcms_output_type outType = QCMS_OUTPUT_BGRX;
|
|
#endif
|
|
|
|
void* dst = fDst.get();
|
|
for (int y = 0; y < fSrcInfo.height(); y++) {
|
|
#ifdef SK_DEBUG
|
|
const int rows =
|
|
#endif
|
|
codec->getScanlines(fSrc.get(), 1, 0);
|
|
SkASSERT(1 == rows);
|
|
|
|
qcms_transform_data_type(transform, fSrc.get(), dst, fSrcInfo.width(), outType);
|
|
dst = SkTAddOffset<void>(dst, fDstInfo.minRowBytes());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ColorCodecBench::xformOnly() {
|
|
sk_sp<SkColorSpace> srcSpace = SkColorSpace::NewICC(fSrcData->data(), fSrcData->size());
|
|
if (!srcSpace) {
|
|
srcSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
|
|
}
|
|
std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(srcSpace, fDstSpace);
|
|
SkASSERT(xform);
|
|
|
|
void* dst = fDst.get();
|
|
void* src = fSrc.get();
|
|
for (int y = 0; y < fSrcInfo.height(); y++) {
|
|
xform->apply(dst, (uint32_t*) src, fSrcInfo.width(), fDstInfo.colorType(),
|
|
fDstInfo.alphaType());
|
|
dst = SkTAddOffset<void>(dst, fDstInfo.minRowBytes());
|
|
src = SkTAddOffset<void>(src, fSrcInfo.minRowBytes());
|
|
}
|
|
}
|
|
|
|
#if defined(SK_TEST_QCMS)
|
|
void ColorCodecBench::xformOnlyQCMS() {
|
|
SkAutoTCallVProc<qcms_profile, qcms_profile_release>
|
|
srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size()));
|
|
SkASSERT(srcSpace);
|
|
|
|
SkAutoTCallVProc<qcms_transform, qcms_transform_release>
|
|
transform (qcms_transform_create(srcSpace, QCMS_DATA_RGBA_8, fDstSpaceQCMS.get(),
|
|
QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL));
|
|
SkASSERT(transform);
|
|
|
|
#ifdef SK_PMCOLOR_IS_RGBA
|
|
qcms_output_type outType = QCMS_OUTPUT_RGBX;
|
|
#else
|
|
qcms_output_type outType = QCMS_OUTPUT_BGRX;
|
|
#endif
|
|
|
|
void* dst = fDst.get();
|
|
void* src = fSrc.get();
|
|
for (int y = 0; y < fSrcInfo.height(); y++) {
|
|
// Transform in place
|
|
qcms_transform_data_type(transform, src, dst, fSrcInfo.width(), outType);
|
|
dst = SkTAddOffset<void>(dst, fDstInfo.minRowBytes());
|
|
src = SkTAddOffset<void>(src, fSrcInfo.minRowBytes());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ColorCodecBench::onDelayedSetup() {
|
|
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fEncoded));
|
|
fSrcData = codec->getICCData();
|
|
sk_sp<SkData> dstData = SkData::MakeFromFileName(
|
|
GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
|
|
SkASSERT(dstData);
|
|
|
|
fDstSpace = nullptr;
|
|
#if defined(SK_TEST_QCMS)
|
|
if (FLAGS_qcms) {
|
|
fDstSpaceQCMS.reset(FLAGS_srgb ?
|
|
qcms_profile_sRGB() :
|
|
qcms_profile_from_memory(dstData->data(), dstData->size()));
|
|
SkASSERT(fDstSpaceQCMS);
|
|
|
|
// This call takes a non-trivial amount of time, but I think it's the most fair to
|
|
// treat it as overhead. It only needs to happen once.
|
|
qcms_profile_precache_output_transform(fDstSpaceQCMS);
|
|
} else
|
|
#endif
|
|
{
|
|
fDstSpace = FLAGS_srgb ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) :
|
|
SkColorSpace::NewICC(dstData->data(), dstData->size());
|
|
SkASSERT(fDstSpace);
|
|
}
|
|
|
|
fSrcInfo = codec->getInfo().makeColorType(kRGBA_8888_SkColorType);
|
|
fDstInfo = fSrcInfo;
|
|
|
|
if (FLAGS_half) {
|
|
fDstInfo = fDstInfo.makeColorType(kRGBA_F16_SkColorType);
|
|
fDstSpace = fDstSpace->makeLinearGamma();
|
|
}
|
|
|
|
fDstInfo = fDstInfo.makeColorSpace(fDstSpace);
|
|
|
|
fDst.reset(fDstInfo.getSafeSize(fDstInfo.minRowBytes()));
|
|
|
|
if (FLAGS_xform_only) {
|
|
fSrc.reset(fSrcInfo.getSafeSize(fSrcInfo.minRowBytes()));
|
|
codec->getPixels(fSrcInfo, fSrc.get(), fSrcInfo.minRowBytes());
|
|
}
|
|
#if defined(SK_TEST_QCMS)
|
|
else if (FLAGS_qcms) {
|
|
// Set-up a row buffer to decode into before transforming to dst.
|
|
fSrc.reset(fSrcInfo.minRowBytes());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ColorCodecBench::onDraw(int n, SkCanvas*) {
|
|
#if defined(SK_TEST_QCMS)
|
|
if (FLAGS_qcms && FLAGS_half) {
|
|
SkDebugf("Error: Contradicting flags.\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
#if defined(SK_TEST_QCMS)
|
|
if (FLAGS_qcms) {
|
|
if (FLAGS_xform_only) {
|
|
this->xformOnlyQCMS();
|
|
} else {
|
|
this->decodeAndXformQCMS();
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (FLAGS_xform_only) {
|
|
this->xformOnly();
|
|
} else {
|
|
this->decodeAndXform();
|
|
}
|
|
}
|
|
}
|
|
}
|