Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active October 26, 2021 16:02
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/431cafee38210a684991 to your computer and use it in GitHub Desktop.
Save bellbind/431cafee38210a684991 to your computer and use it in GitHub Desktop.
[swift][osx] Metal programming with commandline (Xcode less)
#!/bin/bash
xcrun -sdk macosx metal -Wall -Wextra -std=osx-metal1.1 Shaders.metal \
-o Shaders.air
xcrun -sdk macosx metal-ar rcs Shaders.metal-ar Shaders.air
xcrun -sdk macosx metallib -o default.metallib Shaders.metal-ar
swift usingmetal.swift
//-*- mode: c++ -*-
// build:
// xcrun -sdk macosx metal -std=osx-metal1.1 Shaders.metal -o shaders.air
// xcrun -sdk macosx metal-ar rcs Shaders.metalar Shaders.air
// xcrun -sdk macosx metallib -o Shaders.metallib Shaders.metalar
#include <metal_stdlib>
kernel void square(const device float* input [[buffer(0)]],
device float* output [[buffer(1)]],
metal::uint id [[thread_position_in_grid]]) {
output[id] = input[id] * input[id];
}
// Metal programming with commandline
// 1. build a ".metallib" file from shader sources ".metal"
// 2. run swift command with metal used swift file
import Metal
import Darwin
// input data
let count = 1024
var data = [Float](count: count, repeatedValue: 0)
for (i, v) in data.enumerate() {
data[i] = Float(i)
}
// metal preparation
let device = MTLCreateSystemDefaultDevice()
if device == nil {
print("This device is not supported Metal")
exit(1)
}
let dev = device!
// `dev.newDefaultLibrary()` applied only when compiled exe
// as "./default.metallib" or "./resources/defaut.metallib" relative from exe
//let lib = dev.newDefaultLibrary()!
let lib = try! dev.newLibraryWithFile("default.metallib")
let queue = dev.newCommandQueue()
let commands = queue.commandBuffer()
// encoding begin
let encoder = commands.computeCommandEncoder()
// shader program
let fun = lib.newFunctionWithName("square")!;
let state = try! dev.newComputePipelineStateWithFunction(fun)
encoder.setComputePipelineState(state)
// as buffer
let size = count * sizeof (Float)
let options = MTLResourceOptions.CPUCacheModeDefaultCache
let input = dev.newBufferWithBytes(&data, length: size, options: options)
let output = dev.newBufferWithLength(size, options: options)
encoder.setBuffer(input, offset: 0, atIndex: 0)
encoder.setBuffer(output, offset: 0, atIndex: 1)
// thread-group setting
let t = 32
let g = count / t + (count % t == 0 ? 0 : 1)
let threads = MTLSize(width: t, height: 1, depth: 1)
let groups = MTLSize(width: g, height: 1, depth: 1)
encoder.dispatchThreadgroups(groups, threadsPerThreadgroup: threads)
// encoding end
encoder.endEncoding()
// execute
commands.commit()
commands.waitUntilCompleted()
let presult = UnsafeMutablePointer<Float>(output.contents())
let result = Array(UnsafeBufferPointer(start: presult, count: count))
print(result)
@bellbind
Copy link
Author

bellbind commented Nov 5, 2015


IMPORTANT: dev.newDefautLibrary() can use only when a compiled exe; (not when on swift command with .swift file).

The path list of metallib that is relative from the exe file:

  • "./default.metallib"
  • "./resources/default.metallib"

compile: xcrun -sdk macosx swiftc usingmetal.swift -o usingmetal

@erickingxu
Copy link

hi,
when i used device!.makeLibrary(filepath: metalfilepath) , but it always return nil
Why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment