【问题标题】:In metal how to clear the depth buffer or the stencil buffer?在金属中如何清除深度缓冲区或模板缓冲区?
【发布时间】:2020-03-16 17:49:46
【问题描述】:

在金属中,当我已经有一个 RenderCommandEncoder 并且已经使用它完成了一些工作时,我如何清除 深度缓冲区 或 模板缓冲区(但不是两个我都需要保留一个)?例如,在 OpenGl 中我们有 glClearDepthf / GL_DEPTH_BUFFER_BIT 和 glClearStencil / GL_STENCIL_BUFFER_BIT 但我没有在金属中找到任何等价物。

【问题讨论】:

  • Metal 中没有用于清除渲染通道中间任何附件内容的 API。要在传递开始时清除附件,请将其加载操作设置为 .clear。要保留附件的内容,请将其加载操作设置为 .load。
  • @warrenm :在深度附件上加载的接缝不起作用:(我确实调用了 RenderCommandEncoder 的 endEncoding 然后我将 loadAction 设置为 MTLLoadActionLoad ,最后我创建了一个新的 renderCommandEncoder 但它没有帮助
  • 你是不是把前面pass的深度附件的store action设置为.store(而不是.dontCare)?
  • @warrenm :我想我找到了问题所在,事实上我没有为 DepthAttachment 和 StencilAttachment 创建任何纹理!框架创建这些纹理(但当我不知道但我可以看到它们时)。肖恩,当我对框架进行编码时,也会释放那些纹理并创建新的......你可以接缝吗?
  • 如果您使用MTKView,深度和模板附件不会在通道结束时自动释放和/或重新创建。但是,如果您手动创建 MTLRenderPassDescriptor 对象(而不是使用视图的 currentRenderPassDescriptor),则您有责任在每个通道开始时为这些相应的附件设置相同的纹理。

标签: ios objective-c metal metalkit


【解决方案1】:

虽然 Metal 确实没有提供在渲染通道中间清除深度或模板缓冲区的机制,但可以创建一个近乎平凡的管道状态,让您可以根据需要有选择地执行此操作.

在将一些 OpenGL 代码移植到 Metal 的过程中,我发现自己需要清除与当前设置的视口边界相对应的深度缓冲区部分。这是我的解决方案:

在我的设置代码中,我创建了一个专门的 MTLRenderPipelineState 和 MTLDepthStencilState,它们仅用于清除深度缓冲区,并将它们与我的其他长期资源一起存储在我的 MTKView 子类中:

@property (nonatomic, retain) id<MTLRenderPipelineState> pipelineDepthClear;
@property (nonatomic, retain) id<MTLDepthStencilState> depthStencilClear;

[...]

// Special depth stencil state for clearing the depth buffer
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];

// Don't actually perform a depth test, just always write the buffer
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
depthStencilDescriptor.depthWriteEnabled = YES;
depthStencilDescriptor.label = @"depthStencilClear";
depthStencilClear = [self.device newDepthStencilStateWithDescriptor:depthStencilDescriptor];

// Special pipeline state just for clearing the depth buffer.
MTLRenderPipelineDescriptor *renderPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];

// Omit the color attachment, since we don't want to write the color buffer for this case.

renderPipelineDescriptor.depthAttachmentPixelFormat = self.depthStencilPixelFormat;
renderPipelineDescriptor.rasterSampleCount = self.sampleCount;

renderPipelineDescriptor.vertexFunction = [self.library newFunctionWithName:@"vertex_depth_clear"];
renderPipelineDescriptor.vertexFunction.label = @"vertexDepthClear";

renderPipelineDescriptor.fragmentFunction = [self.library newFunctionWithName:@"fragment_depth_clear"];
renderPipelineDescriptor.fragmentFunction.label = @"fragmentDepthClear";

MTLVertexDescriptor *vertexDescriptor = [[MTLVertexDescriptor alloc] init];
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2;
vertexDescriptor.attributes[0].offset = 0;
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.layouts[0].stepRate = 1;
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertexDescriptor.layouts[0].stride = 8;

renderPipelineDescriptor.vertexDescriptor = vertexDescriptor;

NSError* error = NULL;
renderPipelineDescriptor.label = @"pipelineDepthClear";
self.pipelineDepthClear = [self.device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&error];

并在我的 .metal 文件中设置匹配的顶点和片段函数:

struct DepthClearVertexIn
{
    float2 position [[ attribute(0) ]];
};

struct DepthClearVertexOut
{
    float4 position [[ position ]];
};

struct DepthClearFragmentOut
{
    float depth [[depth(any)]];
};

vertex DepthClearVertexOut 
vertex_depth_clear(     DepthClearVertexIn in [[ stage_in ]])
{
    DepthClearVertexOut out;
    // Just pass the position through. We're clearing in NDC space.
    out.position = float4(in.position, 0.5, 1.0);
    return out;
}

fragment DepthClearFragmentOut fragment_depth_clear()
{
    DepthClearFragmentOut out;
    out.depth = 1.0;
    return out;
}

最后,我的 clearDepthBuffer() 方法的主体如下所示:

// Set up the pipeline and depth/stencil state to write a clear value to only the depth buffer.
[view.commandEncoder setDepthStencilState:view.depthStencilClear];
[view.commandEncoder setRenderPipelineState:view.pipelineDepthClear];

// Normalized Device Coordinates of a tristrip we'll draw to clear the buffer
// (the vertex shader set in pipelineDepthClear ignores all transforms and just passes these through)
float clearCoords[8] = {
    -1, -1,
    1, -1,
    -1, 1,
    1, 1
};

[view.commandEncoder setVertexBytes:clearCoords length:sizeof(float) * 8 atIndex:0];
[view.commandEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];

// Be sure to reset the setDepthStencilState and setRenderPipelineState for further drawing

由于顶点着色器根本不转换坐标,我在 NDC 空间中指定输入几何,因此从 (-1, -1) 到 (1, 1) 的矩形覆盖整个视口。如果您适当地设置几何和/或变换,则可以使用相同的技术来清除深度缓冲区的任何部分。

类似的技术应该适用于清除模板缓冲区,但我将把它作为练习留给读者。 ;)

【讨论】:

  • 谢谢!特别是这部分:struct DepthClearFragmentOut { float depth [[depth(any)]]; }; 帮助我处理了一些深度缓冲区修改代码(很难挖掘 depth_attributes 应该基于 Apple 的文档,结果至少 any 有效)
猜你喜欢
  • 2017-03-21
  • 2016-01-31
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
  • 1970-01-01
相关资源
最近更新 更多