Particles: Merge spawn & update into one code string with two functions

Change-Id: If57fb79db8f8c5fd185fefaa202167c8082dd846
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/229921
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/include/SkParticleEffect.h b/modules/particles/include/SkParticleEffect.h
index c2648cc..4f7b420 100644
--- a/modules/particles/include/SkParticleEffect.h
+++ b/modules/particles/include/SkParticleEffect.h
@@ -53,11 +53,8 @@
     // Drawable (image, sprite sheet, etc.)
     sk_sp<SkParticleDrawable> fDrawable;
 
-    // Code to configure particles at spawn time
-    SkString fSpawnCode;
-
-    // Code to update existing particles over their lifetime
-    SkString fUpdateCode;
+    // Code with spawn() and update() functions
+    SkString fCode;
 
     SkTArray<sk_sp<SkParticleBinding>> fBindings;
 
@@ -67,12 +64,8 @@
     friend class SkParticleEffect;
 
     // Cached
-    struct Program {
-        std::unique_ptr<SkSL::ByteCode> fByteCode;
-        SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
-    };
-    Program fSpawnProgram;
-    Program fUpdateProgram;
+    std::unique_ptr<SkSL::ByteCode> fByteCode;
+    SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
 
     void rebuild();
 };
diff --git a/modules/particles/src/SkParticleEffect.cpp b/modules/particles/src/SkParticleEffect.cpp
index 056e12f..9238ce8 100644
--- a/modules/particles/src/SkParticleEffect.cpp
+++ b/modules/particles/src/SkParticleEffect.cpp
@@ -302,8 +302,7 @@
 };
 
 static const char* kDefaultCode =
-R"(
-// float rand; Every read returns a random float [0 .. 1)
+R"(// float rand; Every read returns a random float [0 .. 1)
 layout(ctype=float) in uniform float dt;
 layout(ctype=float) in uniform float effectAge;
 
@@ -319,7 +318,10 @@
   float  frame;
 };
 
-void main(inout Particle p) {
+void spawn(inout Particle p) {
+}
+
+void update(inout Particle p) {
 }
 )";
 
@@ -328,14 +330,12 @@
         , fEffectDuration(1.0f)
         , fRate(8.0f)
         , fDrawable(nullptr)
-        , fSpawnCode(kDefaultCode)
-        , fUpdateCode(kDefaultCode) {
+        , fCode(kDefaultCode) {
     this->rebuild();
 }
 
 void SkParticleEffectParams::visitFields(SkFieldVisitor* v) {
-    SkString oldSpawnCode = fSpawnCode;
-    SkString oldUpdateCode = fUpdateCode;
+    SkString oldCode = fCode;
 
     v->visit("MaxCount", fMaxCount);
     v->visit("Duration", fEffectDuration);
@@ -343,55 +343,49 @@
 
     v->visit("Drawable", fDrawable);
 
-    v->visit("Spawn", fSpawnCode);
-    v->visit("Update", fUpdateCode);
+    v->visit("Code", fCode);
 
     v->visit("Bindings", fBindings);
 
     // TODO: Or, if any change to binding metadata?
-    if (fSpawnCode != oldSpawnCode || fUpdateCode != oldUpdateCode) {
+    if (fCode != oldCode) {
         this->rebuild();
     }
 }
 
 void SkParticleEffectParams::rebuild() {
-    auto buildProgram = [this](Program* p, const SkString& code) {
-        SkSL::Compiler compiler;
-        SkSL::Program::Settings settings;
+    SkSL::Compiler compiler;
+    SkSL::Program::Settings settings;
 
-        SkTArray<std::unique_ptr<SkParticleExternalValue>> externalValues;
+    SkTArray<std::unique_ptr<SkParticleExternalValue>> externalValues;
 
-        auto rand = skstd::make_unique<SkRandomExternalValue>("rand", compiler);
-        compiler.registerExternalValue(rand.get());
-        externalValues.push_back(std::move(rand));
+    auto rand = skstd::make_unique<SkRandomExternalValue>("rand", compiler);
+    compiler.registerExternalValue(rand.get());
+    externalValues.push_back(std::move(rand));
 
-        for (const auto& binding : fBindings) {
-            if (binding) {
-                auto value = binding->toValue(compiler);
-                compiler.registerExternalValue(value.get());
-                externalValues.push_back(std::move(value));
-            }
+    for (const auto& binding : fBindings) {
+        if (binding) {
+            auto value = binding->toValue(compiler);
+            compiler.registerExternalValue(value.get());
+            externalValues.push_back(std::move(value));
         }
+    }
 
-        auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
-                                               SkSL::String(code.c_str()), settings);
-        if (!program) {
-            SkDebugf("%s\n", compiler.errorText().c_str());
-            return;
-        }
+    auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
+                                            SkSL::String(fCode.c_str()), settings);
+    if (!program) {
+        SkDebugf("%s\n", compiler.errorText().c_str());
+        return;
+    }
 
-        auto byteCode = compiler.toByteCode(*program);
-        if (!byteCode) {
-            SkDebugf("%s\n", compiler.errorText().c_str());
-            return;
-        }
+    auto byteCode = compiler.toByteCode(*program);
+    if (!byteCode) {
+        SkDebugf("%s\n", compiler.errorText().c_str());
+        return;
+    }
 
-        p->fByteCode = std::move(byteCode);
-        p->fExternalValues.swap(externalValues);
-    };
-
-    buildProgram(&fSpawnProgram, fSpawnCode);
-    buildProgram(&fUpdateProgram, fUpdateCode);
+    fByteCode = std::move(byteCode);
+    fExternalValues.swap(externalValues);
 }
 
 SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
@@ -448,18 +442,18 @@
         }
     }
 
-    auto runProgram = [](SkParticleEffectParams::Program& program, SkParticles& particles,
-                         float updateParams[], int start, int count) {
-        if (const auto& byteCode = program.fByteCode) {
+    auto runProgram = [](const SkParticleEffectParams* params, const char* entry,
+                         SkParticles& particles, float updateParams[], int start, int count) {
+        if (const auto& byteCode = params->fByteCode) {
             float* args[SkParticles::kNumChannels];
             for (int i = 0; i < SkParticles::kNumChannels; ++i) {
                 args[i] = particles.fData[i].get() + start;
             }
             SkRandom* randomBase = particles.fRandom.get() + start;
-            for (const auto& value : program.fExternalValues) {
+            for (const auto& value : params->fExternalValues) {
                 value->setRandom(randomBase);
             }
-            SkAssertResult(byteCode->runStriped(byteCode->getFunction("main"),
+            SkAssertResult(byteCode->runStriped(byteCode->getFunction(entry),
                                                 args, SkParticles::kNumChannels, count,
                                                 updateParams, 2, nullptr, 0));
         }
@@ -496,7 +490,7 @@
         }
 
         // Run the spawn script
-        runProgram(fParams->fSpawnProgram, fParticles, updateParams, spawnBase, numToSpawn);
+        runProgram(fParams.get(), "spawn", fParticles, updateParams, spawnBase, numToSpawn);
 
         // Now stash copies of the random generators and compute inverse particle lifetimes
         // (so that subsequent updates are faster)
@@ -513,7 +507,7 @@
     }
 
     // Run the update script
-    runProgram(fParams->fUpdateProgram, fParticles, updateParams, 0, fCount);
+    runProgram(fParams.get(), "update", fParticles, updateParams, 0, fCount);
 
     // Do fixed-function update work (integration of position and orientation)
     for (int i = 0; i < fCount; ++i) {
diff --git a/resources/particles/default.json b/resources/particles/default.json
index ecc43a5..18b68cf 100644
--- a/resources/particles/default.json
+++ b/resources/particles/default.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 1
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -23,7 +23,7 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = mix(1, 3, rand);",
       "  float a = radians(mix(250, 290, rand));",
       "  float s = mix(10, 30, rand);",
@@ -31,27 +31,8 @@
       "  p.vel.y = sin(a) * s;",
       "  p.pos = text(rand).xy;",
       "}",
-      ""
-   ],
-   "Update": [
       "",
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
-      "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  float4 startColor = float4(1, 0.196, 0.078, 1);",
       "  float4 endColor   = float4(1, 0.784, 0.078, 1);",
       "  p.color = mix(startColor, endColor, p.age);",
diff --git a/resources/particles/explosion.json b/resources/particles/explosion.json
index 2a2035f..791f1d7 100644
--- a/resources/particles/explosion.json
+++ b/resources/particles/explosion.json
@@ -8,7 +8,7 @@
       "Columns": 4,
       "Rows": 4
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -35,31 +35,13 @@
       "  return float2(x, y);",
       "}",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 1.0 + rand * 2.0;",
       "  p.pos = circle() * 60;",
       "  p.vel = p.pos / 3;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.frame = p.age;",
       "}",
       ""
diff --git a/resources/particles/interp.json b/resources/particles/interp.json
index 3d82b6c..b9a5d4e 100644
--- a/resources/particles/interp.json
+++ b/resources/particles/interp.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 2
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -23,31 +23,13 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 2 + (rand * 2);",
       "  p.vel.x = (30 * rand) + 50;",
       "  p.vel.y = (20 * rand) - 10;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.color.r = p.age;",
       "  p.color.g = 1 - p.age;",
       "  float s1 = 0.5 + (1.5 * p.age);",
diff --git a/resources/particles/penguin_cannon.json b/resources/particles/penguin_cannon.json
index e4b28b6..a8e80ae 100644
--- a/resources/particles/penguin_cannon.json
+++ b/resources/particles/penguin_cannon.json
@@ -8,7 +8,7 @@
       "Columns": 1,
       "Rows": 1
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -25,33 +25,15 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 20;",
       "  float a = radians(10 + 60 * rand);",
       "  float s = 140 + rand * 60;",
       "  p.vel.x = cos(a) * s;",
       "  p.vel.y = -sin(a) * s;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.vel.y += 50 * dt;",
       "  p.dir = normalize(p.vel);",
       "}",
diff --git a/resources/particles/snowfall.json b/resources/particles/snowfall.json
index 190454a..0b5c33d 100644
--- a/resources/particles/snowfall.json
+++ b/resources/particles/snowfall.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 1
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -23,32 +23,14 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 10;",
       "  p.vel.y = 10 + rand * 20;",
       "  p.vel.x = -5 + 10 * rand;",
       "  p.pos.x = rand * 500;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.scale = size(p.age);",
       "}",
       ""
diff --git a/resources/particles/spiral.json b/resources/particles/spiral.json
index cff1533..4641c92 100644
--- a/resources/particles/spiral.json
+++ b/resources/particles/spiral.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 2
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -23,33 +23,15 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 2 + rand;",
       "  float a = radians(effectAge * 1080);",
       "  float s = 50 + rand * 10;",
       "  p.vel.x = cos(a) * s;",
       "  p.vel.y = sin(a) * s;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.scale = 0.5 + 1.5 * p.age;",
       "  float3 a0 = float3(0.098, 0.141, 0.784);",
       "  float3 a1 = float3(0.525, 0.886, 0.980);",
diff --git a/resources/particles/swirl.json b/resources/particles/swirl.json
index eb820b7..3063d49 100644
--- a/resources/particles/swirl.json
+++ b/resources/particles/swirl.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 2
    },
-   "Spawn": [
+   "Code": [
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
       "layout(ctype=float) in uniform float effectAge;",
@@ -23,7 +23,7 @@
       "  float  frame;",
       "};",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 1 + 2 * rand;",
       "  p.pos.x = rand * 50;",
       "  float a = radians(-10 + 20 * rand);",
@@ -31,26 +31,8 @@
       "  p.vel.x = sin(a) * s;",
       "  p.vel.y = -cos(a) * s;",
       "}",
-      ""
-   ],
-   "Update": [
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
       "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.vel.x += wind(p.age) * dt;",
       "  p.scale = 3 - (1.5 * p.age);",
       "  p.color = color(p.age);",
diff --git a/resources/particles/warp.json b/resources/particles/warp.json
index f495d19..845814b 100644
--- a/resources/particles/warp.json
+++ b/resources/particles/warp.json
@@ -6,7 +6,7 @@
       "Type": "SkCircleDrawable",
       "Radius": 2
    },
-   "Spawn": [
+   "Code": [
       "",
       "// float rand; Every read returns a random float [0 .. 1)",
       "layout(ctype=float) in uniform float dt;",
@@ -34,31 +34,12 @@
       "  return float2(x, y);",
       "}",
       "",
-      "void main(inout Particle p) {",
+      "void spawn(inout Particle p) {",
       "  p.lifetime = 30;",
       "  p.pos = circle() * 40;",
       "}",
-      ""
-   ],
-   "Update": [
       "",
-      "// float rand; Every read returns a random float [0 .. 1)",
-      "layout(ctype=float) in uniform float dt;",
-      "layout(ctype=float) in uniform float effectAge;",
-      "",
-      "struct Particle {",
-      "  float  age;",
-      "  float  lifetime;",
-      "  float2 pos;",
-      "  float2 dir;",
-      "  float  scale;",
-      "  float2 vel;",
-      "  float  spin;",
-      "  float4 color;",
-      "  float  frame;",
-      "};",
-      "",
-      "void main(inout Particle p) {",
+      "void update(inout Particle p) {",
       "  p.vel += normalize(p.pos) * dt * 10;",
       "  p.scale = mix(0.25, 3, p.age);",
       "}",