Performance Tips n' Tricks wiki page
richard.bair at oracle.com
Mon Jun 3 16:26:19 PDT 2013
Dan, Thanks for the thoughts / questions, I've added them to the tail of the wiki for processing.
> 1) turning cache to true and cache hint to SPEED I haven't seen explained what the drawbacks are to doing this. I assume there is some trade-off for turning this on otherwise it would be on by default?
Caching is the process of rendering a node (or a node & its children) into a texture, and then render from texture each time the node needs to be rendered. In cases where you set things up once and do a lot of subsequent rendering, this is a win. In cases where you have to reconstruct the texture (or draw to the texture) every time, this is a loss. There is some point in the curve where the cost savings is worth it.
So once you have a cached image, the question is, when do you need to reconstruct it? Clearly when the node or any of its children have changed visual appearance (such as the fill of a rectangle or the visual state of a button when pressed) then you need to regenerate it (if you didn't, then instead of using the cache property you would use an ImageView and renderToImage to create a static view of some nodes). But you also have to regenerate it as the node is scaled or rotated -- at least, you have to if you care about visual quality more than animation speed.
The cache hints tell us under what circumstances we can cheat on quality for the sake of performance. QUALITY says "never cheat". ROTATE says "cheat on rotations". SCALE says "cheat on scales". SCALE_AND_ROTATE can cheat on both and SPEED cheats on everything and everything it can think of to get good performance.
The one thing that will never be cheated on is if the visual state (such as the fill) has changed, then an image will be redrawn.
So as long as you have a scene graph which isn't changing much, you can cache it and animate it for better performance than if you just animate it. For simple things this shouldn't make any difference, but if rendering times are getting long, it is something to look at. Used incorrectly, it can make things worse -- much worse. On the JavaOne scheduler app we did last year for JavaOne we made things faster by turning off caching, because in our particular case the memory pressure and changes in the UI were making things worse by using caching rather than better.
> 2) AnimationTimer - how is this related to transitions (like TransitionTimer). They seem to be two very different things - not just at the usage level but the actual underlying performance and implementation - but it's not clear to me exactly the difference. I wanted to work with both AnimationTimer stuff and Transitions in a consistent way (e.g. pause one thing to pause my whole app). When trying to use the API my instinct was that the transitions used an underlying AnimationTimer, so I was looking for a way to setAnimationTimer on the transitions, so they all shared the same instance. I'm obviously seeing it wrong, but it's not clear to me why these things are so different.
I assume TransitionTimer is just Translation :-)
They are quite different. Timeline and Transition are both Animations. They are defined by having some parameters that define an animation, and then the ability to play, cycle, reverse, etc.
The AnimationTimer on the other hand is a timer that is called whenever the next step in an animation should occur. Games are typically written around a game loop, which is what the AnimationTimer is designed for. Whenever the AnimationTimer or an Animation is running, it causes us to process a pulse ever 16.66ms (or as close as we can get). The only other time a pulse happens is when the scene graph is made dirty.
Both are driven off the same essential timing mechanism -- we get a pulse, and the first step of the pulse is to run the animations (see QuantumToolkit.java around line 594, in the pulse(boolean collect) method). After the various animations run (and this part of the code is more convoluted than is needed any longer, having its history way back in 1.0 with the differences between SE and ME), then the pulse is sent to the stages & scenes where CSS, Layout, synchronization to the render tree, etc is performed.
AnimationTimer is "lighter weight" only in that there is no machinery to run -- it is just given a timestamp and that's it. The Timeline and Transition classes need to do a bunch of stuff on your behalf such as discover what KeyFrames to run, what KeyValues, pump the time into an Interpolator to get an interpolated time, etc etc. But fundamentally I have not yet seen any great overhead spent in the animation classes and would consider them perfectly safe for any usage -- iOS, embedded, or Desktop. They could probably be made evil but I have not seen such a thing yet.
> 3) If I have an app with multiple views (say 20 or 30) and I want to animate between them (slide in, out, etc). Should I have all the views already added to the scene and then just move them off-center (and/or make them invisible), or should I add and remove them at the start and end of each animation. I'd assumed that adding and removing was the way to go for performance, is this correct? The question really is: If a node is in the scene graph but not visible does it add much overhead to rendering, picking, etc? Doing something like Mac's Mission Control would be much easier if I didn't have to add/remove and could just zoom, but is this a bad idea from performance perspective?
For the most part adding / removing from the scene is more expensive than toggling visibility, and toggling visibility is more expensive than setting opacity to 0. At least, that's what I determined a couple weeks ago but I could be wrong. And it depends on what is happening to the memory on your system. If you're hauling around 400MB of nodes you don't need to see, then things might be worse than creating them when needed. I'm not sure but my feeling is that these cases are going to be a lot of "it depends".
> 4) Is there much of a hit in building a view full of nodes and then throwing them away and rebuilding on each showing of that view? In my apps (browser style) I would build all the views on startup and then reuse these by loading different data into them (e.g. the same instance was used for showing Person1 details as Person2, just loading different values into it). I thought this would be the best from performance, but there was a comment from Richard a while back that suggested it was perfectly ok/performant to throw away and build the page again on each showing? This would be much easier from an animation point of view as animating the transition 'back' or 'forward' between two pages of the same view is problematic when you are reusing the same view for both!
We know we have a problem with Node creation time, but we don't know what it is yet. So for now probably reusing the same nodes (when reasonable, see above) is probably going to be better for you.
> 5) Is there much overhead to a view that it is in memory but not actually added to the scene. i.e. if I do end up building a new instance of the view for every "page load" (as per question 4), then it would be extra handy to just keep those views in memory so when the back button is hit I don't have to rebuild them. I assume the memory would build up pretty quickly if there were large tables, images, videos, etc? How does a web browser manage to cache all these pages in history and would the same approach be performant for JFX?
There is no per-pulse overhead, as only Scenes and Stages get pulse notification. The nodes probably don't have peers (but they might in the future if they don't now in all scenarios -- wouldn't bet one way or the other). So from a memory perspective they're still there. But there is no layout, no CSS, no picking, etc.
More information about the openjfx-dev