plumb experimental SkSL layer effect in native Skottie player

Long term plan is to expose a plugin (standalone or with bodymovin) that allows motion artists to write sksl into a composition.
This is the first step where we test how we'd read in the json data under the hood.

Change-Id: I300d3af5d01e12f5b495970f89fd12b5f464a9a1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/464368
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
Reviewed-by: Florin Malita <fmalita@google.com>
This commit is contained in:
Jorge Betancourt 2021-11-01 13:18:14 -04:00 committed by SkCQ
parent 83e99569bd
commit 172c7998e1
5 changed files with 150 additions and 1 deletions

View File

@ -64,6 +64,7 @@ skia_skottie_sources = [
"$_src/effects/RadialWipeEffect.cpp",
"$_src/effects/ShadowStyles.cpp",
"$_src/effects/ShiftChannelsEffect.cpp",
"$_src/effects/SkSLEffect.cpp",
"$_src/effects/SphereEffect.cpp",
"$_src/effects/ThresholdEffect.cpp",
"$_src/effects/TintEffect.cpp",

View File

@ -56,7 +56,8 @@ EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectVal
{ "ADBE Tritone" , &EffectBuilder::attachTritoneEffect },
{ "ADBE Venetian Blinds" , &EffectBuilder::attachVenetianBlindsEffect },
{ "CC Sphere" , &EffectBuilder::attachSphereEffect },
{ "CC Toner" , &EffectBuilder::attachCCTonerEffect },
{ "CC Toner" , &EffectBuilder::attachCCTonerEffect },
{ "SkSL Shader" , &EffectBuilder::attachSkSLEffect },
};
const skjson::StringValue* mn = jeffect["mn"];

View File

@ -79,6 +79,8 @@ private:
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachShiftChannelsEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachSkSLEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachSphereEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachThresholdEffect (const skjson::ArrayValue&,

View File

@ -0,0 +1,144 @@
/*
* Copyright 2021 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/skottie/src/effects/Effects.h"
#include "include/effects/SkRuntimeEffect.h"
#include "modules/skottie/src/Adapter.h"
#include "modules/skottie/src/SkottieJson.h"
#include "modules/skottie/src/SkottieValue.h"
#include "modules/sksg/include/SkSGColorFilter.h"
namespace skottie::internal {
#ifdef SK_ENABLE_SKSL
namespace {
class SkSLShaderNode final : public sksg::CustomRenderNode {
public:
explicit SkSLShaderNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {}
SG_ATTRIBUTE(Shader, sk_sp<SkShader>, fEffectShader)
private:
SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
const auto& child = this->children()[0];
return child->revalidate(nullptr, SkMatrix::I());
}
void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
const auto& bounds = this->bounds();
const auto local_ctx = ScopedRenderContext(canvas, ctx)
.setIsolation(bounds, canvas->getTotalMatrix(), true);
canvas->saveLayer(&bounds, nullptr);
this->children()[0]->render(canvas, local_ctx);
SkPaint effect_paint;
effect_paint.setShader(fEffectShader);
effect_paint.setBlendMode(SkBlendMode::kSrcIn);
canvas->drawPaint(effect_paint);
}
const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
sk_sp<SkShader> fEffectShader;
using INHERITED = sksg::CustomRenderNode;
};
class SkSLEffectAdapter final : public DiscardableAdapterBase<SkSLEffectAdapter,
SkSLShaderNode> {
public:
SkSLEffectAdapter(const skjson::ArrayValue& jprops,
const AnimationBuilder& abuilder,
sk_sp<SkSLShaderNode> node)
: INHERITED(std::move(node))
{
enum : size_t {
kSkSL_index = 0,
kFirstUniform_index = 1,
};
if (jprops.size() < 1) {
return;
}
const skjson::ObjectValue* jSkSL = jprops[kSkSL_index];
if (!jSkSL) {
return;
}
const skjson::StringValue* jShader = (*jSkSL)["sh"];
if (!jShader) {
return;
}
SkString shader = SkString(jShader->begin(), jShader->size());
auto result = SkRuntimeEffect::MakeForShader(shader, {});
if (!result.effect) {
abuilder.log(Logger::Level::kError, nullptr, "Failed to parse SkSL shader: %s",
result.errorText.c_str());
return;
}
fEffect = std::move(result.effect);
// construct dynamic uniform list from jprops, skip SkSL property
for (size_t i = kFirstUniform_index; i < jprops.size(); i++) {
const skjson::ObjectValue* jprop = jprops[i];
if (!jprop) { continue; }
const skjson::StringValue* uniformName = (*jprop)["nm"];
if (!uniformName) { continue; }
auto uniformTuple = std::make_tuple(SkString(uniformName->begin(),
uniformName->size()),
std::make_unique<VectorValue>());
fUniforms.push_back(std::move(uniformTuple));
this->bind(abuilder, (*jprop)["v"], std::get<1>(fUniforms.back()).get());
}
}
private:
void onSync() override {
this->node()->setShader(buildEffectShader());
}
sk_sp<SkShader> buildEffectShader() const {
if (!fEffect) {
return nullptr;
}
// TODO: consider dumping builder and work with lower level API
SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(fEffect);
for (const auto& uniform : fUniforms) {
const auto& name = std::get<0>(uniform);
const auto& data = std::get<1>(uniform);
auto metadata = fEffect->findUniform(name.c_str());
// TODO: build SkData from SkRuntimeEffect::Uniform data
switch (metadata->type) {
case SkRuntimeEffect::Uniform::Type::kFloat:
builder.uniform(name.c_str()) = data->at(0); break;
default:
printf("!!! %s\n", "uniform data type not supported");
}
}
return builder.makeShader(&SkMatrix::I(), false);
}
sk_sp<SkRuntimeEffect> fEffect;
std::vector<std::tuple<SkString, std::unique_ptr<VectorValue>>> fUniforms;
using INHERITED = DiscardableAdapterBase<SkSLEffectAdapter, SkSLShaderNode>;
};
} // namespace
#endif // SK_ENABLE_SKSL
sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLEffect(const skjson::ArrayValue& jprops,
sk_sp<sksg::RenderNode> layer) const {
#ifdef SK_ENABLE_SKSL
auto shaderNode = sk_make_sp<SkSLShaderNode>(std::move(layer));
return fBuilder->attachDiscardableAdapter<SkSLEffectAdapter>(jprops, *fBuilder, shaderNode);
#else
return layer;
#endif
}
} // namespace skottie::internal

View File

@ -0,0 +1 @@
{"assets":[],"ddd":0,"fr":60,"h":500,"ip":0,"layers":[{"ao":0,"bm":0,"ddd":0,"ef":[{"ef":[{"ix":1,"mn":"SkSL Shader-0001","nm":"SKSL","sh":"uniform float u_time; float f(vec3 p) {p.z -= u_time * 10.;float a = p.z * .1;p.xy *= mat2(cos(a), sin(a), -sin(a), cos(a));return .1 - length(cos(p.xy) + sin(p.yz));}half4 main(vec2 fragcoord) {vec3 d = .5 - fragcoord.xy1 / 500;vec3 p=vec3(0);for (int i = 0; i < 32; i++) p += f(p) * d;return ((sin(p) + vec3(2, 5, 9)) / length(p)).xyz1;}","ty":0},{"ix":1,"mn":"SkSL Shader-0002","nm":"u_time","ty":0,"v":{"a":1,"ix":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0],"t":0},{"s":[3.922],"t":299}]}}],"en":1,"ix":1,"mn":"SkSL Shader","nm":"sksl","np":3,"ty":5}],"ind":1,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[200,200,0],"l":2},"o":{"a":0,"ix":11,"k":100},"p":{"a":0,"ix":2,"k":[250,250,0],"l":2},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100],"l":2}},"nm":"Lime Green Solid 1","op":300,"sc":"#83e325","sh":400,"sr":1,"st":0,"sw":400,"ty":1}],"markers":[],"nm":"sksl","op":300,"v":"5.7.14","w":500}