I want to make animated SVG graphics (as in having a single SVGSVGElement and animating attributes of thousands of descendant elements). For example, make I want to make a force-directed graph with thousands of nodes.
It is nearly impossible to do this by animating SVG elements.
In contrast, I can use a library like Two.js to achieve the same things. In fact, Two.js has an SVG interpreter that can read an SVG from DOM and create a drawing from it which can then be animated with SPEED.
Why is the native SVG implementation so slow compared to concepts like Two.js.
Here's three examples, comparing native SVG, Pixi.js, and Two.js. Both Pixi and Two win, while SVG is the slowest:
On 7/15/17 9:28 PM, Joseph Orbegoso Pea wrote:
> I want to make animated SVG graphics (as in having a single SVGSVGElement and animating attributes of thousands of descendant elements). For example, make I want to make a force-directed graph with thousands of nodes.
> It is nearly impossible to do this by animating SVG elements.
> In contrast, I can use a library like Two.js to achieve the same things. In fact, Two.js has an SVG interpreter that can read an SVG from DOM and create a drawing from it which can then be animated with SPEED.
> Why is the native SVG implementation so slow compared to concepts like Two.js.
First, a clarification: it looks like Two.js is just a layer of
abstraction on top of the native SVG implementation -- it's not a
different thing, as far as I can tell (you can see what it's doing if
you right-click one of its circles and choose "inspect") Also, your own
"native SVG" animation isn't that much more bare-metal than the others
-- it's animating in JS (with requestAnimationFrame). So, all of the
examples here are using JS-driven animation.
The key difference for performance is *what attribute you're animating*
to move the circles around. Your two.js example is animating the
"transform" attribute, and browsers have optimizations for
transform-only changes (by putting the transformed thing into its own
"layer" with some dedicated graphics memory, so that we can simply tweak
that layer rather than repainting from scratch when it moves).
In your "native" example, you're animating the cx and cy attributes, and
those do not have that optimization -- so we have to redraw all of the
circles from scratch on every movement. (I don't know offhand if that's
something we could optimize like "transform" -- it's possible we might
be able to.)
So I expect that if you adjust your "native" demo to use the transform
attribute, you'd get better performance. If there's still a signifcant
benefit to using Two.js after that, you could drill in using devtools to
see what Two.js is doing under the hood in more detail -- it's likely
that the Two.js developers have studied how to structure their
animations to get the best results from browsers.
Looks like the SVG version here is still using "cx" / "cy".
Though if I change it to use transform, it looks like (on my machine at
least) we run up against some limit and fail to allocate new graphics
textures until I close that tab -- I see chunks of the page disappearing
and logging to my terminal like the following:
[GFX1-]: Failed 2 buffer db=0 dw=0 for 0, 0, 240, 240
I don't know what exactly is happening under the hood in the
(canvas-based) Two.js / Pixi.js examples -- and FWIW, they're a bit
janky on my system too, though not quite as janky.
Anyway -- I don't have a solid answer for you about the difference,
except for the following (based on some quick performance profiles that
I made [and you can too!] using the gecko profiler extension from
https://perf-html.io): - In your SVG demo without 'transform', we spent all of our time
repainting after each move.
- With 'transform', we seem to run up against some limit of layers (on
my machine at least), as noted above, and fall over.
- I presume the faster canvas-backed examples are using some
lighter-weight shim above the hardware to avoid these problems, since
they don't spend anywhere near as much time in rasterization -- though
I'm not sure about the details.
Basically, our SVG implementation is not designed with this "thousands
of things moving around at once" use-case as a primary target. It's
pretty good at rendering static content, and it's good at rendering some
limited amount of moving content at once -- and even better if that
stuff is moving with "transform", which lets us allocate a dedicated
layer for the moving thing. (But that only works up to a certain
point.) If your sort of example were a common use-case, it's likely we
would have invested more time in optimizing for it. :) But as it stands,
SVG doesn't get a ton of developer resources right now.
It's possible that the performance will be better with WebRender,
though! (WebRender is a next-generation GPU-based painting engine on the
tip of the rendering pipeline, used in our research browser engine Servo
& which might make it into Firefox): https://github.com/servo/webrender
I think WebRender's SVG capabilities are somewhat limited at the moment,
though this might get a lot better once those are fully baked.