Thanks, Dan.
So, I take it that there's no means to have multiple MTKViews on a single background (NSView) with reliability -- ie. it won't stall on obtaining drawables. In short, I've solved the issue with the 4 main MTKViews by relegating them to their own window. This gave each MTKView its own set of drawables.
My 'downstream' requirement is this, however: while those MTKViews are playing their videos I want the main-screen UI to show mini regions of interest of the playing videos all within one window. I need a mouseable custom view in which I can move about in real time to pick a region of interest in the animating video frame. There are ≈4 such views (each subviews of an NSBox). The NSBox views are all subviews of a parent NSView. (One solution would be to create a floating window for each such view but that's not an ideal solution).
Below is my attempt to add a custom view with a backing CAMetalLayer instead. It also works about 95% of the time. I would appreciate any suggestions to get it right.
Previously this custom view was an MTKView but occasional drawable stalling occured (as previously).
I am now attempting to use a plain NSView for a custom view. I am adding this view as a subview of NSBox, just as before — and things are still mouseable and work most of the time — but 'drawables' stalling is still an issue.
If I use 'setNeedsDisplay:YES' on the view , the main thread brutally stalls when there's no drawable available, as expected. I can obviate by instead doing an async 'manualDraw' call on a background thread but still I end up with drawable starvation.
Below is my attempted solution using just a plain NSView.
In that custom view's initWithFrame: handler, I do this:
self.metalLayer = [[CAMetalLayer alloc] init];
if (!self.metalLayer)
return nil;
[self.metalLayer setDevice:self.device];
[self.metalLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
[self.metalLayer setFramebufferOnly:YES];
[self.metalLayer setFrame:frameRect];
addedLayer=NO; // can't add this CAMetalLayer to self.layer right now as self.layer is nil
Here are the other bits of that custom NSView. I've removed a lot of error-checking from my metalDraw method for the sake of brevity. I should mention that (1) the inputTexture is tripled-buffered from the upstream MTKView's command buffer and (2) the metal vertices mentioned here are only for a single quad, which corresponds the region of interest on the texture.
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[self manualDraw];
}
-(BOOL)wantsUpdateLayer
{
if (!addedLayer) {. // delayed addition of our self.metalLayer to self.layer
if (self.layer) {
addedLayer=YES;
[self.layer addSublayer:self.metalLayer];
}
}
return YES;
}
-(void)updateLayer {
[self manualDraw];
}
- (void)manualDraw {
@autoreleasepool {
[self metalDraw];
}
}
-(void)metalDraw {
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
id<CAMetalDrawable> drawable = [self.metalLayer nextDrawable];
renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 1.0);
id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
MTLViewport viewPort = {0.0, 0.0, (double)self.drawableSize.width, (double)self.drawableSize.height, -1.0, 1.0};
[encoder setViewport:viewPort];
[encoder setRenderPipelineState:metalVertexPipelineState];
NSUInteger vSize = _vertexInfo.metalVertexCount*sizeof(AAPLVertex);
id<MTLBuffer> mBuff = [self.device newBufferWithBytes:_vertexInfo.metalVertices
length:vSize
options:MTLResourceStorageModeShared];
[encoder setVertexBuffer:mBuff offset:0 atIndex:0];
[encoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:1];
[encoder setFragmentTexture:inputTexture atIndex:0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertexInfo.metalVertexCount];
[encoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}