What do you do if the amount of interactive items you give Flex to render makes it choke and drop the frame rate to 0.3? This is something we encountered recently at Gigantt, and I thought I'd share our solution to the problem.
The Problem
So let's say you have a few hundred complex interactive items that you want to add to the stage. In our case we sometimes need to fit a very large number of "task" objects into the screen by scaling them down, because that's part of Gigantt's infinite-zoom approach.
Giving Flex too many items to render at once just chokes it. Flex tries to render them all at the next frame and just doesn't make it. It tries to do something called phased instantiation, but most of the time this seems to do more harm than good. You can end up with a Flex application that, instead of rendering at a smooth - let's say, 25 FPS - pauses every once in a while to render a bunch of items, which results in a really choppy UI experience. This becomes especially clear if you use lots of animation.
To be sure, if you constantly need to render more than Flex can do in 1/25th of a second then you're out of luck. But in many cases rendering happens in bursts. This is because one of the more costlier operations you can do with interactive objects in Flex is to add them to the stage. Once they're staged moving them around and even transforming and scaling them is much quicker.
To top that all off, you never know just how much free CPU your customer's machine is going to have. Maybe they're running a bunch of stuff already and there just isn't enough juice left for Flex. So you can't really decide in advance how many items per second you feel comfortable rendering, because this number will vary between machines.
The Solution
What we need is a way to make sure our application never drops below 25 FPS so that everything feels snappy. In the current development version of Gigantt we ended up implementing something we call an idle rendering queue. In essence what we do is divide all our rendering operations into reasonably sized chunks that are rather small. We then measure the application's actual frame rate. As soon as we detect a high enough frame rate we know that Flex is done rendering whatever it worked hard to render and that the engine is free to handle more work. We then take another chunk out from the queue and feed it to Flex. Our application's declared frame rate is 60 FPS, but it never really reaches that number unless your have a strong CPU or you're not rendering a lot of new items. By throttling our rendering jobs according to what the user's machine is able to handle at each point in time we make sure our application is never too heavy for whatever machine is running it. The frame rate remains the same - a minimum of 25 FPS - it might just take it more time to actually show every interactive object. If you implement a ZUI like Google Maps (or Gigantt) you can naturally allow "farther away" elements to render in the background, so to speak.
This isn't ideal. An ideal solution would be something that's able to render as many items as can be discerned on screen, so that if you zoom in you get a continuous impression of the items already "being there". But that's an extraordinarily large number of polygons to render (in the trillions, really). Our approach does the best with what we have by providing a seamless zoom-in effect that takes advantage of the fact that it takes the eye a few hundred milliseconds to actually see what's being rendered on screen before the user can make a conscious decision where next to zoom-in to. This optimization is neat trick that we thought we'd share with the world. You can expect to enjoy it in the next release of Gigantt in a few weeks.
7 comments:
Hello guys,
I stumbled upon your older post which seems to outline some solution that I'm trying to find for our Flex application.
Could you please elaborate on how/where did you implement the "idle rendering queue"? Did you somehow hacked the default LayoutManager in Flex or even created your own one with this rendering queue? Or you came up with some solution which can be applied only to some UI components?
I would be grateful for any deeper insight on this..
Thanks!
Tomas
Hi Tomas,
I created a Gist with the relevant code: https://gist.github.com/assaflavie/7595828
Assaf, thanks a lot!
I will take a look and ask questions if needed..
Hi Assaf,
I have reviewed the relevant code of your solution and I think I understand how your chunking works. But it's related mainly to processing of already queued chunks which I already tried to implement.
But what I particularly wonder is how and when you actually pass those chunks of rendering tasks to your background worker that processes them when there are any free cycles. So could you please also explain that a bit?
Do you somehow apply this chunking to the whole Flex rendering and wiring it up to any custom LayoutManager?
Or do you just apply that only to some custom Flex component?
And how do you apply that in regards of the Flex Component Lifecycle which sticks to some principles and uses invalidation/validation cycles for rendering all Flex components?
Thanks for your hints and comments!
Tomad, I don't use this for anything related to rendering. Only for purely computational jobs that take too long for one frame.
Sorry, *Tomas.
btw, feel free to reach me directly at assaf@ (my company's name, whcih is gigantt) .com
Hi Assaf,
well, I'm quite surprised that you don't use it for anything related to rendering as you refer to rendering in Flex a lot in your post and even call your solution the "idle rendering queue".
So that's why my assumption was that you use it exactly for chunking the rendering operations in Flex when you need to add too many display objects and/or UI components on stage which is, as you pointed out, quite costly operation and causes hiccups of the Flex app.
However, I'm still finding some solution for chunking the rendering operations in Flex.
So do you have any idea or hint how such problem in could be resolved?
I was thinking about wiring some smart chunking into the Flex component invalidation/validation cycle performed by default LayoutManager which can be replaced by custom implementation.
Do you think your chunking solution could be somehow used in custom layout management?
Thanks for any comments..
Tomas
P.S. Feel free to drop me a reply via email tomas(at)flexets.com if that's more convenient for you.
Post a Comment