【问题标题】:Crash with passing about a MTLTexture传递一个 MTLTexture 导致崩溃
【发布时间】:2017-10-23 04:42:23
【问题描述】:

这是我的场景:

MTKView(以及其中的单个命令缓冲区)中,我有一个MTLTexture,它是MTLComputeEncoder 操作的结果。

我在MTKView 上的draw 方法被称为dispatch_get_global_queue 的高优先级实例。在drawRect 的末尾MTKView 中,我“挖出”该纹理并将其设置为不同MTKView 上的属性。然后我调度一个低优先级线程来调用该视图的draw 方法,将原始纹理的一个区域复制到第二个MTKView,以及最终的presentDrawable 调用——所有这些都在不同的命令缓冲区上。同时,原始的MTKView 以自己对presentDrawable 的调用结束了draw 方法。

我应该提一下,这两个MTKViews 都配置了isPaused=YESenableSetNeedsDisplay=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)

如果我删除/注释掉辅助“独家新闻”并重新绘制原始纹理,则不会发生崩溃。

有没有更可接受的方法来实现这一点?可能使用MTLBlitCommandEncoder 显式复制所提供纹理的内容?

更新 #1

更清晰的代码是一个明智的想法,可能:

这是主要MTKViewdrawRect 方法,由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];
}

【问题讨论】:

    标签: metal metalkit


    【解决方案1】:

    我不确定您是否意识到创建自己的纹理并对其进行渲染既可以接受又很常见。您不必渲染到视图或图层的可绘制对象的纹理。

    创建您自己的要渲染到的纹理,然后,就目前的步骤而言,从您的纹理渲染到可绘制的纹理。

    请注意,根据您的具体操作,您可能需要一个由三个左右的纹理组成的池,供您旋转。您需要关心的问题是 Metal 是否仍在从纹理中读取,因此在读取完成之前不要写入它。

    【讨论】:

    • 我提供了一些(有问题的)代码,偶尔会崩溃。如果我将辅助视图降级为由 SetNeedsDisplay 安排处理,我的问题就会消失。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多