金属中的多个渲染目标

时间:2015-11-29 12:03:27

标签: objective-c cocoa core-animation render-to-texture metal

我正在尝试实现两个不同的CAMetalLayers,并使用一个MTLRenderCommandEncoder将相同的场景渲染到两个图层(Metal for OS X)。

为此,我尝试创建一个MTLRenderPassDescriptor并将两个图层的纹理附加到其颜色附件上。我的渲染方法如下所示:

- (void)render {
    dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);

    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    __block dispatch_semaphore_t block_sema = _inflight_semaphore;
    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
        dispatch_semaphore_signal(block_sema);
    }];

    MTLRenderPassDescriptor *renderPass = [MTLRenderPassDescriptor renderPassDescriptor];

    for (int i = 0; i < [_metalLayers count]; i++) {
        _metalDrawables[i] = [_metalLayers[i] nextDrawable];
        renderPass.colorAttachments[i].texture = _metalDrawables[[_metalDrawables count] - 1].texture;
        renderPass.colorAttachments[i].clearColor = MTLClearColorMake(0.5, 0.5, (float)i / (float)[_metalLayers count], 1);
        renderPass.colorAttachments[i].storeAction = MTLStoreActionStore;
        renderPass.colorAttachments[i].loadAction = MTLLoadActionClear;
    }

    id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPass];
    [commandEncoder setRenderPipelineState:_pipeline];
    [commandEncoder setVertexBuffer:_positionBuffer offset:0 atIndex:0 ];
    [commandEncoder setVertexBuffer:_colorBuffer offset:0 atIndex:1 ];
    [commandEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3 instanceCount:1];
    [commandEncoder endEncoding];

    for (int i = 0; i < [_metalDrawables count]; i++) {
        [commandBuffer presentDrawable:_metalDrawables[i]];
    }
    [commandBuffer commit];
}

然而,场景仅渲染到其中一个图层,结果是与第一个颜色附件的纹理相关联的图层。另一层使用指定的清晰颜色清除,但不绘制任何内容。

在尝试将同一场景渲染到多个屏幕(即CAMetalLayers)时,是否有机会成功或使用渲染过程描述符的颜色附件完全没有意义?如果是这样,有没有其他可以想象的方法来实现这个结果?

2 个答案:

答案 0 :(得分:2)

要写入多个渲染目标,您需要在片段着色器中显式写出该渲染目标。 @lock已经指出了这一点。

struct MyFragmentOutput {
    // color attachment 0
    float4 clr_f [[ color(0) ]]; 

    // color attachment 1
    int4 clr_i [[ color(1) ]]; 

    // color attachment 2
    uint4 clr_ui [[ color(2) ]]; 
};

fragment MyFragmentOutput
my_frag_shader( ... )
{
     MyFragmentOutput f; 
     ....
     f.clr_f = ...;
     f.clr_i = ...;
     ...
     return f;
    }

然而,这是一种矫枉过正,因为你真的不需要GPU来渲染场景两次。因此@Kacper的上述答案对您的案例更准确。但是,为了补充他的答案,我建议使用可以在GPU上的两个纹理之间复制数据的BlitEncoder,我认为它应该比CPU快得多。

https://developer.apple.com/library/mac/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Blit-Ctx/Blit-Ctx.html#//apple_ref/doc/uid/TP40014221-CH9-SW4

答案 1 :(得分:1)

据我读到这个问题,你可以尝试只渲染一个MTLTexture(不是可绘制层),然后尝试使用MTLTexture方法getBytes和replaceRegion将纹理数据复制到两个可绘制层中。

目前我正在渲染普通纹理,但我遇到了一些文物,目前它不适合我,也许你会找到解决方法。