Rajesh
Rajesh

Reputation:

removing/adding CALayers for GPU optimization

I have a layer backed view, I am trying to add subLayers roughly sized around 300 X 270 (in pixels) to it.

The sublayers' count may reach 1000 to 2000, not to mention each sublayer is again scalable to roughly 4280 X 1500 or more for starters.

So the problem is obviously that of a GPU constraint.

After adding around 100 subLayers sized 300 X 270 , there is a warning image is too large for GPU, ignoring and that is messing with the layer display.

The solution for such a problem (from some mailing lists) was to use CATiledLayer, but I can't make use of the tiledLayer due to the complex requirement of the subLayers' display.

Is there a possibility of removing the subLayers which don't fall under VisibleRect of the view?

I tried to removeFromSuperlayer and then add it whenever required, there's always a crash when I try to add the subLayer back.

How can I do this?

I am adding sublayer twice (I need to change it) but for now just for the gist of the code:

-(IBAction)addLayer:(id)sender
{
  Layer *l = [[Layer alloc] init];
  CALayer *layer = [l page];
  [contentArray addObject:page];
  [drawLayer addSublayer:layer];
  [self layout];
}

-(void)layout
{
    NSEnumerator *pageEnumr = [contentArray objectEnumerator];

    float widthMargin = [self frame].size.width;
    CGRect rect;
    float zoom = [self zoomFactor];
    while(obj = [contentEnmr nextObject] )
    {
        [obj setZoomFactor:zoom];
        CALayer *pg =(CALayer *)[obj page] ;
        rect = pg.bounds;

        if ( x + pg.bounds.size.width   > widthMargin  )
        {
            x = xOffset;
            y += rect.size.height + spacing ;

        }
        rect.origin = CGPointMake(x,y);
        [obj changeBounds];

        NSRect VisibleRect = [self visibleRect];
        NSRect result = NSIntersectionRect(VisibleRect,NSRectFromCGRect( rect));
        if( NSEqualRects (result ,NSZeroRect) )
        {
            [pg removeFromSuperlayer];
        }else
        {
            [drawLayer addSublayer:pg];
            [pg setFrame:rect];
            [pg setNeedsDisplay];
        }


        x += ( rect.size.width + spacing);
    }

    NSRect viewRect = [self frame];
    if(viewRect.size.height < ( y + rect.size.height + spacing )  )
        viewRect.size.height = ( y + rect.size.height + spacing) ;

    [self setFrameSize: viewRect.size];

}

@interface Layer : NSObject {
CALayer *page;
}
@property (retain) CALayer *page;

Upvotes: 1

Views: 1552

Answers (2)

Chris Devereux
Chris Devereux

Reputation: 5473

You'll need to do what NSTableView and UITableView do, and manage the addition / removal of layers yourself whenever the visible rect changes. Subscribe to the boundsDidChange noitification of the enclosing scroll view's clip view (I'm assuming that the reason some of the layer is offscreen is that it's enclosed in a scroll view):

- (void) viewDidMoveToSuperview
{
    NSClipView* clipView = [[self enclosingScrollView] contentView];
    [clipView setPostsBoundsChangedNotifications:YES];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(clipViewBoundsDidChange:)
                                                 name:NSViewBoundsDidChangeNotification
                                               object:clipView];
}

and then write a clipViewBoundsDidChange: method that adds and removes sublayers as needed. You may also want to cache and reuse invalidated layers to cut down on allocations. Take a look at the way UITableView and NSTableView interact with their dataSource object for some ideas about how to design the interface for this.

CATiledLayer solves this problem the content of a layer --- ie, whatever you set its contents property or draw into its graphics context directly. It won't do this for sublayers, in fact I think you're advised not to add sublayers to a CATiledLayer at all, as this interferes with its drawing behaviour.

Upvotes: 0

Paul Alexander
Paul Alexander

Reputation: 32367

Have a look at the PhotoScroller application included as part of the WWDC conference. It demonstrates how to zoom and scroll through a very large image by loading only portions of that image that are currently visible.

Also check out this discussion.

Upvotes: 1

Related Questions