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
MTLVertexDescriptor
instances 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
@VertexObject
macro. 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:
-
cObject
Property: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
MTLVertexDescriptor
based 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 }
-
memorySize
Property: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:
Bool
Int8
UInt8
Int16
UInt16
Int32
UInt32
Int
(Int64 on 64-bit platforms)Float16
Float
simd_int2
simd_uint2
simd_int3
simd_uint3
simd_int4
simd_uint4
simd_float2
simd_float3
simd_float4
simd_half2
simd_half3
simd_half4
simd_half2x2
simd_half2x3
simd_half2x4
simd_half3x2
simd_half3x3
simd_half3x4
simd_half4x2
simd_half4x3
simd_half4x4
simd_float2x2
simd_float2x3
simd_float2x4
simd_float3x2
simd_float3x3
simd_float3x4
simd_float4x2
simd_float4x3
simd_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.memorySize
Use 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! 🚀