centralize runtime effect caching

Relative runtimes for creating luma color filter:

    - cached SkColorFilter   -->   1x
    - cached SkRuntimeEffect -->  12x
    - no caching             --> 186x
    - this CL                -->  24x

Added insert_or_update() to SkLRUCache because
I keep falling into this trap using insert()...

Change-Id: Ic3dad32d38fc001701d8e1ad5662ad382503418f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/376776
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Mike Klein 2021-02-26 10:17:48 -06:00 committed by Skia Commit-Bot
parent 6706c9fa14
commit ef7a28e26a
6 changed files with 96 additions and 58 deletions

View File

@ -443,16 +443,13 @@ sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0
return cf1; return cf1;
} }
static SkRuntimeEffect* effect = [&]{ auto [effect,err] = SkRuntimeEffect::Make(SkString{
auto [effect,err] = SkRuntimeEffect::Make(SkString{ "uniform shader cf0;"
"uniform shader cf0;" "uniform shader cf1;"
"uniform shader cf1;" "uniform half weight;"
"uniform half weight;" "half4 main() { return mix(sample(cf0), sample(cf1), weight); }"
"half4 main() { return mix(sample(cf0), sample(cf1), weight); }" });
}); SkASSERT(effect && err.isEmpty());
SkASSERT(effect && err.isEmpty());
return effect.release();
}();
sk_sp<SkColorFilter> inputs[] = {cf0,cf1}; sk_sp<SkColorFilter> inputs[] = {cf0,cf1};
return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)), return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)),

View File

@ -56,6 +56,8 @@ public:
} }
V* insert(const K& key, V value) { V* insert(const K& key, V value) {
SkASSERT(!this->find(key));
Entry* entry = new Entry(key, std::move(value)); Entry* entry = new Entry(key, std::move(value));
fMap.set(entry); fMap.set(entry);
fLRU.addToHead(entry); fLRU.addToHead(entry);
@ -65,6 +67,15 @@ public:
return &entry->fValue; return &entry->fValue;
} }
V* insert_or_update(const K& key, V value) {
if (V* found = this->find(key)) {
*found = std::move(value);
return found;
} else {
return this->insert(key, std::move(value));
}
}
int count() { int count() {
return fMap.count(); return fMap.count();
} }

View File

@ -15,7 +15,9 @@
#include "src/core/SkColorFilterBase.h" #include "src/core/SkColorFilterBase.h"
#include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkLRUCache.h"
#include "src/core/SkMatrixProvider.h" #include "src/core/SkMatrixProvider.h"
#include "src/core/SkOpts.h"
#include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h" #include "src/core/SkReadBuffer.h"
#include "src/core/SkUtils.h" #include "src/core/SkUtils.h"
@ -125,7 +127,42 @@ static bool init_uniform_type(const SkSL::Context& ctx,
return false; return false;
} }
SK_BEGIN_REQUIRE_DENSE;
struct Key {
uint32_t skslHashA;
uint32_t skslHashB;
int inlineThreshold;
bool operator==(const Key& that) const {
return this->skslHashA == that.skslHashA
&& this->skslHashB == that.skslHashB
&& this->inlineThreshold == that.inlineThreshold;
}
Key(const SkString& sksl, const SkRuntimeEffect::Options& options)
: skslHashA(SkOpts::hash(sksl.c_str(), sksl.size(), 0))
, skslHashB(SkOpts::hash(sksl.c_str(), sksl.size(), 1))
, inlineThreshold(options.inlineThreshold) {}
};
SK_END_REQUIRE_DENSE;
static SkMutex gCacheLock;
static SkLRUCache<Key, sk_sp<SkRuntimeEffect>>* cache() {
gCacheLock.assertHeld();
static auto* cache = new SkLRUCache<Key, sk_sp<SkRuntimeEffect>>(11/*totally arbitrary*/);
return cache;
}
SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options) { SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options) {
Key key(sksl, options);
{
SkAutoMutexExclusive _(gCacheLock);
if (sk_sp<SkRuntimeEffect>* found = cache()->find(key)) {
return Result{*found, SkString()};
}
}
SkSL::SharedCompiler compiler; SkSL::SharedCompiler compiler;
SkSL::Program::Settings settings; SkSL::Program::Settings settings;
settings.fInlineThreshold = options.inlineThreshold; settings.fInlineThreshold = options.inlineThreshold;
@ -246,6 +283,10 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
std::move(varyings), std::move(varyings),
usesSampleCoords, usesSampleCoords,
allowColorFilter)); allowColorFilter));
{
SkAutoMutexExclusive _(gCacheLock);
cache()->insert_or_update(key, effect);
}
return Result{std::move(effect), SkString()}; return Result{std::move(effect), SkString()};
} }

View File

@ -563,12 +563,7 @@ namespace {
if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) { if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) {
auto cache_program = [&](skvm::Program&& program, Coverage coverage) { auto cache_program = [&](skvm::Program&& program, Coverage coverage) {
if (!program.empty()) { if (!program.empty()) {
Key key = fKey.withCoverage(coverage); cache->insert_or_update(fKey.withCoverage(coverage), std::move(program));
if (skvm::Program* found = cache->find(key)) {
*found = std::move(program);
} else {
cache->insert(key, std::move(program));
}
} }
}; };
cache_program(std::move(fBlitH), Coverage::Full); cache_program(std::move(fBlitH), Coverage::Full);

View File

@ -18,38 +18,35 @@ sk_sp<SkColorFilter> SkHighContrastFilter::Make(const SkHighContrastConfig& conf
struct Uniforms { float grayscale, invertStyle, contrast; }; struct Uniforms { float grayscale, invertStyle, contrast; };
static SkRuntimeEffect* effect = []{ SkString code{R"(
SkString code{R"( uniform shader input;
uniform shader input; uniform half grayscale, invertStyle, contrast;
uniform half grayscale, invertStyle, contrast; )"};
)"}; code += kRGB_to_HSL_sksl;
code += kRGB_to_HSL_sksl; code += kHSL_to_RGB_sksl;
code += kHSL_to_RGB_sksl; code += R"(
code += R"( half4 main() {
half4 main() { half4 c = sample(input); // linear unpremul RGBA in dst gamut.
half4 c = sample(input); // linear unpremul RGBA in dst gamut. if (grayscale == 1) {
if (grayscale == 1) { c.rgb = dot(half3(0.2126, 0.7152, 0.0722), c.rgb).rrr;
c.rgb = dot(half3(0.2126, 0.7152, 0.0722), c.rgb).rrr;
}
if (invertStyle == 1/*brightness*/) {
c.rgb = 1 - c.rgb;
} else if (invertStyle == 2/*lightness*/) {
c.rgb = rgb_to_hsl(c.rgb);
c.b = 1 - c.b;
c.rgb = hsl_to_rgb(c.rgb);
}
c.rgb = mix(half3(0.5), c.rgb, contrast);
return half4(saturate(c.rgb), c.a);
} }
)"; if (invertStyle == 1/*brightness*/) {
c.rgb = 1 - c.rgb;
auto [effect, err] = SkRuntimeEffect::Make(code); } else if (invertStyle == 2/*lightness*/) {
if (!err.isEmpty()) { c.rgb = rgb_to_hsl(c.rgb);
SkDebugf("%s\n%s\n", code.c_str(), err.c_str()); c.b = 1 - c.b;
c.rgb = hsl_to_rgb(c.rgb);
}
c.rgb = mix(half3(0.5), c.rgb, contrast);
return half4(saturate(c.rgb), c.a);
} }
SkASSERT(effect && err.isEmpty()); )";
return effect.release();
}(); auto [effect, err] = SkRuntimeEffect::Make(code);
if (!err.isEmpty()) {
SkDebugf("%s\n%s\n", code.c_str(), err.c_str());
}
SkASSERT(effect && err.isEmpty());
// A contrast setting of exactly +1 would divide by zero (1+c)/(1-c), so pull in to +1-ε. // A contrast setting of exactly +1 would divide by zero (1+c)/(1-c), so pull in to +1-ε.
// I'm not exactly sure why we've historically pinned -1 up to -1+ε, maybe just symmetry? // I'm not exactly sure why we've historically pinned -1 up to -1+ε, maybe just symmetry?

View File

@ -10,17 +10,14 @@
#include "include/effects/SkRuntimeEffect.h" #include "include/effects/SkRuntimeEffect.h"
sk_sp<SkColorFilter> SkLumaColorFilter::Make() { sk_sp<SkColorFilter> SkLumaColorFilter::Make() {
static SkColorFilter* filter = []{ const char* code =
const char* code = "uniform shader input;"
"uniform shader input;" "half4 main() {"
"half4 main() {" "return saturate(dot(half3(0.2126, 0.7152, 0.0722), sample(input).rgb)).000r;"
"return saturate(dot(half3(0.2126, 0.7152, 0.0722), sample(input).rgb)).000r;" "}";
"}"; auto [effect, err] = SkRuntimeEffect::Make(SkString{code});
auto [effect, err] = SkRuntimeEffect::Make(SkString{code}, SkRuntimeEffect::Options{}); SkASSERT(effect && err.isEmpty());
SkASSERT(effect && err.isEmpty());
sk_sp<SkColorFilter> input = nullptr; sk_sp<SkColorFilter> input = nullptr;
return effect->makeColorFilter(SkData::MakeEmpty(), &input, 1).release(); return effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
}();
return sk_ref_sp(filter);
} }