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