From 91cef4b43637fc70e2e03cd440aef85a7fa0c18d Mon Sep 17 00:00:00 2001 From: towneh <25694892+towneh@users.noreply.github.com> Date: Sun, 24 May 2026 15:10:14 +0100 Subject: [PATCH 1/6] docs: add scalable avatar dynamics analysis and BasisAuthoredMotion design spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem analysis for the per-avatar cosmetic Animator scaling cost at high CCU, and the design spec for BasisAuthoredMotion — a data-only SDK component plus a batched Burst job system (mirroring RemoteBoneJobSystem) that replaces the Animator for authored secondary motion on non-humanoid transforms. --- BasisAuthoredMotion-Design-Spec.md | 231 ++++++++++++++++++ ...-Dynamics-Animator-Performance-Analysis.md | 123 ++++++++++ 2 files changed, 354 insertions(+) create mode 100644 BasisAuthoredMotion-Design-Spec.md create mode 100644 Leona-Dynamics-Animator-Performance-Analysis.md diff --git a/BasisAuthoredMotion-Design-Spec.md b/BasisAuthoredMotion-Design-Spec.md new file mode 100644 index 000000000..ee36f72fe --- /dev/null +++ b/BasisAuthoredMotion-Design-Spec.md @@ -0,0 +1,231 @@ +# `BasisAuthoredMotion` — native authored-motion driver (design spec) + +**Problem.** Basis avatars commonly drive *cosmetic and secondary* motion — looping idles, tail and ear movement, spinning accessories — with a Unity Animator. Each Animator carries high *fixed* per-instance overhead (Playable-graph setup and evaluation, state-machine and transition processing, the per-Animator update pass) that is independent of how trivial the motion is, and it is paid on **every replicated copy of every avatar in the instance**. That overhead scales linearly with player count, so at high CCU it becomes a substantial share of frame time: a 1000-CCU load test measured a single avatar's cosmetic Animator at roughly **1/5 of scene frame time**. The motion itself is trivially simple — the cost is almost entirely Animator machinery, not animation. This doc proposes what to build instead. (Background measurement and the options weighed: see the analysis doc in the appendix.) + +**Names are provisional** (`BasisAuthoredMotion`, `BasisAuthoredMotionSystem`) — final naming is the maintainer's call. + +**Scope.** This is a general facility for **authored, deterministic dynamic movement on transforms the humanoid rig and IK don't drive** — non-humanoid *rigged* bones (tail and ear chains, extra bones) and standalone accessory objects. The boundary is the humanoid/IK-driven, networked skeleton — *not* bones in general, so rigged chains like tails are squarely in scope. It's a third category of avatar motion: the rigged skeleton is the primary networked/IK-driven pose, jiggle physics adds *emergent* physics-driven follow-through, and this is the *authored* motion the avatar (or prop) declares. The three **stack rather than compete** — authored motion supplies the animated base and **jiggle physics layers on top of it** (see *Jiggle physics ordering*). Cosmetic looping idle sequences or secondary animations (tail flicks, ears twitching, accessories spinning) are among the first use cases driving this, not the limit: any set-sequence or continuously-animated movement on those non-humanoid transforms belongs here. + +--- + +## What we're building + +**One deliverable**, in two co-shipped pieces: + +1. A whitelisted, **data-only** SDK component (`BasisAuthoredMotion`) the avatar carries — pure serialized configuration, no per-instance runtime `Update`. +2. A **batched, Burst-compiled job system** (`BasisAuthoredMotionSystem`) that evaluates every registered avatar's movements in one parallel pass per frame and writes the target transforms directly — no Playable graph, no state machine, no per-instance dispatch. + +All runtime work happens in the shared job; the component just declares what to do. It mirrors the existing `RemoteBoneJobSystem` (`Packages/com.basis.framework/Drivers/Remote/BasisRemoteBoneDriver.cs`), which already proves the batched-transform pattern at thousand-avatar scale. Optimization is a hard requirement, not a later pass (see *Performance requirements*). + +--- + +## Authoring surface — the component + +A whitelisted SDK component following the established `BasisParameterDriver` pattern (`Packages/com.basis.sdk/Scripts/BasisParameterDriver.cs`) — a serializable array of reusable, parameterised **movements**. These are general primitives, not avatar-specific behaviours: the same set drives a tail, hair, antennae, ear, cloth strip, halo, orbiting gem, blink, or ambient micro-gesture. Any avatar declares whatever combination it needs. + +```csharp +public class BasisAuthoredMotion : MonoBehaviour // names provisional +{ + public Movement[] movements = Array.Empty(); + + [Serializable] + public class Movement + { + // Open, extensible set — new kinds slot in without changing the + // registration / scheduling model (see "Extensibility"). + public enum Kind { Oscillate, Rotate, Orbit, RandomSelect, Sequence } + public enum Channel { Rotation, Position, Scale } // what Oscillate drives + + public Kind kind = Kind.Oscillate; + public string label; // author-facing identifier only + public bool enabled = true; // runtime-toggleable (see open question on toggles) + public Vector3 axis = Vector3.up; // local axis the movement acts about + + // Oscillate — periodic (sine) motion on `channel`, optionally propagated + // down a chain to form a travelling wave (1 entry = simple sway). + public Channel channel = Channel.Rotation; // amplitude unit: deg | metres | scale-factor + public Transform[] chain; + public float amplitude = 15f; + public float frequencyHz = 0.5f; + public float phase = 0f; + public float chainPhaseStep = 0f; // phase delay per element down the chain + public float chainFalloff = 1f; // amplitude scale per element down the chain + + // Rotate — constant angular velocity about `axis`, in place. + public Transform target; + public float speedDeg = 36f; // deg/sec + + // Orbit — revolve `target` around `pivot` at `radius` (not a spin-in-place). + public Transform pivot; + public float radius = 0.1f; + public float orbitSpeedDeg = 90f; // deg/sec around the pivot + + // RandomSelect — on a randomised interval pick one weighted option, ease in/out. + public Transform selectTarget; + public Option[] options = Array.Empty