What is the connection between:
[[stage_in]]
in a Metal ShaderMTLVertexDescriptor
MTKMesh
For example
[[stage_in]]
without using MTLVertexDescriptor
?MTLVertexDescriptor
without using MTKMesh
, but an array of a custom struct based data structure? Such as struct Vertex {...}, Array<Vertex>
?MTKMesh
without using MTLVertexDescriptor
? For example using the same struct based data structure?I didn't find this information on the internet, and the Metal Shading Language Specification doesn't even include the words "descriptor" or "mesh".
No. If you try to create a render pipeline state from a pipeline descriptor without a vertex descriptor, and the corresponding vertex function has a [[stage_in]]
parameter, the pipeline state creation call will fail.
Yes. After all, when you draw an MTKMesh
, you're still obligated to call setVertexBuffer(...)
with the buffers wrapped by the mesh's constituent MTKMeshBuffer
s. You could just as readily create an MTLBuffer
yourself and copy your custom vertex structs into it.
Yes. Instead of having a [[stage_in]]
parameter, you'd have a parameter attributed with [[buffer(0)]]
(assuming all of the vertex data is interleaved in a single vertex buffer) of type MyVertexType *
, as well as a [[vertex_id]]
parameter that tells you where to index into that buffer.
Here's an example of setting the vertex buffers from an MTKMesh
on a render command encoder:
for (index, vertexBuffer) in mesh.vertexBuffers.enumerated() {
commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: index)
}
vertexBuffer
is of type MTKMeshBuffer
, while its buffer
property is of type MTLBuffer
; I mention this because it can be confusing.
Here is one way in which you might create a vertex descriptor to tell Model I/O and MetalKit to lay out the mesh data you're loading:
let mdlVertexDescriptor = MDLVertexDescriptor()
mdlVertexDescriptor.attributes[0] = MDLVertexAttribute(name: MDLVertexAttributePosition, format: MDLVertexFormat.float3, offset: 0, bufferIndex: 0)
mdlVertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal, format: MDLVertexFormat.float3, offset: 12, bufferIndex: 0)
mdlVertexDescriptor.attributes[2] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: MDLVertexFormat.float2, offset: 24, bufferIndex: 0)
mdlVertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: 32)
You can create a corresponding MTLVertexDescriptor
in order to create a render pipeline state suitable for rendering such a mesh:
let vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mdlVertexDescriptor)!
Here's a vertex struct that matches that layout:
struct VertexIn {
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
float2 texCoords [[attribute(2)]];
};
Here's a stub vertex function that consumes one of these vertices:
vertex VertexOut vertex_main(VertexIn in [[stage_in]])
{
}
And finally, here's a vertex struct and vertex function you could use to render the exact same mesh data without a vertex descriptor:
struct VertexIn {
packed_float3 position;
packed_float3 normal;
packed_float2 texCoords;
};
vertex VertexOut vertex_main(device VertexIn *vertices [[buffer(0)]],
uint vid [[vertex_id]])
{
VertexIn in = vertices[vid];
}
Note that in this last case, I need to mark the struct members as packed since by default, Metal's simd types are padded for alignment purposes (specifically, the stride of a float3
is 16 bytes, not 12 as we requested in our vertex descriptor).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With