skia2/modules/canvaskit/particles_bindings.cpp
Brian Osman e89d8ea20b Update particle system to use skvm, rather than ByteCode
This was the last remaining user of ByteCode. The skvm solution
is faster, and lets us delete the ByteCode system.

Testing on 15 instances of sinusoidal_emitter (90k particles):
  - ByteCode                     ~9   ms
  - ByteCode (older, optimized): ~5.5 ms
  - skvm                         ~2.1 ms

Change-Id: Ia2e5c9ab2d36c97e59af28a6f989bf212889e439
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/356919
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
2021-01-21 23:07:29 +00:00

211 lines
7.8 KiB
C++

/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkTypes.h"
#include "include/utils/SkRandom.h"
#include "modules/particles/include/SkParticleEffect.h"
#include "modules/particles/include/SkParticleSerialization.h"
#include "modules/skresources/include/SkResources.h"
#include "src/sksl/SkSLVMGenerator.h"
#include <string>
#include "modules/canvaskit/WasmCommon.h"
#include <emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
namespace {
class ParticleAssetProvider : public skresources::ResourceProvider {
public:
~ParticleAssetProvider() override = default;
// Tried using a map, but that gave strange errors like
// https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
// Not entirely sure why, but perhaps the iterator in the map was
// confusing enscripten.
using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
static sk_sp<ParticleAssetProvider> Make(AssetVec assets) {
if (assets.empty()) {
return nullptr;
}
return sk_sp<ParticleAssetProvider>(new ParticleAssetProvider(std::move(assets)));
}
sk_sp<skresources::ImageAsset> loadImageAsset(const char[] /* path */,
const char name[],
const char[] /* id */) const override {
// For CK we ignore paths & IDs, and identify images based solely on name.
if (auto data = this->findAsset(name)) {
return skresources::MultiFrameImageAsset::Make(std::move(data));
}
return nullptr;
}
sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
// Same as images paths, we ignore font URLs.
return this->findAsset(name);
}
private:
explicit ParticleAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
sk_sp<SkData> findAsset(const char name[]) const {
for (const auto& asset : fAssets) {
if (asset.first.equals(name)) {
return asset.second;
}
}
SkDebugf("Could not find %s\n", name);
return nullptr;
}
const AssetVec fAssets;
};
}
struct SimpleUniform {
int columns;
int rows;
int slot; // the index into the uniforms array that this uniform begins.
};
SimpleUniform fromUniform(const SkSL::UniformInfo::Uniform& u) {
SimpleUniform su;
su.columns = u.fColumns;
su.rows = u.fRows;
su.slot = u.fSlot;
return su;
}
EMSCRIPTEN_BINDINGS(Particles) {
class_<SkParticleEffect>("ParticleEffect")
.smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
.function("draw", &SkParticleEffect::draw, allow_raw_pointers())
.function("_effectUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
return reinterpret_cast<uintptr_t>(self.effectUniforms());
}))
.function("_particleUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
return reinterpret_cast<uintptr_t>(self.particleUniforms());
}))
.function("getEffectUniformCount", optional_override([](SkParticleEffect& self)->int {
auto ec = self.effectUniformInfo();
if (!ec) {
return -1;
}
return ec->fUniforms.size();
}))
.function("getEffectUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
auto ec = self.effectUniformInfo();
if (!ec) {
return -1;
}
return ec->fUniformSlotCount;
}))
.function("getEffectUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
auto ec = self.effectUniformInfo();
if (!ec) {
return emscripten::val::null();
}
return emscripten::val(ec->fUniforms[i].fName.c_str());
}))
.function("getEffectUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
SimpleUniform su;
auto ec = self.effectUniformInfo();
if (!ec) {
return su;
}
su = fromUniform(ec->fUniforms[i]);
return su;
}))
.function("getParticleUniformCount", optional_override([](SkParticleEffect& self)->int {
auto ec = self.particleUniformInfo();
if (!ec) {
return -1;
}
return ec->fUniforms.size();
}))
.function("getParticleUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
auto ec = self.particleUniformInfo();
if (!ec) {
return -1;
}
return ec->fUniformSlotCount;
}))
.function("getParticleUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
auto ec = self.particleUniformInfo();
if (!ec) {
return emscripten::val::null();
}
return emscripten::val(ec->fUniforms[i].fName.c_str());
}))
.function("getParticleUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
SimpleUniform su;
auto ec = self.particleUniformInfo();
if (!ec) {
return su;
}
su = fromUniform(ec->fUniforms[i]);
return su;
}))
.function("setPosition", select_overload<void (SkPoint)>(&SkParticleEffect::setPosition))
.function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate))
.function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
.function("update", select_overload<void (double)>(&SkParticleEffect::update));
value_object<SimpleUniform>("SimpleUniform")
.field("columns", &SimpleUniform::columns)
.field("rows", &SimpleUniform::rows)
.field("slot", &SimpleUniform::slot);
function("_MakeParticles", optional_override([](std::string json,
size_t assetCount,
uintptr_t /* char** */ nptr,
uintptr_t /* uint8_t** */ dptr,
uintptr_t /* size_t* */ sptr)
->sk_sp<SkParticleEffect> {
// See the comment in canvaskit_bindings.cpp about the use of uintptr_t
static bool didInit = false;
if (!didInit) {
SkParticleEffect::RegisterParticleTypes();
didInit = true;
}
const auto assetNames = reinterpret_cast<char** >(nptr);
const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
const auto assetSizes = reinterpret_cast<size_t* >(sptr);
ParticleAssetProvider::AssetVec assets;
assets.reserve(assetCount);
for (size_t i = 0; i < assetCount; i++) {
auto name = SkString(assetNames[i]);
auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
}
sk_sp<SkParticleEffectParams> params(new SkParticleEffectParams());
skjson::DOM dom(json.c_str(), json.length());
SkFromJsonVisitor fromJson(dom.root());
params->visitFields(&fromJson);
params->prepare(skresources::DataURIResourceProviderProxy::Make(
ParticleAssetProvider::Make(std::move(assets))).get());
return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params)));
}));
constant("particles", true);
}