On 6 Dec 2013, at 2:42 pm, Roland King <r...@rols.org> wrote:

> 
>> 
>>> 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. 
>> 
>> Yep, that was also on my list to try, but for top performance it would make 
>> sense not to do that if it can be avoided.
>> 
> 
> My WAG would be that works well enough. Heavy lifting on the threads, then 
> toss a bitmap/CGImage copy to the main thread where I'd hope it was just a 
> bitblt into the context. I've seen similar recommended for iOS but only with 
> a fullsize bitmap, not with bits and pieces. In most of those cases the view 
> was an image and the contents was constructed on a background thread and then 
> set into the image outside the drawrect call. 


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 also tried to create one big bitmap into which all the different contexts 
drew. That worked to a degree, but it seems that when you create a context that 
wraps a block of memory, the first thing it does it to clear it to some 
background colour. That’s annoying, because you end up with some blank areas 
where a later thread cleared the bitmap that an earlier thread had already 
drawn. If just creating the context didn’t do this clearing, it would probably 
work fine and be a lot more performant, because the bitmap and image would not 
need to be created on the fly, and afterwards you can just blit the update 
rects back to the main context. It would be useful if some graphics gurus (Ken 
Ferry??) could chip in and comment on whether this conclusion is correct, and 
if there’s a workaround.

Also, to answer my earlier question, blocks capture the values of variables 
when they’re created, not when they’re run, so that makes life much easier as 
well.

The next step will be to make the tiles themselves represent the visible rect 
of the view, and the context factor in the zoom scale in such a way that 
drawing paths and so on always rasterizes to the native resolution of the 
screen. At the moment zooming in shows pixelization because the tile bitmaps 
are scaling with the view frame. Need some more thought on how to do that.




- (void)                drawRect:(NSRect) dirtyRect
{
        NSTimeInterval  time = [NSDate timeIntervalSinceReferenceDate];
        
        // get current context:
        
        CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];

        CGContextSaveGState( ctx );
        
        const CGRect* rects;
        NSInteger count;
        
        [self getRectsBeingDrawn:&rects count:&count];
        
        CGContextClipToRects( ctx, rects, count );

        // 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:^
                                {
                                        // make sure retina screens taken into 
account
                                        
                                        NSRect backing = [[self window] 
convertRectToBacking:tileRect];
                                        
                                        // create a bitmap image for the tile
                                        
                                        CGColorSpaceRef cspace = 
CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
                                        
                                        NSUInteger      bytesPerRow = NSWidth( 
backing ) * 4;
                                        size_t bytes = bytesPerRow * NSHeight( 
backing );
                                        
                                        void* bitsPtr = malloc( bytes );
                                        
                                        CGDataProviderRef provider = 
CGDataProviderCreateWithData( NULL, bitsPtr, bytes, NULL );
                                        CGImageRef tileImage = CGImageCreate( 
backing.size.width, backing.size.height, 8, 32, bytesPerRow, cspace, 
(CGBitmapInfo)kCGImageAlphaPremultipliedLast, provider, NULL, false, 
kCGRenderingIntentDefault);
                                        CGDataProviderRelease( provider );
                                        CGColorSpaceRelease( cspace );
                                        
                                        // create a context to draw into
                                        
                                        CGContextRef    ncx = 
CGBitmapContextCreateWithData( bitsPtr, backing.size.width, 
backing.size.height, 8, bytesPerRow, cspace, 
(CGBitmapInfo)kCGImageAlphaPremultipliedLast, NULL, NULL );
                                        
                                        // apply scaling for user space to 
backing
                                        
                                        CGAffineTransform tfm = 
CGAffineTransformMakeScale( backing.size.width/tileRect.size.width, 
backing.size.height/tileRect.size.height);
                                        tfm = CGAffineTransformTranslate( tfm, 
-tileRect.origin.x, -tileRect.origin.y );
                                        CGContextConcatCTM( ncx, tfm );
                                        
                                        // draw some content in the tile
                                        
                                        [self drawTile:tileRect inContext:ncx];

                                        CGContextFlush( ncx );
                                        CGContextRelease( ncx );
                                        
                                        // blit the result back to the main 
context. This must be serialized.
                                        
                                        @synchronized( self )
                                        {
                                                CGContextDrawImage( ctx, 
tileRect, tileImage );
                                        }
                                        CGImageRelease( tileImage );
                                        
                                        free( bitsPtr );
                                }];
                                
                                [mDrawingQueue addOperation:op];
#else
                                [self drawTile:tileRect inContext:ctx];
#endif
                
                        }
                }
        }
        
#if DRAW_THREADED
        [mDrawingQueue waitUntilAllOperationsAreFinished];
#endif
        
        CGContextSetStrokeColorWithColor( ctx, [[NSColor blueColor] CGColor]);
        CGContextSetLineWidth( ctx, 1 );
        CGContextStrokeRect( ctx, CGRectInset( mSel, 1, 1 ));
        
        CGContextRestoreGState( ctx );
        
        time = [NSDate timeIntervalSinceReferenceDate] - time;
        
        //NSLog(@"drawing time = %f", time);
}


_______________________________________________

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