这是我的场景:
在一个MTKView
(以及其中的单个命令缓冲区)中,我有一个操作MTLTexture
的结果MTLComputeEncoder
。
我draw
在 上的方法MTKView
被称为 a 的高优先级实例dispatch_get_global_queue
。在结尾处drawRect
,MTKView
我“舀”那个纹理并将其设置为不同的属性MTKView
。然后我调度一个低优先级线程来调用该视图的draw
方法,将原始纹理的一个区域复制到第二个MTKView
,以及最终的presentDrawable
调用——所有这些都在不同的命令缓冲区上。同时,原来的方法以自己的调用MTKView
结束。draw
presentDrawable
我应该提一下,这两个MTKViews
都配置了isPaused=YES
和enableSetNeedsDisplay=NO
。
这一切都运行得相当好,但我偶尔会遇到“ [CAMetalLayerDrawable texture] should not be called after presenting the drawable
”警告或某些无法识别的线程中偶尔崩溃的崩溃:
QuartzCore`-[CAMetalDrawable present]:
0x7fff881cf471 <+0>: pushq %rbp
0x7fff881cf472 <+1>: movq %rsp, %rbp
0x7fff881cf475 <+4>: movq %rdi, %rax
0x7fff881cf478 <+7>: movq 0x15f25ce1(%rip), %rcx ; CAMetalDrawable._priv
0x7fff881cf47f <+14>: movq (%rax,%rcx), %rcx
0x7fff881cf483 <+18>: movq 0x20(%rcx), %rdi <---- crashes here
0x7fff881cf487 <+22>: xorps %xmm0, %xmm0
0x7fff881cf48a <+25>: movq %rax, %rsi
0x7fff881cf48d <+28>: popq %rbp
0x7fff881cf48e <+29>: jmp 0x7fff881cf493 ; layer_private_present(_CAMetalLayerPrivate*, CAMetalDrawable*, double, unsigned int)
如果我删除/注释掉辅助“独家新闻”并重新绘制原始纹理,则不会发生崩溃。
有没有更可接受的方法来实现这一点?可能使用 anMTLBlitCommandEncoder
显式复制所提供纹理的内容?
更新#1
使代码更加清晰是一个明智的想法,可能是:
这是drawRect
primary 的方法,MTKView
由 a 驱动dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
:
- (void)drawRect:(NSRect)dirtyRect {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER);
[super drawRect:dirtyRect];
…
id<MTLTexture> theTexture = …
[renderedQueue addObject:theTexture];
// populate secondary views
for (BoardMonitorMTKView *v in self.downstreamOutputs)
[v enqueueTexture:(hasValidContent) ? [renderedQueue firstObject] : nil];
__block dispatch_semaphore_t fSemaphore = inflightSemaphore;
__weak __block NSMutableArray *rQueue = renderedQueue;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
if ([rQueue count]==2)
[rQueue removeObjectAtIndex:0];
dispatch_semaphore_signal(fSemaphore);
}];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
}
在辅助视图中:
-(void)enqueueTexture:(id<MTLTexture>)inputTexture {
dispatch_semaphore_wait(inflightSemaphore2, DISPATCH_TIME_FOREVER);
if (inputTexture) {
[textureQueue addObject:inputTexture];
if ([textureQueue count]>kTextureQueueCapacity)
[textureQueue removeObject:[textureQueue firstObject]];
}
dispatch_semaphore_signal(inflightSemaphore2);
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(aQueue, ^{
[self draw];
});
}
- (void)drawRect:(NSRect)dirtyRect {
dispatch_semaphore_wait(inflightSemaphore2, DISPATCH_TIME_FOREVER);
[super drawRect:dirtyRect];
…
id<MTLTexture>inputTexture = [textureQueue firstObject];
MTLRenderPassDescriptor *renderPassDescriptor = self.currentRenderPassDescriptor;
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];
// draw main content
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];
__block dispatch_semaphore_t fSemaphore = inflightSemaphore2;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
[textureQueue removeObject:[textureQueue firstObject]];
dispatch_semaphore_signal(fSemaphore);
}];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
}