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>
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 4031d5e..606c7b9 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -443,16 +443,13 @@
return cf1;
}
- static SkRuntimeEffect* effect = [&]{
- auto [effect,err] = SkRuntimeEffect::Make(SkString{
- "uniform shader cf0;"
- "uniform shader cf1;"
- "uniform half weight;"
- "half4 main() { return mix(sample(cf0), sample(cf1), weight); }"
- });
- SkASSERT(effect && err.isEmpty());
- return effect.release();
- }();
+ auto [effect,err] = SkRuntimeEffect::Make(SkString{
+ "uniform shader cf0;"
+ "uniform shader cf1;"
+ "uniform half weight;"
+ "half4 main() { return mix(sample(cf0), sample(cf1), weight); }"
+ });
+ SkASSERT(effect && err.isEmpty());
sk_sp<SkColorFilter> inputs[] = {cf0,cf1};
return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)),
diff --git a/src/core/SkLRUCache.h b/src/core/SkLRUCache.h
index d5be4f3..50429d5 100644
--- a/src/core/SkLRUCache.h
+++ b/src/core/SkLRUCache.h
@@ -56,6 +56,8 @@
}
V* insert(const K& key, V value) {
+ SkASSERT(!this->find(key));
+
Entry* entry = new Entry(key, std::move(value));
fMap.set(entry);
fLRU.addToHead(entry);
@@ -65,6 +67,15 @@
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() {
return fMap.count();
}
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 868bf6d..f14d7df 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -15,7 +15,9 @@
#include "src/core/SkColorFilterBase.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkLRUCache.h"
#include "src/core/SkMatrixProvider.h"
+#include "src/core/SkOpts.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkUtils.h"
@@ -125,7 +127,42 @@
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) {
+ Key key(sksl, options);
+ {
+ SkAutoMutexExclusive _(gCacheLock);
+ if (sk_sp<SkRuntimeEffect>* found = cache()->find(key)) {
+ return Result{*found, SkString()};
+ }
+ }
+
SkSL::SharedCompiler compiler;
SkSL::Program::Settings settings;
settings.fInlineThreshold = options.inlineThreshold;
@@ -246,6 +283,10 @@
std::move(varyings),
usesSampleCoords,
allowColorFilter));
+ {
+ SkAutoMutexExclusive _(gCacheLock);
+ cache()->insert_or_update(key, effect);
+ }
return Result{std::move(effect), SkString()};
}
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index 2d527c4..e6eed85 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -563,12 +563,7 @@
if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) {
auto cache_program = [&](skvm::Program&& program, Coverage coverage) {
if (!program.empty()) {
- Key key = fKey.withCoverage(coverage);
- if (skvm::Program* found = cache->find(key)) {
- *found = std::move(program);
- } else {
- cache->insert(key, std::move(program));
- }
+ cache->insert_or_update(fKey.withCoverage(coverage), std::move(program));
}
};
cache_program(std::move(fBlitH), Coverage::Full);
diff --git a/src/effects/SkHighContrastFilter.cpp b/src/effects/SkHighContrastFilter.cpp
index e8dfc58..a0fcd2f 100644
--- a/src/effects/SkHighContrastFilter.cpp
+++ b/src/effects/SkHighContrastFilter.cpp
@@ -18,38 +18,35 @@
struct Uniforms { float grayscale, invertStyle, contrast; };
- static SkRuntimeEffect* effect = []{
- SkString code{R"(
- uniform shader input;
- uniform half grayscale, invertStyle, contrast;
- )"};
- code += kRGB_to_HSL_sksl;
- code += kHSL_to_RGB_sksl;
- code += R"(
- half4 main() {
- half4 c = sample(input); // linear unpremul RGBA in dst gamut.
- if (grayscale == 1) {
- 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);
+ SkString code{R"(
+ uniform shader input;
+ uniform half grayscale, invertStyle, contrast;
+ )"};
+ code += kRGB_to_HSL_sksl;
+ code += kHSL_to_RGB_sksl;
+ code += R"(
+ half4 main() {
+ half4 c = sample(input); // linear unpremul RGBA in dst gamut.
+ if (grayscale == 1) {
+ c.rgb = dot(half3(0.2126, 0.7152, 0.0722), c.rgb).rrr;
}
- )";
-
- auto [effect, err] = SkRuntimeEffect::Make(code);
- if (!err.isEmpty()) {
- SkDebugf("%s\n%s\n", code.c_str(), err.c_str());
+ 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);
}
- 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-ε.
// I'm not exactly sure why we've historically pinned -1 up to -1+ε, maybe just symmetry?
diff --git a/src/effects/SkLumaColorFilter.cpp b/src/effects/SkLumaColorFilter.cpp
index df234b1..a62432a 100644
--- a/src/effects/SkLumaColorFilter.cpp
+++ b/src/effects/SkLumaColorFilter.cpp
@@ -10,17 +10,14 @@
#include "include/effects/SkRuntimeEffect.h"
sk_sp<SkColorFilter> SkLumaColorFilter::Make() {
- static SkColorFilter* filter = []{
- const char* code =
- "uniform shader input;"
- "half4 main() {"
- "return saturate(dot(half3(0.2126, 0.7152, 0.0722), sample(input).rgb)).000r;"
- "}";
- auto [effect, err] = SkRuntimeEffect::Make(SkString{code}, SkRuntimeEffect::Options{});
- SkASSERT(effect && err.isEmpty());
+ const char* code =
+ "uniform shader input;"
+ "half4 main() {"
+ "return saturate(dot(half3(0.2126, 0.7152, 0.0722), sample(input).rgb)).000r;"
+ "}";
+ auto [effect, err] = SkRuntimeEffect::Make(SkString{code});
+ SkASSERT(effect && err.isEmpty());
- sk_sp<SkColorFilter> input = nullptr;
- return effect->makeColorFilter(SkData::MakeEmpty(), &input, 1).release();
- }();
- return sk_ref_sp(filter);
+ sk_sp<SkColorFilter> input = nullptr;
+ return effect->makeColorFilter(SkData::MakeEmpty(), &input, 1);
}