Re: Coalesced updates and refresh rate
Re: Coalesced updates and refresh rate
- Subject: Re: Coalesced updates and refresh rate
- From: Graham Cox <email@hidden>
- Date: Fri, 24 Jun 2011 14:49:14 +1000
OK, I needed a diversion from serious work and was intrigued to find out how practical it is to animate many instances of an analogue meter and how far you can go with the standard view update mechanism. Think of it as putting my money where my mouth is. I claimed it should be possible to get smooth animation using the standard mechanism if you are careful and play by the rules.
So, here's an app project that attempts to do that.
http://apptree.net/code/LED3.zip
I hope this is reasonably realistic for your particular situation. I have two windows, one which has a bargraph display somewhat like a spectrum analyser and 2 large VU meters, and a second which has 36 smaller instances of the same VU meters. Each of these is an autonomous view, and entirely looks after its own simulation, including the meter ballistics by simulating a RC circuit. These are all animated using a common timer instance running at 30 frames per second. You can opt to set the VU view to be layer backed as well if you want - the four instances on the far left are layer backed and the layer transform is additionally animated. To turn this on, just check the layer backing checkbox in IB - another motivation for this was to get some more experience of using Core Animation - but note that overall I'm not using Core Animation to perform the majority of the animation you see, just a standard timer which updates the view periodically using the standard update mechanism. No threads, no smoke and mirrors.
I measured the drawing time for rendering a single VU meter (large) and it settled down to an average of about 220 µS. This is on my fairly up to date mid range Mac - a 27" iMac with 3.2 GHz i3 CPU and ATI Radeon 5670 graphics, 64-bit debugging build. So, your mileage will vary. But at 220µS, I have time to render about 75 instances of the meter in the 16.7mS timeslot available at 60 fps (though I animate these at only 30 fps which seems adequate for fluid movement). The image used to draw the meter background is a PDF and it does not seem to cache this within the NSImage in another form - whether there is some other caching going on behind the scenes I don't know, but the same image is used for all instances, scaled to fit. I also used a common timer for all instances; that did seem to make a difference - having one timer per instance was slower, with a noticeable slowing of the apparent framerate.
Is it good enough? I don't know. Maybe 36 instances is far fewer than you have - you can easily add more by simply duplicating them in IB. Maybe your hardware is much slower, and of course, this app is not doing anything else in between frames except going to sleep. A real audio processing app would have much more work to do, but in theory it should have plenty of time to do it.
Well, it might give you some starting point or something to compare against. Anyway, it was fun :)
--Graham
On 15/06/2011, at 11:42 PM, Wolfgang Kundrus wrote:
> Thanks for taking the time to respond. I am certainly not a newbie, being a Mac programmer since 1987 having brought three major applications with millions of customers to market that all run cross platform.
>
> That aside, I am trying to understand, why Cocoa does not flush the graphics, if there has been drawn something to the window with the a method that works well otherwise. I understand coalescing updates and I want to stay away from CGContextFlush as this would cause the application to block, if another flush has to happen.
>
> We do use the invalidate/draw wherever appropriate. In this specific base, invalidate does not help, because it is not a simple redraw operation, but rather moving an object by a few pixel without tearing. The other case where we use direct drawing is the potentially hundreds of meter object that need to be updated in a very specific way at a rate that is perceived as fluent.
>
> All the best
>
> Wolfgang
>
>
> 2011/6/15 Graham Cox <email@hidden>
>
> On 15/06/2011, at 10:30 PM, Wolfgang Kundrus wrote:
>
> > We have to update a lot of small onscreen objects and performance is way better, when we travers them outside the Cocoa view tree. If we would use invalidating, we would have to go thru our complete view tree and check for overlaps with the update rect.
> >
>
> Does each object have its own view? If so, then that's definitely not a good idea. If not, then there really shouldn't be a problem - checking for overlap with the update rect is a simple matter of a) invalidating the update rect(s) and b) at DRAW time, call [NSView needsToDrawRect:] to test for overlap. Invariably it is drawing that dominates performance for this type of scenario, so a simple YES/NO test of this type is well worthwhile and is very fast.
>
> If you have, say < 1000 objects that need updating, a linear list of these will probably suffice on a modern machine. If you have more, a spatial hashing scheme such as BSP can give useful improvements, and NSView has the method -getRectsBeingDrawn:count: that can be used as an input to a spatial hash. I use this technique in DrawKit, and unless the objects are especially complex in what they draw, it's easy to achieve a refresh of many thousands of objects within 16mS (which equates to 60fps).
>
> If you have found performance to be bad using Cocoa's drawing mechanism, I suggest you must have overlooked something or made a newbie error such as 1 view per object, because in my experience it is adequate in terms of scheduling the updates and testing for objects needing to be drawn. Cocoa caps the framerate at 60fps anyway so you will never do better than that, but it sounds like you're way off base, so there's a long way to go before giving up on the standard mechanism.
>
> --Graham
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden