On 6 Dec 2013, at 5:46 pm, Graham Cox <graham....@bigpond.com> 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.

—Graham




_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to