1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci* Copyright 2019 Google LLC 3cb93a386Sopenharmony_ci* 4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci* found in the LICENSE file. 6cb93a386Sopenharmony_ci*/ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "modules/particles/include/SkParticleEffect.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkPaint.h" 11cb93a386Sopenharmony_ci#include "include/private/SkOnce.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 13cb93a386Sopenharmony_ci#include "modules/particles/include/SkParticleBinding.h" 14cb93a386Sopenharmony_ci#include "modules/particles/include/SkParticleDrawable.h" 15cb93a386Sopenharmony_ci#include "modules/particles/include/SkReflected.h" 16cb93a386Sopenharmony_ci#include "modules/skresources/include/SkResources.h" 17cb93a386Sopenharmony_ci#include "src/core/SkArenaAlloc.h" 18cb93a386Sopenharmony_ci#include "src/core/SkPaintPriv.h" 19cb93a386Sopenharmony_ci#include "src/core/SkVM.h" 20cb93a386Sopenharmony_ci#include "src/sksl/SkSLCompiler.h" 21cb93a386Sopenharmony_ci#include "src/sksl/SkSLUtil.h" 22cb93a386Sopenharmony_ci#include "src/sksl/codegen/SkSLVMCodeGenerator.h" 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci// Cached state for a single program (either all Effect code, or all Particle code) 25cb93a386Sopenharmony_cistruct SkParticleProgram { 26cb93a386Sopenharmony_ci SkParticleProgram(skvm::Program effectSpawn, 27cb93a386Sopenharmony_ci skvm::Program effectUpdate, 28cb93a386Sopenharmony_ci skvm::Program spawn, 29cb93a386Sopenharmony_ci skvm::Program update, 30cb93a386Sopenharmony_ci std::vector<std::unique_ptr<SkSL::ExternalFunction>> externalFunctions, 31cb93a386Sopenharmony_ci skvm::Uniforms externalFunctionUniforms, 32cb93a386Sopenharmony_ci std::unique_ptr<SkArenaAlloc> alloc, 33cb93a386Sopenharmony_ci std::unique_ptr<SkSL::UniformInfo> uniformInfo) 34cb93a386Sopenharmony_ci : fEffectSpawn(std::move(effectSpawn)) 35cb93a386Sopenharmony_ci , fEffectUpdate(std::move(effectUpdate)) 36cb93a386Sopenharmony_ci , fSpawn(std::move(spawn)) 37cb93a386Sopenharmony_ci , fUpdate(std::move(update)) 38cb93a386Sopenharmony_ci , fExternalFunctions(std::move(externalFunctions)) 39cb93a386Sopenharmony_ci , fExternalFunctionUniforms(std::move(externalFunctionUniforms)) 40cb93a386Sopenharmony_ci , fAlloc(std::move(alloc)) 41cb93a386Sopenharmony_ci , fUniformInfo(std::move(uniformInfo)) {} 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_ci // Programs for each entry point 44cb93a386Sopenharmony_ci skvm::Program fEffectSpawn; 45cb93a386Sopenharmony_ci skvm::Program fEffectUpdate; 46cb93a386Sopenharmony_ci skvm::Program fSpawn; 47cb93a386Sopenharmony_ci skvm::Program fUpdate; 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ci // External functions created by each SkParticleBinding 50cb93a386Sopenharmony_ci std::vector<std::unique_ptr<SkSL::ExternalFunction>> fExternalFunctions; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci // Storage for uniforms generated by external functions 53cb93a386Sopenharmony_ci skvm::Uniforms fExternalFunctionUniforms; 54cb93a386Sopenharmony_ci std::unique_ptr<SkArenaAlloc> fAlloc; 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci // Information about uniforms declared in the SkSL 57cb93a386Sopenharmony_ci std::unique_ptr<SkSL::UniformInfo> fUniformInfo; 58cb93a386Sopenharmony_ci}; 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_cistatic const char* kCommonHeader = 61cb93a386Sopenharmony_ciR"( 62cb93a386Sopenharmony_cistruct Effect { 63cb93a386Sopenharmony_ci float age; 64cb93a386Sopenharmony_ci float lifetime; 65cb93a386Sopenharmony_ci int loop; 66cb93a386Sopenharmony_ci float rate; 67cb93a386Sopenharmony_ci int burst; 68cb93a386Sopenharmony_ci 69cb93a386Sopenharmony_ci float2 pos; 70cb93a386Sopenharmony_ci float2 dir; 71cb93a386Sopenharmony_ci float scale; 72cb93a386Sopenharmony_ci float2 vel; 73cb93a386Sopenharmony_ci float spin; 74cb93a386Sopenharmony_ci float4 color; 75cb93a386Sopenharmony_ci float frame; 76cb93a386Sopenharmony_ci float seed; 77cb93a386Sopenharmony_ci}; 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_cistruct Particle { 80cb93a386Sopenharmony_ci float age; 81cb93a386Sopenharmony_ci float lifetime; 82cb93a386Sopenharmony_ci float2 pos; 83cb93a386Sopenharmony_ci float2 dir; 84cb93a386Sopenharmony_ci float scale; 85cb93a386Sopenharmony_ci float2 vel; 86cb93a386Sopenharmony_ci float spin; 87cb93a386Sopenharmony_ci float4 color; 88cb93a386Sopenharmony_ci float frame; 89cb93a386Sopenharmony_ci float seed; 90cb93a386Sopenharmony_ci}; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ciuniform float dt; 93cb93a386Sopenharmony_ciuniform Effect effect; 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci// We use a not-very-random pure-float PRNG. It does have nice properties for our situation: 96cb93a386Sopenharmony_ci// It's fast-ish. Importantly, it only uses types and operations that exist in public SkSL's 97cb93a386Sopenharmony_ci// minimum spec (no bitwise operations on integers). 98cb93a386Sopenharmony_cifloat rand(inout float seed) { 99cb93a386Sopenharmony_ci seed = sin(31*seed) + sin(19*seed + 1); 100cb93a386Sopenharmony_ci return fract(abs(10*seed)); 101cb93a386Sopenharmony_ci} 102cb93a386Sopenharmony_ci)"; 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_cistatic const char* kDefaultCode = 105cb93a386Sopenharmony_ciR"(void effectSpawn(inout Effect effect) { 106cb93a386Sopenharmony_ci} 107cb93a386Sopenharmony_ci 108cb93a386Sopenharmony_civoid effectUpdate(inout Effect effect) { 109cb93a386Sopenharmony_ci} 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_civoid spawn(inout Particle p) { 112cb93a386Sopenharmony_ci} 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_civoid update(inout Particle p) { 115cb93a386Sopenharmony_ci} 116cb93a386Sopenharmony_ci)"; 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ciSkParticleEffectParams::SkParticleEffectParams() 119cb93a386Sopenharmony_ci : fMaxCount(128) 120cb93a386Sopenharmony_ci , fDrawable(nullptr) 121cb93a386Sopenharmony_ci , fCode(kDefaultCode) {} 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_civoid SkParticleEffectParams::visitFields(SkFieldVisitor* v) { 124cb93a386Sopenharmony_ci v->visit("MaxCount", fMaxCount); 125cb93a386Sopenharmony_ci v->visit("Drawable", fDrawable); 126cb93a386Sopenharmony_ci v->visit("Code", fCode); 127cb93a386Sopenharmony_ci v->visit("Bindings", fBindings); 128cb93a386Sopenharmony_ci} 129cb93a386Sopenharmony_ci 130cb93a386Sopenharmony_civoid SkParticleEffectParams::prepare(const skresources::ResourceProvider* resourceProvider) { 131cb93a386Sopenharmony_ci for (auto& binding : fBindings) { 132cb93a386Sopenharmony_ci if (binding) { 133cb93a386Sopenharmony_ci binding->prepare(resourceProvider); 134cb93a386Sopenharmony_ci } 135cb93a386Sopenharmony_ci } 136cb93a386Sopenharmony_ci if (fDrawable) { 137cb93a386Sopenharmony_ci fDrawable->prepare(resourceProvider); 138cb93a386Sopenharmony_ci } 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci auto buildProgram = [this](const SkSL::String& code) -> std::unique_ptr<SkParticleProgram> { 141cb93a386Sopenharmony_ci SkSL::ShaderCapsPointer caps = SkSL::ShaderCapsFactory::Standalone(); 142cb93a386Sopenharmony_ci SkSL::Compiler compiler(caps.get()); 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci // We use two separate blocks of uniforms (ie two args of stride 0). The first is for skvm 145cb93a386Sopenharmony_ci // uniforms generated by any external functions. These are managed with a Uniforms instance, 146cb93a386Sopenharmony_ci // and after it's populated, the values never need to be touched again. 147cb93a386Sopenharmony_ci // The second uniform arg is for things declared as 'uniform' in the SkSL (including the 148cb93a386Sopenharmony_ci // built-in declarations of 'dt' and 'effect'). 149cb93a386Sopenharmony_ci skvm::Uniforms efUniforms(skvm::UPtr{0}, 0); 150cb93a386Sopenharmony_ci auto alloc = std::make_unique<SkArenaAlloc>(0); 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci std::vector<std::unique_ptr<SkSL::ExternalFunction>> externalFns; 153cb93a386Sopenharmony_ci externalFns.reserve(fBindings.size()); 154cb93a386Sopenharmony_ci for (const auto& binding : fBindings) { 155cb93a386Sopenharmony_ci if (binding) { 156cb93a386Sopenharmony_ci externalFns.push_back(binding->toFunction(compiler, &efUniforms, alloc.get())); 157cb93a386Sopenharmony_ci } 158cb93a386Sopenharmony_ci } 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci SkSL::Program::Settings settings; 161cb93a386Sopenharmony_ci settings.fRemoveDeadFunctions = false; 162cb93a386Sopenharmony_ci settings.fExternalFunctions = &externalFns; 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci auto program = compiler.convertProgram(SkSL::ProgramKind::kGeneric, code, settings); 165cb93a386Sopenharmony_ci if (!program) { 166cb93a386Sopenharmony_ci SkDebugf("%s\n", compiler.errorText().c_str()); 167cb93a386Sopenharmony_ci return nullptr; 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci 170cb93a386Sopenharmony_ci std::unique_ptr<SkSL::UniformInfo> uniformInfo = SkSL::Program_GetUniformInfo(*program); 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci // For each entry point, convert to an skvm::Program. We need a fresh Builder and uniform 173cb93a386Sopenharmony_ci // IDs (though we can reuse the Uniforms object, thanks to how it works). 174cb93a386Sopenharmony_ci auto buildFunction = [&](const char* name){ 175cb93a386Sopenharmony_ci auto fn = SkSL::Program_GetFunction(*program, name); 176cb93a386Sopenharmony_ci if (!fn) { 177cb93a386Sopenharmony_ci return skvm::Program{}; 178cb93a386Sopenharmony_ci } 179cb93a386Sopenharmony_ci 180cb93a386Sopenharmony_ci skvm::Builder b; 181cb93a386Sopenharmony_ci skvm::UPtr efUniformPtr = b.uniform(), // aka efUniforms.base 182cb93a386Sopenharmony_ci skslUniformPtr = b.uniform(); 183cb93a386Sopenharmony_ci (void)efUniformPtr; 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci std::vector<skvm::Val> uniformIDs; 186cb93a386Sopenharmony_ci for (int i = 0; i < uniformInfo->fUniformSlotCount; ++i) { 187cb93a386Sopenharmony_ci uniformIDs.push_back(b.uniform32(skslUniformPtr, i * sizeof(int)).id); 188cb93a386Sopenharmony_ci } 189cb93a386Sopenharmony_ci if (!SkSL::ProgramToSkVM(*program, *fn, &b, /*debugInfo=*/nullptr, 190cb93a386Sopenharmony_ci SkMakeSpan(uniformIDs))) { 191cb93a386Sopenharmony_ci return skvm::Program{}; 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci return b.done(); 194cb93a386Sopenharmony_ci }; 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci skvm::Program effectSpawn = buildFunction("effectSpawn"), 197cb93a386Sopenharmony_ci effectUpdate = buildFunction("effectUpdate"), 198cb93a386Sopenharmony_ci spawn = buildFunction("spawn"), 199cb93a386Sopenharmony_ci update = buildFunction("update"); 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci return std::make_unique<SkParticleProgram>(std::move(effectSpawn), 202cb93a386Sopenharmony_ci std::move(effectUpdate), 203cb93a386Sopenharmony_ci std::move(spawn), 204cb93a386Sopenharmony_ci std::move(update), 205cb93a386Sopenharmony_ci std::move(externalFns), 206cb93a386Sopenharmony_ci std::move(efUniforms), 207cb93a386Sopenharmony_ci std::move(alloc), 208cb93a386Sopenharmony_ci std::move(uniformInfo)); 209cb93a386Sopenharmony_ci }; 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci SkSL::String particleCode(kCommonHeader); 212cb93a386Sopenharmony_ci particleCode.append(fCode.c_str()); 213cb93a386Sopenharmony_ci 214cb93a386Sopenharmony_ci if (auto prog = buildProgram(particleCode)) { 215cb93a386Sopenharmony_ci fProgram = std::move(prog); 216cb93a386Sopenharmony_ci } 217cb93a386Sopenharmony_ci} 218cb93a386Sopenharmony_ci 219cb93a386Sopenharmony_ciSkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params) 220cb93a386Sopenharmony_ci : fParams(std::move(params)) 221cb93a386Sopenharmony_ci , fLooping(false) 222cb93a386Sopenharmony_ci , fCount(0) 223cb93a386Sopenharmony_ci , fLastTime(-1.0) 224cb93a386Sopenharmony_ci , fSpawnRemainder(0.0f) { 225cb93a386Sopenharmony_ci fState.fAge = -1.0f; 226cb93a386Sopenharmony_ci this->updateStorage(); 227cb93a386Sopenharmony_ci} 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_civoid SkParticleEffect::updateStorage() { 230cb93a386Sopenharmony_ci // Handle user edits to fMaxCount 231cb93a386Sopenharmony_ci if (fParams->fMaxCount != fCapacity) { 232cb93a386Sopenharmony_ci this->setCapacity(fParams->fMaxCount); 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci 235cb93a386Sopenharmony_ci // Ensure our storage block for uniforms is large enough 236cb93a386Sopenharmony_ci if (this->uniformInfo()) { 237cb93a386Sopenharmony_ci int newCount = this->uniformInfo()->fUniformSlotCount; 238cb93a386Sopenharmony_ci if (newCount > fUniforms.count()) { 239cb93a386Sopenharmony_ci fUniforms.push_back_n(newCount - fUniforms.count(), 0.0f); 240cb93a386Sopenharmony_ci } else { 241cb93a386Sopenharmony_ci fUniforms.resize(newCount); 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci } 244cb93a386Sopenharmony_ci} 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_cibool SkParticleEffect::setUniform(const char* name, const float* val, int count) { 247cb93a386Sopenharmony_ci const SkSL::UniformInfo* info = this->uniformInfo(); 248cb93a386Sopenharmony_ci if (!info) { 249cb93a386Sopenharmony_ci return false; 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci auto it = std::find_if(info->fUniforms.begin(), info->fUniforms.end(), 253cb93a386Sopenharmony_ci [name](const auto& u) { return u.fName == name; }); 254cb93a386Sopenharmony_ci if (it == info->fUniforms.end()) { 255cb93a386Sopenharmony_ci return false; 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci if (it->fRows * it->fColumns != count) { 258cb93a386Sopenharmony_ci return false; 259cb93a386Sopenharmony_ci } 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci std::copy(val, val + count, this->uniformData() + it->fSlot); 262cb93a386Sopenharmony_ci return true; 263cb93a386Sopenharmony_ci} 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_civoid SkParticleEffect::start(double now, bool looping, SkPoint position, SkVector heading, 266cb93a386Sopenharmony_ci float scale, SkVector velocity, float spin, SkColor4f color, 267cb93a386Sopenharmony_ci float frame, float seed) { 268cb93a386Sopenharmony_ci fCount = 0; 269cb93a386Sopenharmony_ci fLastTime = now; 270cb93a386Sopenharmony_ci fSpawnRemainder = 0.0f; 271cb93a386Sopenharmony_ci fLooping = looping; 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci fState.fAge = 0.0f; 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci // A default lifetime makes sense - many effects are simple loops that don't really care. 276cb93a386Sopenharmony_ci // Every effect should define its own rate of emission, or only use bursts, so leave that as 277cb93a386Sopenharmony_ci // zero initially. 278cb93a386Sopenharmony_ci fState.fLifetime = 1.0f; 279cb93a386Sopenharmony_ci fState.fLoopCount = 0; 280cb93a386Sopenharmony_ci fState.fRate = 0.0f; 281cb93a386Sopenharmony_ci fState.fBurst = 0; 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci fState.fPosition = position; 284cb93a386Sopenharmony_ci fState.fHeading = heading; 285cb93a386Sopenharmony_ci fState.fScale = scale; 286cb93a386Sopenharmony_ci fState.fVelocity = velocity; 287cb93a386Sopenharmony_ci fState.fSpin = spin; 288cb93a386Sopenharmony_ci fState.fColor = color; 289cb93a386Sopenharmony_ci fState.fFrame = frame; 290cb93a386Sopenharmony_ci fState.fRandom = seed; 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_ci // Defer running effectSpawn until the first update (to reuse the code when looping) 293cb93a386Sopenharmony_ci} 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci// Just the update step from our "rand" function 296cb93a386Sopenharmony_cistatic float advance_seed(float x) { 297cb93a386Sopenharmony_ci return sinf(31*x) + sinf(19*x + 1); 298cb93a386Sopenharmony_ci} 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_civoid SkParticleEffect::runEffectScript(EntryPoint entryPoint) { 301cb93a386Sopenharmony_ci if (!fParams->fProgram) { 302cb93a386Sopenharmony_ci return; 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci const skvm::Program& prog = entryPoint == EntryPoint::kSpawn ? fParams->fProgram->fEffectSpawn 306cb93a386Sopenharmony_ci : fParams->fProgram->fEffectUpdate; 307cb93a386Sopenharmony_ci if (prog.empty()) { 308cb93a386Sopenharmony_ci return; 309cb93a386Sopenharmony_ci } 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci constexpr size_t kNumEffectArgs = sizeof(EffectState) / sizeof(int); 312cb93a386Sopenharmony_ci void* args[kNumEffectArgs 313cb93a386Sopenharmony_ci + 1 // external function uniforms 314cb93a386Sopenharmony_ci + 1]; // SkSL uniforms 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci args[0] = fParams->fProgram->fExternalFunctionUniforms.buf.data(); 317cb93a386Sopenharmony_ci args[1] = fUniforms.data(); 318cb93a386Sopenharmony_ci for (size_t i = 0; i < kNumEffectArgs; ++i) { 319cb93a386Sopenharmony_ci args[i + 2] = SkTAddOffset<void>(&fState, i * sizeof(int)); 320cb93a386Sopenharmony_ci } 321cb93a386Sopenharmony_ci 322cb93a386Sopenharmony_ci memcpy(&fUniforms[1], &fState.fAge, sizeof(EffectState)); 323cb93a386Sopenharmony_ci prog.eval(1, args); 324cb93a386Sopenharmony_ci} 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_civoid SkParticleEffect::runParticleScript(EntryPoint entryPoint, int start, int count) { 327cb93a386Sopenharmony_ci if (!fParams->fProgram) { 328cb93a386Sopenharmony_ci return; 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci const skvm::Program& prog = entryPoint == EntryPoint::kSpawn ? fParams->fProgram->fSpawn 332cb93a386Sopenharmony_ci : fParams->fProgram->fUpdate; 333cb93a386Sopenharmony_ci if (prog.empty()) { 334cb93a386Sopenharmony_ci return; 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci 337cb93a386Sopenharmony_ci void* args[SkParticles::kNumChannels 338cb93a386Sopenharmony_ci + 1 // external function uniforms 339cb93a386Sopenharmony_ci + 1]; // SkSL uniforms 340cb93a386Sopenharmony_ci args[0] = fParams->fProgram->fExternalFunctionUniforms.buf.data(); 341cb93a386Sopenharmony_ci args[1] = fUniforms.data(); 342cb93a386Sopenharmony_ci for (int i = 0; i < SkParticles::kNumChannels; ++i) { 343cb93a386Sopenharmony_ci args[i + 2] = fParticles.fData[i].get() + start; 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci memcpy(&fUniforms[1], &fState.fAge, sizeof(EffectState)); 347cb93a386Sopenharmony_ci prog.eval(count, args); 348cb93a386Sopenharmony_ci} 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_civoid SkParticleEffect::advanceTime(double now) { 351cb93a386Sopenharmony_ci // TODO: Sub-frame spawning. Tricky with script driven position. Supply variable effect.age? 352cb93a386Sopenharmony_ci // Could be done if effect.age were an external value that offset by particle lane, perhaps. 353cb93a386Sopenharmony_ci float deltaTime = static_cast<float>(now - fLastTime); 354cb93a386Sopenharmony_ci if (deltaTime <= 0.0f) { 355cb93a386Sopenharmony_ci return; 356cb93a386Sopenharmony_ci } 357cb93a386Sopenharmony_ci fLastTime = now; 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ci // Possibly re-allocate cached storage, if our params have changed 360cb93a386Sopenharmony_ci this->updateStorage(); 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ci // Copy known values into the uniform blocks 363cb93a386Sopenharmony_ci if (fParams->fProgram) { 364cb93a386Sopenharmony_ci fUniforms[0] = deltaTime; 365cb93a386Sopenharmony_ci } 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci // Is this the first update after calling start()? 368cb93a386Sopenharmony_ci // Run 'effectSpawn' to set initial emitter properties. 369cb93a386Sopenharmony_ci if (fState.fAge == 0.0f && fState.fLoopCount == 0) { 370cb93a386Sopenharmony_ci this->runEffectScript(EntryPoint::kSpawn); 371cb93a386Sopenharmony_ci } 372cb93a386Sopenharmony_ci 373cb93a386Sopenharmony_ci fState.fAge += deltaTime / fState.fLifetime; 374cb93a386Sopenharmony_ci if (fState.fAge > 1) { 375cb93a386Sopenharmony_ci if (fLooping) { 376cb93a386Sopenharmony_ci // If we looped, then run effectSpawn again (with the updated loop count) 377cb93a386Sopenharmony_ci fState.fLoopCount += sk_float_floor2int(fState.fAge); 378cb93a386Sopenharmony_ci fState.fAge = fmodf(fState.fAge, 1.0f); 379cb93a386Sopenharmony_ci this->runEffectScript(EntryPoint::kSpawn); 380cb93a386Sopenharmony_ci } else { 381cb93a386Sopenharmony_ci // Effect is dead if we've reached the end (and are not looping) 382cb93a386Sopenharmony_ci return; 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci } 385cb93a386Sopenharmony_ci 386cb93a386Sopenharmony_ci // Advance age for existing particles, and remove any that have reached their end of life 387cb93a386Sopenharmony_ci for (int i = 0; i < fCount; ++i) { 388cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kAge][i] += 389cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kLifetime][i] * deltaTime; 390cb93a386Sopenharmony_ci if (fParticles.fData[SkParticles::kAge][i] > 1.0f) { 391cb93a386Sopenharmony_ci // NOTE: This is fast, but doesn't preserve drawing order. Could be a problem... 392cb93a386Sopenharmony_ci for (int j = 0; j < SkParticles::kNumChannels; ++j) { 393cb93a386Sopenharmony_ci fParticles.fData[j][i] = fParticles.fData[j][fCount - 1]; 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci fStableRandoms[i] = fStableRandoms[fCount - 1]; 396cb93a386Sopenharmony_ci --i; 397cb93a386Sopenharmony_ci --fCount; 398cb93a386Sopenharmony_ci } 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci // Run 'effectUpdate' to adjust emitter properties 402cb93a386Sopenharmony_ci this->runEffectScript(EntryPoint::kUpdate); 403cb93a386Sopenharmony_ci 404cb93a386Sopenharmony_ci // Do integration of effect position and orientation 405cb93a386Sopenharmony_ci { 406cb93a386Sopenharmony_ci fState.fPosition += fState.fVelocity * deltaTime; 407cb93a386Sopenharmony_ci float s = sk_float_sin(fState.fSpin * deltaTime), 408cb93a386Sopenharmony_ci c = sk_float_cos(fState.fSpin * deltaTime); 409cb93a386Sopenharmony_ci // Using setNormalize to prevent scale drift 410cb93a386Sopenharmony_ci fState.fHeading.setNormalize(fState.fHeading.fX * c - fState.fHeading.fY * s, 411cb93a386Sopenharmony_ci fState.fHeading.fX * s + fState.fHeading.fY * c); 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci 414cb93a386Sopenharmony_ci // Spawn new particles 415cb93a386Sopenharmony_ci float desired = fState.fRate * deltaTime + fSpawnRemainder + fState.fBurst; 416cb93a386Sopenharmony_ci fState.fBurst = 0; 417cb93a386Sopenharmony_ci int numToSpawn = sk_float_round2int(desired); 418cb93a386Sopenharmony_ci fSpawnRemainder = desired - numToSpawn; 419cb93a386Sopenharmony_ci numToSpawn = SkTPin(numToSpawn, 0, fParams->fMaxCount - fCount); 420cb93a386Sopenharmony_ci if (numToSpawn) { 421cb93a386Sopenharmony_ci const int spawnBase = fCount; 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_ci for (int i = 0; i < numToSpawn; ++i) { 424cb93a386Sopenharmony_ci // Mutate our random seed so each particle definitely gets a different generator 425cb93a386Sopenharmony_ci fState.fRandom = advance_seed(fState.fRandom); 426cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kAge ][fCount] = 0.0f; 427cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kLifetime ][fCount] = 0.0f; 428cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kPositionX ][fCount] = fState.fPosition.fX; 429cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kPositionY ][fCount] = fState.fPosition.fY; 430cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kHeadingX ][fCount] = fState.fHeading.fX; 431cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kHeadingY ][fCount] = fState.fHeading.fY; 432cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kScale ][fCount] = fState.fScale; 433cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kVelocityX ][fCount] = fState.fVelocity.fX; 434cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kVelocityY ][fCount] = fState.fVelocity.fY; 435cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kVelocityAngular][fCount] = fState.fSpin; 436cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kColorR ][fCount] = fState.fColor.fR; 437cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kColorG ][fCount] = fState.fColor.fG; 438cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kColorB ][fCount] = fState.fColor.fB; 439cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kColorA ][fCount] = fState.fColor.fA; 440cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kSpriteFrame ][fCount] = fState.fFrame; 441cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kRandom ][fCount] = fState.fRandom; 442cb93a386Sopenharmony_ci fCount++; 443cb93a386Sopenharmony_ci } 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci // Run the spawn script 446cb93a386Sopenharmony_ci this->runParticleScript(EntryPoint::kSpawn, spawnBase, numToSpawn); 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci // Now stash copies of the random seeds and compute inverse particle lifetimes 449cb93a386Sopenharmony_ci // (so that subsequent updates are faster) 450cb93a386Sopenharmony_ci for (int i = spawnBase; i < fCount; ++i) { 451cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kLifetime][i] = 452cb93a386Sopenharmony_ci sk_ieee_float_divide(1.0f, fParticles.fData[SkParticles::kLifetime][i]); 453cb93a386Sopenharmony_ci fStableRandoms[i] = fParticles.fData[SkParticles::kRandom][i]; 454cb93a386Sopenharmony_ci } 455cb93a386Sopenharmony_ci } 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci // Restore all stable random seeds so update scripts get consistent behavior each frame 458cb93a386Sopenharmony_ci for (int i = 0; i < fCount; ++i) { 459cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kRandom][i] = fStableRandoms[i]; 460cb93a386Sopenharmony_ci } 461cb93a386Sopenharmony_ci 462cb93a386Sopenharmony_ci // Run the update script 463cb93a386Sopenharmony_ci this->runParticleScript(EntryPoint::kUpdate, 0, fCount); 464cb93a386Sopenharmony_ci 465cb93a386Sopenharmony_ci // Do fixed-function update work (integration of position and orientation) 466cb93a386Sopenharmony_ci for (int i = 0; i < fCount; ++i) { 467cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kPositionX][i] += 468cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kVelocityX][i] * deltaTime; 469cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kPositionY][i] += 470cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kVelocityY][i] * deltaTime; 471cb93a386Sopenharmony_ci 472cb93a386Sopenharmony_ci float spin = fParticles.fData[SkParticles::kVelocityAngular][i]; 473cb93a386Sopenharmony_ci float s = sk_float_sin(spin * deltaTime), 474cb93a386Sopenharmony_ci c = sk_float_cos(spin * deltaTime); 475cb93a386Sopenharmony_ci float oldHeadingX = fParticles.fData[SkParticles::kHeadingX][i], 476cb93a386Sopenharmony_ci oldHeadingY = fParticles.fData[SkParticles::kHeadingY][i]; 477cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kHeadingX][i] = oldHeadingX * c - oldHeadingY * s; 478cb93a386Sopenharmony_ci fParticles.fData[SkParticles::kHeadingY][i] = oldHeadingX * s + oldHeadingY * c; 479cb93a386Sopenharmony_ci } 480cb93a386Sopenharmony_ci} 481cb93a386Sopenharmony_ci 482cb93a386Sopenharmony_civoid SkParticleEffect::update(double now) { 483cb93a386Sopenharmony_ci if (this->isAlive()) { 484cb93a386Sopenharmony_ci this->advanceTime(now); 485cb93a386Sopenharmony_ci } 486cb93a386Sopenharmony_ci} 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_civoid SkParticleEffect::draw(SkCanvas* canvas) { 489cb93a386Sopenharmony_ci if (this->isAlive() && fParams->fDrawable) { 490cb93a386Sopenharmony_ci SkPaint paint; 491cb93a386Sopenharmony_ci fParams->fDrawable->draw(canvas, fParticles, fCount, paint); 492cb93a386Sopenharmony_ci } 493cb93a386Sopenharmony_ci} 494cb93a386Sopenharmony_ci 495cb93a386Sopenharmony_civoid SkParticleEffect::setCapacity(int capacity) { 496cb93a386Sopenharmony_ci for (int i = 0; i < SkParticles::kNumChannels; ++i) { 497cb93a386Sopenharmony_ci fParticles.fData[i].realloc(capacity); 498cb93a386Sopenharmony_ci } 499cb93a386Sopenharmony_ci fStableRandoms.realloc(capacity); 500cb93a386Sopenharmony_ci 501cb93a386Sopenharmony_ci fCapacity = capacity; 502cb93a386Sopenharmony_ci fCount = std::min(fCount, fCapacity); 503cb93a386Sopenharmony_ci} 504cb93a386Sopenharmony_ci 505cb93a386Sopenharmony_ciconst SkSL::UniformInfo* SkParticleEffect::uniformInfo() const { 506cb93a386Sopenharmony_ci return fParams->fProgram ? fParams->fProgram->fUniformInfo.get() : nullptr; 507cb93a386Sopenharmony_ci} 508cb93a386Sopenharmony_ci 509cb93a386Sopenharmony_civoid SkParticleEffect::RegisterParticleTypes() { 510cb93a386Sopenharmony_ci static SkOnce once; 511cb93a386Sopenharmony_ci once([]{ 512cb93a386Sopenharmony_ci REGISTER_REFLECTED(SkReflected); 513cb93a386Sopenharmony_ci SkParticleBinding::RegisterBindingTypes(); 514cb93a386Sopenharmony_ci SkParticleDrawable::RegisterDrawableTypes(); 515cb93a386Sopenharmony_ci }); 516cb93a386Sopenharmony_ci} 517