是否需要 MTLVertexAttributeDescriptors?为什么需要它们?
Are MTLVertexAttributeDescriptors necessary? Why are they needed?
我学习 Metal 的时间是 iOS / OSX,然后我开始学习 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
组件。只需设置顶点缓冲区,着色器就可以访问所有组件。那么描述符有什么用呢?
当然,有多种做事方式。顶点描述符仅用于其中之一。
例如,顶点函数可以这样声明:
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 将自动进行转换。
同一个着色器可以用于不同的顶点描述符,因此,顶点属性在缓冲区中的分布不同。这为不同的场景提供了灵活性,其中一些顶点属性被分离到不同的缓冲区中(类似于我给出的第一个示例),而另一些顶点属性交错在同一缓冲区中(类似于第二个示例)。等等
如果您不需要这种灵活性和额外的抽象级别,那么您就不需要处理顶点描述符。他们在那里为那些确实需要他们的人服务。
我学习 Metal 的时间是 iOS / OSX,然后我开始学习 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
组件。只需设置顶点缓冲区,着色器就可以访问所有组件。那么描述符有什么用呢?
当然,有多种做事方式。顶点描述符仅用于其中之一。
例如,顶点函数可以这样声明:
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 将自动进行转换。
同一个着色器可以用于不同的顶点描述符,因此,顶点属性在缓冲区中的分布不同。这为不同的场景提供了灵活性,其中一些顶点属性被分离到不同的缓冲区中(类似于我给出的第一个示例),而另一些顶点属性交错在同一缓冲区中(类似于第二个示例)。等等
如果您不需要这种灵活性和额外的抽象级别,那么您就不需要处理顶点描述符。他们在那里为那些确实需要他们的人服务。