Search code examples
objective-cmacoscore-animationcalayermetal

CAMetalLayer with texture rendered by CARenderer is not visible?


I'm using a CARenderer to render another CALayer tree into a CAMetalLayer, which I hope to use as the mask of yet another layer. For testing purposes, I've tried adding the CAMetalLayer as a normal sublayer instead of a mask.

The layer object below is not visible after adding it to a superlayer that is definitely visible. I've confirmed the frame of the layer is not a problem. Here's how I'm making the CAMetalLayer and its CARenderer.

CAMetalLayer *layer = [CAMetalLayer layer];
layer.frame = bounds;
layer.device = MTLCreateSystemDefaultDevice();
//layer.opaque = NO;
//layer.framebufferOnly = NO;

id<CAMetalDrawable> drawable = layer.nextDrawable;
_lastDrawable = drawable;

_renderer = [CARenderer rendererWithMTLTexture:drawable.texture options:nil];
_renderer.layer = self.superview.layer;
_renderer.bounds = bounds;

👇 By creating a CIImage and inspecting it with the debugger, I've confirmed the CARenderer is updating the Metal texture.

CIImage *img = [CIImage imageWithMTLTexture:_lastDrawable.texture options:nil];

But when I set the superlayer of the CAMetalLayer, it's nowhere to be seen.

[self.layer addSublayer:layer];

Here's how I'm using the CARenderer:

[_renderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
[_renderer addUpdateRect:bounds];
[_renderer render];
[_renderer endFrame];    

That last snippet runs frequently.


edit 1

I've added a backgroundColor and now the layer is visible, but its texture is not being rendered inside it.

layer.backgroundColor = NSColor.yellowColor.CGColor;

Solution

  • I would recommend just setting the original layer as a mask rather than trying to render it to a texture first; you’re sort of duplicating the work that CA would be doing anyway.

    If you really need control over when the mask layer tree gets rendered—and again you should definitely try the standard method first—the right way to do this would be to create an IOSurface-backed MTLTexture rather than using a CAMetalLayer’s drawable, draw into the texture with your CARenderer, set the IOSurface as the contents of a regular CALayer, and use that layer as the mask.