Re: Getting the best frame rate for NSView drawing
Re: Getting the best frame rate for NSView drawing
- Subject: Re: Getting the best frame rate for NSView drawing
- From: Graham Cox <email@hidden>
- Date: Wed, 28 Mar 2012 19:19:05 +1100
On 28/03/2012, at 6:36 PM, Kenneth Baxter wrote:
> I am trying out some different ideas for animating portions of a view, but am having problems with performance.
>
> Essentially I have an animation running using a CABasicAnimation, and when it calls back to my view, it invalidates the rect where the object was drawn, and the new place where it is going to be drawn. Then in my drawRect code, I ask each of my objects that overlap the dirty rect to redraw itself.
>
> For testing purposes, I have a view that is 5,000 x 5,000 and have 100 of my objects which are placed randomly and size 200 x 400.
>
> For each object, I save the graphics state, transform my coordinates, clip to the bounds of the object, draw it, and then restore the graphics state.
>
> At the moment, on my test machine (which is admittedly not the latest and greatest, but it probably representative of our typical customer), I have tried the following scenarios:
>
> Draw each object individually (admittedly it's just a simple oval in rect which is filled and stroked): 12fps
> Generate an NSImage of the object once and cache it, then just drawInRect...: 8fps
> Cache a CIImage and draw that: 8fps
> Cache a CGLayer and draw that: 8fps
>
> This sure does seem to be awfully slow. Instruments tell that the time is all being spent in drawing the object, no matter how I do the drawing.
>
> Is there some better way to get decent drawing performance from drawing into an NSView?
200 x 400 is quite a big area to fill, whether by using stroke/fill or blitting an image. Multiply by 100 and you are doing a lot of work pushing pixels (equivalent to blitting 8 million pixels!). Though I'm surprised that the NSImage method is slower than fill/stroke, in most cases I would expect that to be faster, so there might be an inefficiency there somewhere.
The only way to get faster drawing is to avoid doing any drawing you don't need to - it's always worth it. So for example if your objects overlap (they surely do) then avoid drawing anything except the topmost object which finally sets the pixels you see. Also, using the rect parameter to -drawRect can end up doing more drawing than necessary because it's actually the union of any disjoint or non-rectangular dirty regions. You could use -getRectsBeingDrawn:count: or -needsToDrawRect: instead to ensure you really keep the set of drawn objects to a minimum.
The next step, once you have the minimal set of objects to draw, is only to draw the pixels that have changed. Depending on your animation, it might not be many - for example, if a solid object moves, only its leading and trailing edges have actually changed, though of course for anything non-trivial that might not help at all. If you really need to animate this many objects this large, you are going to have to get clever to reduce the pixels involved. I expect using CABasicAnimation and invalidating most of the view is not smart enough - you'll need to track each object and how it has moved so that you invalidate strictly what area changed. Combined with -needsToDrawRect: you should see a big gain in performance.
You might also get some improvement using CALayer to draw the layer content rather than a view. This ultimately uses OpenGL to blit the pixels as a texture, though capturing the image in the first place is just as slow if you must redraw it every time. However, to just move a static graphic around, CALayer will cache the image and not need to ever redraw it, so you'll see a massive speedup for that case. You could use a CALayer for each object, and just animate their x,y positions. The speed boost will be huge.
--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