MTLVertexAttributeDescriptors 是必要的吗?为啥需要它们?

Posted

技术标签:

【中文标题】MTLVertexAttributeDescriptors 是必要的吗?为啥需要它们?【英文标题】:Are MTLVertexAttributeDescriptors necessary? Why are they needed?MTLVertexAttributeDescriptors 是必要的吗?为什么需要它们? 【发布时间】:2018-04-13 04:03:09 【问题描述】:

我一直在学习适用于 ios / OSX 的 Metal,我从关注 Ray Wenderlich tutorial 开始。本教程运行良好,但没有提及MTLVertexAttributeDescriptors

现在我正在开发自己的应用程序,我遇到了奇怪的故障,我想知道我不使用 MTLVertexAttributeDescriptors 的事实是否与问题有关。

它们有什么不同?我已经能够制作各种具有不同顶点结构的着色器,但我什至不知道这些东西。

我知道您使用它们来描述在着色器中使用的顶点组件的布局。例如,着色器可能会将此结构用于顶点,并将在下面函数的顶点描述符中设置它。

typedef struct

    float3 position [[attribute(T_VertexAttributePosition)]];
    float2 texCoord [[attribute(T_VertexAttributeTexcoord)]];
 Vertex;

 class func buildMetalVertexDescriptor() -> MTLVertexDescriptor 

    let mtlVertexDescriptor = MTLVertexDescriptor()

    mtlVertexDescriptor.attributes[T_VertexAttribute.position.rawValue].format = MTLVertexFormat.float3
    mtlVertexDescriptor.attributes[T_VertexAttribute.position.rawValue].offset = 0
    mtlVertexDescriptor.attributes[T_VertexAttribute.position.rawValue].bufferIndex = T_BufferIndex.meshPositions.rawValue

    mtlVertexDescriptor.attributes[T_VertexAttribute.texcoord.rawValue].format = MTLVertexFormat.float2
    mtlVertexDescriptor.attributes[T_VertexAttribute.texcoord.rawValue].offset = 0
    mtlVertexDescriptor.attributes[T_VertexAttribute.texcoord.rawValue].bufferIndex = T_BufferIndex.meshGenerics.rawValue

    mtlVertexDescriptor.layouts[T_BufferIndex.meshPositions.rawValue].stride = 12
    mtlVertexDescriptor.layouts[T_BufferIndex.meshPositions.rawValue].stepRate = 1
    mtlVertexDescriptor.layouts[T_BufferIndex.meshPositions.rawValue].stepFunction = MTLVertexStepFunction.perVertex

    mtlVertexDescriptor.layouts[T_BufferIndex.meshGenerics.rawValue].stride = 8
    mtlVertexDescriptor.layouts[T_BufferIndex.meshGenerics.rawValue].stepRate = 1
    mtlVertexDescriptor.layouts[T_BufferIndex.meshGenerics.rawValue].stepFunction = MTLVertexStepFunction.perVertex

    return mtlVertexDescriptor

但即使没有MTLVertexDescriptor 设置,着色器也可以访问顶点缓冲区和数组中顶点的position / texCoord 组件。只需设置顶点缓冲区,着色器就可以访问所有组件。那么描述符有什么用呢?

【问题讨论】:

【参考方案1】:

当然,有多种做事方式。顶点描述符仅用于其中之一。

例如,一个顶点函数可以这样声明:

vertex MyVertexOut vertex_func(device const float3 *positions [[buffer(0)]],
                               device const float2 *texCoords [[buffer(1)]],
                               uint vid [[vertex_id]])

    // use positions[vid] and texCoords[vid] to fill in and return a MyVertexOut structure

这规定顶点属性在单独的缓冲区中提供,每个缓冲区都有特定的布局。

你也可以这样做:

struct MyVertexIn

    float3 position;
    float2 texCoord;
;
vertex MyVertexOut vertex_func(device const MyVertexIn *vertexes [[buffer(0)]],
                               uint vid [[vertex_id]])

    // use vertexes[vid].position and vertexes[vid].texCoord to fill in and return a MyVertexOut structure

这规定顶点属性在与MyVertexIn 布局匹配的单个结构缓冲区中提供。

以上都不需要或使用顶点描述符。这完全无关紧要。

不过,您也可以这样做:

struct MyVertexIn

    float3 position [[attribute(0)]];
    float2 texCoord [[attribute(1)]];
;
vertex MyVertexOut vertex_func(MyVertexIn vertex [[stage_in]])

    // use vertex.position and vertex.texCoord to fill in and return a MyVertexOut structure

注意attribute(n)stage_in 属性的使用。这并不规定如何提供顶点属性。相反,顶点描述符描述了从一个或多个缓冲区到顶点属性的映射。映射还可以执行转换和扩展。例如,上面的着色器代码指定position 字段是float3,但缓冲区可能包含(并被描述为包含)half3 值(或各种其他类型),Metal 将自动进行转换。

相同的着色器可以用于不同的顶点描述符,因此,顶点属性在缓冲区中的分布不同。这为不同的场景提供了灵活性,一些顶点属性被分离到不同的缓冲区(类似于我给出的第一个示例),而另一些则它们在同一个缓冲区中交错(类似于第二个示例)。等等。

如果您不需要这种灵活性和额外的抽象级别,那么您就不需要处理顶点描述符。他们为那些需要他们的人而存在。

【讨论】:

谢谢 - 我想知道 [[stage_in]] 和 [[buffer]] 语法之间的区别是什么。不幸的是,这也意味着缺少顶点描述符可能不是我出现故障的原因。 请注意 - 有时您可能需要在结构中使用 packed_floatN 而不是 floatN,具体取决于您发送数据的方式。

以上是关于MTLVertexAttributeDescriptors 是必要的吗?为啥需要它们?的主要内容,如果未能解决你的问题,请参考以下文章