Re: Threaded drawing
Re: Threaded drawing
- Subject: Re: Threaded drawing
- From: Mike Abdullah <email@hidden>
- Date: Mon, 09 Dec 2013 16:01:27 +0000
On 9 Dec 2013, at 15:47, Graham Cox <email@hidden> wrote:
>
> On 6 Dec 2013, at 5:46 pm, Graham Cox <email@hidden> wrote:
>
>> OK, I’ve now tried this approach, and it’s much cleaner in that it works with scrollers, with and without “responsive” scrolling (which appears to buffer its contents) and also zooming. Code follows. In this case, drawing overall is slower than the normal case, because the simple drawing I’m doing doesn’t tax things much, so the set up and tear down of the bitmaps is dominating, but I would expect for very complex drawing it would show a win.
>
>
>
> I think I’ve explored this as far as I can go. Here’s my wrap-up, for what it’s worth to anyone. Not a lot, I expect.
>
> The conclusion is, I don’t think it can be done with the current graphics APIs with any worthwhile performance. Here’s my summary of why that is. I really hope someone who knows the graphics innards could take a look and see if I’ve overlooked anything, because in principle this *could* dramatically improve drawing performance *if* there were some support for doing it.
>
> GOAL: to improve drawing performance in a ‘heavy' view by tiling the visible rect of the view and rendering each tile on a separate thread. By ‘heavy’ I mean the view has thousands of objects to draw, which ultimately make a huge number of Core Graphics calls.
>
> (n.b. in my previously posted code, I tiled the bounds of the view, which is not quite the same as what I’m talking about here, which is tiling the visible rect of the view in such a way that each tile renders the scaled, translated view content into a fixed backing store region. Having solved that problem, I don’t think it’s worth posting the code because overall this technique doesn’t gain any performance).
>
> APPROACH: Each tile is used to construct an offscreen bitmap and a context that wraps it. The context is set up so that normal drawing (just as if it were done by -drawRect:) will render into the tile context. Because each tile has its own context, each one can be executed on a separate thread. In principle this should show a drawing performance boost because different non-overlapping parts of the view are drawn in parallel.
>
> After capturing the tile content, the resulting image is copied back into the original current context as part of -drawRect:
>
> This last step is where it all falls down, because this one call, to CGContextDrawImage, takes a whopping 67% of the overall time for drawRect: to run, and normal drawing doesn’t need this call (this is testing in a ‘light’ view, but nevertheless, it makes the view very noticeably laggy).
>
> However, it’s the only workable approach I’ve managed to discover, so that’s why I’m stuck.
>
> ALTERNATIVES THAT WOULD WORK, IF ONLY:
>
> Because the final drawing of the image takes so long, if that could be avoided then the threaded drawing would probably be a win. Here’s what I tried:
>
> 1. Make one big bitmap instead and create a context for each tile that represents just a portion of it. This doesn’t work because the tile width and the bytesPerRow are not consistent with an image that has an exclusive context for the entire bitmap. Attempting to create the context fails because of this, even though the byte offset between rows is actually correct. Essentially, CGBitmapContextCreate() does not trust your bytesPerRow calculation, even when it’s right. (I say crash and be damned rather than assert here). Even if this worked, it would still require an image draw, but at least it would be just one, not one per tile.
>
> 2. Make one big bitmap + context and set each tile to focus on one portion of this at a time. This doesn’t work because each thread must have its own context so that context state is exclusive per thread.
>
> 3. Make a copy of the current context and focus it per tile. This doesn’t work because there is no API to copy a context, and/or to share the backing store of an existing context.
>
> 4. Create a tile context from the window’s underlying backing store. This works for simple views, but does not work with scrollers or other more complex views that use some intermediate buffering.
>
> I’ll bet there’s some private API that would help here (NSScrollView appears to be doing something along these lines for ‘responsive’ scrolling) but as usual Apple are keeping it out of the hands of us plebs. Even back in the old days of GWorlds on Mac OS 7.x and later you could do this sort of thing much more easily than now.
>
> BOTTOM LINE:
>
> Creating multiple contexts that draw into a single shared backing store is currently not possible. This precludes drawing on multiple threads and so ultimate drawing performance is unattainable.
Maybe a dumb question: How about using CATiledLayer?
_______________________________________________
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