【问题标题】:Multiple Render Targets in Metal金属中的多个渲染目标
【发布时间】:2016-03-03 03:23:18
【问题描述】:

我正在尝试实现两个不同的CAMetalLayers 并使用一个MTLRenderCommandEncoder 将相同的场景渲染到两个图层(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)时,该方法是否有任何成功的机会,或者使用渲染通道描述符的颜色附件完全没有意义?如果是这样,是否有任何其他可以想象的方法来实现这一结果?

【问题讨论】:

  • 您的片段着色器是否写入多个颜色附件?我会看看'Attribute Qualifiers for Fragment Function Output' 部分和[[ color(m) ]] 属性限定符。
  • 以上答案是正确的,你需要什么。除非您的着色器写入多个渲染目标,否则您不会在附加的纹理中看到这些写入。
  • 我自己是 Metal 新手,也许没什么,但我觉得很奇怪你初始化 '_metalDrawables[i]' 然后使用 '_metalDrawables[[_metalDrawables count] - 1]' in同一个循环。这是否意味着后者没有为 i = 0 thru count-2 初始化?

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


【解决方案1】:

要写入多个渲染目标,您需要在片段着色器中显式写入该渲染目标。 @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

【讨论】:

  • 当 CbCr 纹理的分辨率比 Y 低两倍时,多个渲染目标对于 RGB 到 YCbCr 的转换非常有用
【解决方案2】:

据我所知,您可以尝试仅渲染到一个 MTLTexture(不可绘制层),然后尝试使用 MTLTexture 方法 getBytes 和 replaceRegion 将纹理数据复制到两个可绘制层中。

目前我正在处理普通纹理的渲染,但我遇到了一些伪影,目前它对我不起作用,也许你会找到解决这个问题的方法。

【讨论】:

    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多