- Edited
Starling AS3 Performance Question
Hi there, I've just starting integrating Spine into a Starling 2.0 AS3 project and I've quickly encountered some odd crippling performance issues.
The desired usage of Spine in my game is as follows: I have around 9 different skeletons for which I have a few quick animations. All of these skeletons will spend 98% of their time sitting on screen in the empty state with no internal movement whatsoever. When in this empty state, I will have multiple instances (perhaps 10 of each) on screen being moved (again not animating, simply moving the composite object around the screen). Once movement stops, we will reduce the display count to a handful (tops of around 10), and then run an animation on each.
The issue I'm having is that when I have a larger number moving on screen (again, not animating), I get a crippling reduction in the frame rate (on my desktop I go from 60fps down to around 14-16). Checking out adobe scout, it appears that SkeletonSprite's render function is taking up around 80% of the frame's time. This seems very odd to me as internally nothing has changed within the animation, it should simply be drawing a number of flat images each frame.
Again I am new to the system so I hope I'm simply missing something... Each skeleton is a simple SkeletonAnimation instance added to the display tree after calling skeleton.state.setEmptyAnimation(0, 0). Additionally all art is coming from the same texture and I have a minimal draw call count.
Any help of suggestions would be much appreciated, thanks!
It's surprising you are seeing such a slowdown, especially when you don't have any animations playing. You can try not calling Skeleton updateWorldTransform
when no animations are playing, though if you see most of the time in SkeletonSprite#render it might not help much. To do that, copy and rename SkeletonAnimation, avoid the updateWorldTransform when there are no animations, and use that class instead of SkeletonSprite.
Can you post a screenshot of the Metrics view for some or all of your skeletons?
If Starling provides some mechanism to write to a texture buffer, then you could replace your skeleton rendering with static textures when they are not being animated.
I've tried commenting out the entire advanceTime function and it doesn't change anything. The render function still struggles. I've attached the Metrics view for the more complex of the two skeletons I have atm. Not much going on it seems but let me know if anything stands out... I'm going to spend some time looking through your render function and see what I can do.
Thanks Nate
Hmm, that doesn't look excessive to me. Maybe we should ask Daniel Sperl of Starling fame for some input.
So I managed to greatly improve upon the performance by extending the SkeletonAnimation class to better manage the state between animating or not animating. Whenever setEmptyAnimation is called we move into the not animating state which makes use of a MeshBatch object to greatly reduce the render time. My extended class overrides the render function of SkeletonSprite and, if we are not animating, simply renders the MeshBatch object instead of unnecessarily running the entire render code. When my new class is created I build this MeshBatch instance by simply running the render code and instead adding each sub image to the MeshBatch object. I still get a bit of a frame rate drop but now instead of 60 -> 15 I get a 60 -> 50 and we maintain the draw call count. My implementation works fine for my scenario (as I want better SkeletonAnimation functionality anywho) but someone with better project scope might want to consider getting this concept added into Spine's Starling runtime
Interesting. I think it's somewhat rare to have skeletons that aren't animating, but I suppose there are use cases. The thing is, it's easy for the application to know the skeleton has changed, but a bit hard for the skeleton itself to know. Maybe we could have an API for marking the skeleton as not dirty? By default it would always be dirty, if you set it to not dirty then it won't recompute things for render. You are storing the computations done by render by using MeshBatch for later reuse? It's not clear how cleanly this could be implemented without affecting the typical, animating use case. What do you think, badlogic?
I think a user settable dirty flag is Ok. I'd refrain from internal dirty flags as those tend to break in funny ways and usually require additional computation time that might not be offset by saving other computations. I've opened an issue here [runtimes] Explicit setter to mark Skeleton as not dirty to prevent world transform updates · #918
Just to reiterate, commenting out all of the world transform code had no effect on my performance issue. What helped was redoing the render code to not redraw every layer over and over again when nothing was changing internally. Another quick lesson I learned was that the MeshBatch object doesn't like getting flooded with vertices data. I was able to achieve no frame rate reduction whatsoever after I re-exported my spine project's empty animation states to hide all of the vertex data (just leaving the 4 vertices for each visible image).