1
votes

Consider the following MSL:

#include <metal_stdlib>
using namespace metal;

struct VertexIn {
    float3 position     [[attribute(0)]];
    float3 normal       [[attribute(1)]];
    float3 color        [[attribute(2)]];
};

// Void just for demonstration
vertex void vertexFunc(const VertexIn vIn [[stage_in]]) {}

The concept of MTLBuffer objects and MTLVertexDescriptor objects is clear: the latter describes how data for a Metal struct (VertexIn in this case) is distributed among different MTLBuffer objects. Then, adding the [[stage_in]] attribute to the argument into a vertex function with the struct's type, an instance is created with the struct's members matched to the data.

I have one question: does [[stage_in]] automatically offset the buffer by the total size of all attributes using a particular buffer (totalSize) by totalSize * [[vertex_id]] within each MTLBuffer before binding the struct's members to the data with the attribute layout information? I figure this must happen, but I haven't yet found anything mentioning if/how/when this occurs with [[stage_in]].

For example, if we used

let descriptor = MTLVertexDescriptor()

// Position
descriptor.attributes[0].format = .float3
descriptor.attributes[0].bufferIndex = 0
descriptor.attributes[0].offset = 0

// Normal
descriptor.attributes[1].format = .float3
descriptor.attributes[1].bufferIndex = 0
descriptor.attributes[1].offset = MemoryLayout<vector_float3>.stride

// Color
descriptor.attributes[2].format = .float3
descriptor.attributes[2].bufferIndex = 1
descriptor.attributes[2].offset = 0

descriptor.layouts[0].stride = 2 * MemoryLayout<vector_float3>.stride
descriptor.layouts[1].stride = MemoryLayout<vector_float3>.stride

and set two buffers, one with the interleaved position and normal data and the other with color data, would the vertex shader receive the nth vertex's data at (buffer0Start) + n * (sizeof(float3) + sizeof(float3)) in buffer 0 and (buffer1Start) + n * (sizeof(float3)) in buffer 1 with [[stage_in]]?

1

1 Answers

0
votes

It appears that this is the case. Section 5.2.4, page 87 of Apple's "Metal Shading Language Specification" Version 2.2 reads

A vertex function can read per-vertex inputs by indexing into buffer(s) passed as arguments to the vertex function using the vertex and instance IDs. To assemble per-vertex inputs and pass them as arguments to a vertex function, declare the inputs with the [[stage_in]] attribute.