On 4 Dec 2013, at 9:33 pm, Graham Cox <graham....@bigpond.com> 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 (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