gizhw:metal
an Apple GPU through Metal, driven with Objective-C's runtime and gizlang:native (nothing links at build time). gizhw:gpu opens it as kind "metal" and runs the MSL that Kernel.msl prints.
Memory is unified: one MTLBuffer in shared storage is its contents on the CPU and its gpuAddress on the GPU, so a copy to or from the device is a plain copy. The GPU address is a buffer's address everywhere (a view is that address plus an offset, as with CUDA and HIP), and a kernel gets its arguments as one struct of 8-byte words, so no buffer is bound by index. Every live buffer is made resident (useResources) for the commands that follow.
The driver does not know any program: it calls what Objc gives. system gives the libraries' own; a test gives a fake that records the calls.
import * as Metal from "gizhw:metal";Types
Objc
type Objc = { send: (words: Vec<u64>) => u64 effects { io, fail, read, write }; selector: (name: string) => u64 effects { io, fail, read, write }; find: (name: string) => u64 effects { io, fail, read, write }; push: () => u64 effects { io, fail, read, write }; pop: (pool: u64) => void effects { io, fail, read, write }; device: () => u64 effects { io, fail, read, write }; text: (address: u64) => string effects { io, fail, read, write }; sendAddress: u64; freeAddress: u64; }What the driver needs from Objective-C's runtime and Metal. send is objc_msgSend (words: target, selector,
integer arguments; a struct larger than 16 bytes is passed as the address of a copy); device is
MTLCreateSystemDefaultDevice; text is the C string at an address.
send:(words: Vec<u64>) => u64 effects { fail, read, write, io }selector:(name: string) => u64 effects { fail, read, write, io }find:(name: string) => u64 effects { fail, read, write, io }push:() => u64 effects { fail, read, write, io }pop:(pool: u64) => void effects { fail, read, write, io }device:() => u64 effects { fail, read, write, io }text:(address: u64) => string effects { fail, read, write, io }sendAddress:u64freeAddress:u64
Alloc
type Alloc = { gpu: u64; cpu: u64; size: i64; handle: u64; }One MTLBuffer: its GPU address (what a kernel gets), CPU address, size and object.
gpu:u64cpu:u64size:i64handle:u64
Work
type Work = { buffer: u64; encoder: u64; launches: i64; last: u64; inflight: Vec<u64>; }The command buffer being encoded and its compute encoder (0 when none), the launches in it, the pipeline set last, and the command buffers committed and not yet waited for.
buffer:u64encoder:u64launches:i64last:u64inflight:Vec<u64>
Resident
type Resident = { array: u64; count: i64; dirty: bool; }The array of every buffer's object that useResources reads: dirty when a buffer came or went since it was made.
array:u64count:i64dirty:bool
Launch
type Launch = { pipeline: u64; blocks: i64; threads: i64; arguments: Vec<u64>; }A launch kept for Gpu.replay.
pipeline:u64blocks:i64threads:i64arguments:Vec<u64>
Selectors
type Selectors = { retain: u64; release: u64; setPipeline: u64; setBytes: u64; dispatch: u64; }The selectors of the calls made for every launch, found once.
retain:u64release:u64setPipeline:u64setBytes:u64dispatch:u64
Metal
type MetalAn opened Apple GPU: arch is "apple7" (M1) to "apple10", the instruction set Kernel.msl prints for.
arch:stringname:string
Functions
system
function system(nat: Native): Option<Objc>Objective-C's runtime and Metal as this process has them, or none (not macOS, or no Metal).
Effects: fail, io.
open
function open(nat: Native, objc: Objc): Option<Metal>The Apple GPU objc finds, or none (no GPU, or one before the Apple7 family: Intel Macs, A-series).
Effects: fail, read, write, io.
Methods
Metal.alloc
function Metal.alloc(self: Metal, size: i64): u64A new buffer of size bytes (shared storage), contents undefined: its GPU address, or 0 when there is no room.
Effects: fail, read, write, io.
Metal.free
function Metal.free(self: Metal, address: u64): voidFrees the buffer at GPU address address (after the work before: the GPU may still read it).
Effects: fail, read, write, io.
Metal.host
function Metal.host(self: Metal, address: u64): u64The CPU address of the byte at GPU address address (inside a buffer).
Effects: fail, read.
Metal.sizeOf
function Metal.sizeOf(self: Metal, address: u64): i64The size of the buffer at GPU address address.
Effects: fail, read.
Metal.workingSet
function Metal.workingSet(self: Metal): i64The bytes the GPU lets this process use (its recommended working set), and those its buffers hold now.
Effects: fail, read, write, io.
Metal.allocated
function Metal.allocated(self: Metal): i64The bytes that the buffers of the device hold now.
Effects: fail, read, write, io.
Metal.maxBuffer
function Metal.maxBuffer(self: Metal): i64The size of the largest buffer.
Effects: fail, read, write, io.
Metal.releaseFunction
function Metal.releaseFunction(self: Metal): u64The function that a Gizlang value that owns a buffer is given (Native.own).
Metal.hostFree
function Metal.hostFree(self: Metal): u64The address of the function that frees host memory.
Metal.failure
function Metal.failure(self: Metal): stringThe text of the last failure of a command buffer or a compile.
Effects: fail, read.
Metal.compile
function Metal.compile(self: Metal, source: string, name: string): u64 throws stringThe compute pipeline of the function name of the MSL source, compiled by the system compiler (no Xcode).
Effects: fail, read, write, io.
Metal.sync
function Metal.sync(self: Metal): u64Waits for every launched kernel: 0, or 1 when a command buffer failed (Metal.failure says why).
Effects: fail, read, write, io.
Metal.launch
function Metal.launch(self: Metal, pipeline: u64, blocks: i64, threads: i64, arguments: Vec<u64>): voidRuns pipeline on blocks workgroups of threads threads with arguments (8-byte words): returns at once.
Effects: fail, read, write, io.
Metal.startRecording
function Metal.startRecording(self: Metal): voidFrom now on launches run nothing: they are kept for finishRecording.
Effects: fail, write.
Metal.finishRecording
function Metal.finishRecording(self: Metal): i64The launches kept since startRecording, as a recording to replay (a number); launches run again from now on.
Effects: fail, read, write.
Metal.replay
function Metal.replay(self: Metal, id: i64): voidRuns the launches of recording id again, in order.
Effects: fail, read, write, io.
Metal.forget
function Metal.forget(self: Metal, id: i64): voidForgets recording id.
Effects: fail, read, write.