1
votes

I am trying to migrate my Opengl application to Metal, and I am using CAMetalLayer for the Metal implementation. I have set the following properties on my inherited NSView class object:

self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
[self setWantsLayer:YES];

I have checked other answers on SO, like NSView subclass - drawRect: not called

So, I tried assigning the delegate as suggested in answers above, something like this

[self.layer setDelegate:self];

and I get the following compilation error:

Cannot initialize a parameter of type 'id<CALayerDelegate> _Nullable' with an lvalue of type 'MyNSView *'

Moreover, I also see the documentation for setting up layer objects at https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/SettingUpLayerObjects/SettingUpLayerObjects.html, which states this:

For layer-backed views with custom content, you should continue to override the view’s methods to do your drawing. A layer-backed view automatically makes itself the delegate of its layer and implements the needed delegate methods, and you should not change that configuration. Instead, you should implement your view’s drawRect: method to draw your content.

So, according to this, I don't even have to set the delegate in my case and I am already implementing the drawRect function in MyNSView. Still it doesn't get called.

Is there anything more that needs to be done?

Thanks!

Edit: So, I fixed the compilation error by making my layer follow the protocol CALayerDelegate, but still I don't get any calls to drawLayer or drawRect.

makeBackingLayer

- (CALayer*)makeBackingLayer
{
    id <MTLDevice> device = MTLCreateSystemDefaultDevice();
    CAMetalLayer *backingLayer   = [CAMetalLayer layer];
    backingLayer.opaque          = YES;
    backingLayer.device          = device;
    backingLayer.pixelFormat     = MTLPixelFormatBGRA8Unorm;
    backingLayer.framebufferOnly = YES;

//    [backingLayer setDelegate:self]; //Tried setting the delegate 
                                       //here as well

    return backingLayer;
}

init function

self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
[self setWantsLayer:YES];
[self.layer setDelegate:self];

Draw functions

- (void)displayLayer:(CALayer *)layer
{
    [self drawRect:self.bounds];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    [self drawRect:self.bounds];
}

-(void) drawRect: (NSRect) dirtyRect
{
    [super drawRect:dirtyRect];
}

I have implemented all the draw functions to check if any of them get hit, but I don't receive a call on any of these functions.

For drawing content to the NSView, I draw to offscreen resources and the get the drawable from CALayer of NSView and call presentDrawable before committing on the commandBuffer.

1
Why aren't you using a subclass of MTKView? How did you arrange to use CAMetalLayer for the layer? Did you override -makeBackingLayer? If so, show that method and any place else you try to configure the layer.Ken Thomases
@KenThomases It is a limitation to use NSView only for using Metal Rendering. And Yes, I am using makeBackingLayer to generate the layer. I'll update the question with relevant code snippets.user007
@KenThomases Updated details in question.user007
What other methods of NSView do you override? What other properties do you set on it? For example, do you override -wantsUpdateLayer to return true?Ken Thomases
@KenThomases I have tried overriding -wantsUpdateLayer to return true, and then override UpdateLayer. But, apparently that doesn't get called. I also tried changing the layerContentsRedrawPolicy to NSViewLayerContentsRedrawOnSetNeedsDisplay and calling [nsView setNeedsDisplay:YES], after I call commit on the command buffer, but that doesn't seem to trigger drawing either.user007

1 Answers

1
votes

Since there wasn't much here that I could debug to find where the bug was introduced, so I started playing with the order in which the instructions were executed. And as it turns out, the order in which you call :

[self setWantsLayer:YES];
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;

was causing the issue. On swapping these two statements, I started receiving call to my displayLayer function. I still couldn't figure out why drawRect doesn't get called for layer backed NSView.

Posting this just in case this swapping fixes the issue for someone else as well.