Re: Threaded drawing
Re: Threaded drawing
- Subject: Re: Threaded drawing
- From: Roland King <email@hidden>
- Date: Fri, 06 Dec 2013 19:14:02 +0800
That's very ingenious. Not an OSX expert myself but it does bother me that you're not drawing into the context you are given but one you construct yourself from a piece of the window. That's not something I think iOS would even let you get at.
For a start does that work layer backed?
Is there always a guaranteed correspondence between the context passed to drawrect and the window like that? Does OSX never choose to render to a bitmap and then blend to the window store later?
That might be another way, by the way, render to your own tiled small bitmaps on background threads then blit them into the real context.
Perhaps that code is fine on OSX, I'd certainly be happier if there was a create context from context call you could use to relate your small contexts to the draw rect one. That code would keep me up nights.
> On 6 Dec, 2013, at 18:26, Graham Cox <email@hidden> wrote:
>
>
>> On 4 Dec 2013, at 9:33 pm, Graham Cox <email@hidden> wrote:
>>
>> But that leaves those annoying cases when you have the whole view to redraw. I wondered if it would be worth dividing up the view into rects and rendering each one on a separate thread. The problem seems to me to be that they’d all be drawing into the same CGContext, and I wonder how well that could work - e.g. one thread could set a clip ready for its next drawing operation and another could then change that clip so they’d all be tripping over each other, even though they were all drawing into a different part of the context. If access to the context were synchronised, then that would end up serialising all the drawing so there wouldn’t be any gain.
>>
>> Has anyone trod this path? It would be useful to know whether there’s anything that can be done along these lines, because rendering 10,000 or more objects is just taking too darn long!
>
>
> OK, after some thought and a bit of experimentation, I think I’ve got a handle on this. I just thought I’d share with the list in case anyone is remotely interested, or has anything to suggest/add/criticise…
>
> Obviously, you cannot share a graphics context across multiple threads for the reasons I mentioned - a single context cannot be thread safe, by design. But that doesn’t mean you can’t have a separate context per thread, all drawing into the same backing store. That was my breakthrough insight I guess, for what it’s worth.
>
> So proceeding on that basis, I wrote a test view that uses a NSOperationQueue to dispatch chunks of drawing work by dividing up the view into tiles, creating a context for each tile (but all drawing into the same window backing store). As long as you wait for the tiles to all finish, everything works just tickety-boo.
>
> Here’s the code, which is the -drawRect: method of a view. The view creates an NSOperationQueue called mDrawingQueue in its -initWithFrame: method.
>
> #define TILE_SIZE NSMakeSize( 100, 100 )
> #define DRAW_THREADED 1
>
>
> - (void) drawRect:(NSRect) dirtyRect
> {
> [[NSColor whiteColor] set];
> NSRectFill( dirtyRect );
>
> // get current context:
>
> CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
>
> // divide into tiles
>
> NSSize tileSize = TILE_SIZE;
> NSRect br = self.bounds;
> NSUInteger tx, ty;
>
> tx = ceil(NSWidth( br ) / tileSize.width);
> ty = ceil(NSHeight( br ) / tileSize.height);
>
> NSRect tileRect;
>
> tileRect.size = tileSize;
>
> for( NSInteger i = 0; i < ty; ++i )
> {
> for( NSInteger j = 0; j < tx; ++j )
> {
> tileRect.origin.x = br.origin.x + tileSize.width * j;
> tileRect.origin.y = br.origin.y + tileSize.height * i;
>
> if([self needsToDrawRect:tileRect])
> {
> #if DRAW_THREADED
> NSBlockOperation* op = [NSBlockOperation blockOperationWithBlock:^
> {
> NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithWindow:[self window]];
> CGContextRef ncx = [context graphicsPort];
>
> // align and clip this new context to the view
>
> CGAffineTransform ctm = CGContextGetCTM( ctx );
>
> CGContextConcatCTM( ncx, ctm );
> CGContextClipToRect( ncx, br );
>
> // also clip to the tile (not strictly necessary)
>
> CGContextClipToRect( ncx, tileRect );
>
> [self drawTile:tileRect inContext:ncx];
> }];
>
> [mDrawingQueue addOperation:op];
> #else
> [self drawTile:tileRect inContext:ctx];
> #endif
>
> }
> }
> }
>
> #if DRAW_THREADED
> // need to wait until tiles all drawn before flushing
> [mDrawingQueue waitUntilAllOperationsAreFinished];
> #endif
> }
>
>
>
> - (void) drawTile:(NSRect) tile inContext:(CGContextRef) ctx
> {
> // draw something in the tile. Not very challenging at the moment
>
> NSRect tr = NSInsetRect( tile, 10, 10 );
>
> CGContextSetFillColorWithColor( ctx, [[NSColor redColor] CGColor]);
> CGContextFillRect( ctx, tr );
>
> CGContextSetStrokeColorWithColor(ctx, CGColorGetConstantColor( kCGColorBlack ));
> CGContextStrokeRect( ctx, tile );
> }
>
>
> _______________________________________________
>
> 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
_______________________________________________
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