MetalVertexHelper is a Swift package designed to simplify the creation and management of vertex data for Metal applications. Have you ever struggled with memory alignment between Swift and Metal? MetalVertexHelper was created to solve that very problem. By leveraging Swift's powerful macro system, MetalVertexHelper automatically generates essential components such as C-compatible objects, vertex descriptors, and memory size calculations based on your Swift structs. Simply annotate your Swift struct with @VertexObject, and MetalVertexHelper will handle the necessary padding to align with Metal's requirements, ensuring type safety, reducing boilerplate code, and enhancing performance in your Metal-based graphics and compute applications.
- Automatic C-Compatible Object Generation: Converts Swift vertex structs into C-compatible tuples, including necessary padding for alignment.
- Vertex Descriptor Generation: Automatically creates
MTLVertexDescriptorinstances based on your vertex data, ensuring optimal memory layout and performance. - Memory Size Calculation: Computes the memory size required for your vertex structures, facilitating efficient buffer allocations.
MetalVertexHelper is distributed via Swift Package Manager. To add it to your project:
In your Package.swift, add MetalVertexHelper as a dependency:
dependencies: [
.package(url: "https://github.com/yukiny0811/MetalVertexHelper.git", from: "1.0.0"),
],MetalVertexHelper provides a macro called @VertexObject that you can apply to your vertex structs. This macro automatically generates additional properties and methods to facilitate Metal integration.
-
Import MetalVertexHelper:
import MetalVertexHelper -
Define Your Vertex Struct:
Annotate your struct with the
@VertexObjectmacro. Define your vertex attributes using supported types.@VertexObject struct MyVertex { var position: simd_float3 var color: simd_float4 var texCoord: simd_float2 }
Applying the @VertexObject macro to your struct generates the following members:
-
cObjectProperty:A C-compatible tuple representation of your vertex data, including any necessary padding for alignment.
public var cObject: (simd_float3, simd_float4, simd_float2, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool) { (position, color, texCoord, false, false, false, false, false, false, false, false, false, false, false, false, false) }
-
generateVertexDescriptor()Method:Generates a
MTLVertexDescriptorbased on your vertex attributes, configuring formats, offsets, and buffer indices.public static func generateVertexDescriptor() -> MTLVertexDescriptor { let descriptor = MTLVertexDescriptor() descriptor.attributes[0].format = .float3 descriptor.attributes[0].offset = 0 descriptor.attributes[0].bufferIndex = 0 descriptor.attributes[1].format = .float4 descriptor.attributes[1].offset = 16 descriptor.attributes[1].bufferIndex = 0 descriptor.attributes[2].format = .float2 descriptor.attributes[2].offset = 32 descriptor.attributes[2].bufferIndex = 0 descriptor.layouts[0].stride = 48 descriptor.layouts[0].stepRate = 1 descriptor.layouts[0].stepFunction = .perVertex return descriptor }
-
memorySizeProperty:Provides the memory size required for the vertex struct, facilitating buffer allocations.
public static var memorySize: Int { 48 }
MetalVertexHelper supports a variety of scalar and SIMD types to accommodate different vertex attribute requirements. Below is a comprehensive list of supported types:
BoolInt8UInt8Int16UInt16Int32UInt32Int(Int64 on 64-bit platforms)Float16Float
simd_int2simd_uint2simd_int3simd_uint3simd_int4simd_uint4simd_float2simd_float3simd_float4simd_half2simd_half3simd_half4
simd_half2x2simd_half2x3simd_half2x4simd_half3x2simd_half3x3simd_half3x4simd_half4x2simd_half4x3simd_half4x4simd_float2x2simd_float2x3simd_float2x4simd_float3x2simd_float3x3simd_float3x4simd_float4x2simd_float4x3simd_float4x4
Note: Attempting to use unsupported types will result in a compile-time error.
Below is a step-by-step example demonstrating how to use MetalVertexHelper in a Metal project.
Annotate your vertex struct with @VertexObject and define your vertex attributes using supported types.
import MetalVertexHelper
import simd
@VertexObject
struct Vertex {
var position: simd_float3
var color: simd_float4
var texCoord: simd_float2
}MetalVertexHelper automatically generates additional members for your struct.
// Access the C-compatible object
let cVertex = Vertex(position: [0, 0, 0], color: [1, 0, 0, 1], texCoord: [0, 0]).cObject
// Generate the vertex descriptor
let vertexDescriptor = Vertex.generateVertexDescriptor()
// Get the memory size
let vertexSize = Vertex.memorySizeUse the generated MTLVertexDescriptor in your Metal pipeline setup.
import Metal
// Assuming you have a Metal device
let device: MTLDevice = MTLCreateSystemDefaultDevice()!
// Create a render pipeline descriptor
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexDescriptor = Vertex.generateVertexDescriptor()
// Configure other pipeline settings
// ...
// Create the pipeline state
let pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)Use the memorySize to allocate a vertex buffer.
// Populate the buffer with vertex data
let vertices: [Vertex] = [
Vertex(position: [0, 0, 0], color: [1, 0, 0, 1], texCoord: [0, 0]),
// ... other vertices
]
let vertexBuffer = device.makeBuffer(bytes: vertices.map { $0.cObject }, length: Vertex.memorySize * vertexCount)!Copyright 2025 Yuki Kuwashima
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Happy Coding! 🚀

