Documentation
¶
Overview ¶
Package metal runs compute kernels on a Mac's GPU from pure Go, with no cgo.
A Mac has a large, idle, general-purpose processor in it, and a program that grinds through pixels on the CPU is not merely slower — it is taking cores away from everything else the machine is doing. That is the case this package answers. The shape of it, on an M4 Max, for a 4K frame of image work:
CPU, sixteen cores 65 ms per frame, 82 ms of processor time GPU, this package 4 ms per frame, 0.16 ms of processor time
Sixteen times faster, and five hundred times cheaper in CPU. The second number is the one that matters on a machine that is also syncing files and drawing a browser.
Kernels are Metal Shading Language, compiled at RUN TIME by the system compiler — there is no build step, no toolchain to install, and nothing shipped but the source string.
dev, err := metal.Default()
lib, err := dev.Compile(source)
pipe, err := lib.Pipeline("addOne")
buf, err := dev.NewBuffer(4096)
err = dev.Run(func(e *metal.Encoder) {
e.Use(pipe)
e.Buffer(0, buf)
e.Dispatch(4096)
})
fmt.Println(buf.Bytes()[0])
On a chip with unified memory — every Apple Silicon Mac — a buffer's bytes are the same bytes the GPU reads. Buffer.Bytes hands back a Go slice over them, so there is no upload and no download, and the copy that usually eats a GPU's advantage never happens.
Kernels must check their own bounds ¶
A grid is dispatched in whole threadgroups, so the last group can run past the end of the work. Every kernel here must begin by comparing its thread position against the real size and returning early, exactly as it would if it were written against Metal directly.
Everywhere else ¶
On a platform that is not macOS every constructor returns ErrUnsupported and the rest are no-ops, so a program that offers a GPU path and a portable one cross-compiles without build tags of its own.
Index ¶
- Constants
- Variables
- func Constant[T any](e *Encoder, index int, v *T)
- type Buffer
- type Device
- func (d *Device) Close()
- func (d *Device) Compile(string) (*Library, error)
- func (d *Device) Name() string
- func (d *Device) NewBuffer(int) (*Buffer, error)
- func (d *Device) NewLayer(int, int) (*Layer, error)
- func (d *Device) Present(*Layer, func(*Encoder, *Texture)) (*Pending, error)
- func (d *Device) Run(func(*Encoder)) error
- func (d *Device) Submit(func(*Encoder)) (*Pending, error)
- func (d *Device) UnifiedMemory() bool
- type Encoder
- type Layer
- type Library
- type Pending
- type Pipeline
- type Texture
Constants ¶
const MaxLayerSide = 16384
MaxLayerSide is the largest drawable width or height Resize accepts: the largest 2D texture an Apple GPU makes.
Variables ¶
var ErrNoDevice = errors.New("metal: no GPU on this machine")
ErrNoDevice is returned when macOS reports no usable GPU. It happens: some virtual machines present no Metal device at all.
var ErrNoDrawable = errors.New("metal: the layer has no drawable free")
ErrNoDrawable is returned by Present when the layer has no drawable to give: all of them are still on their way to the screen after a second's wait, or the layer's settings are not ones CoreAnimation accepts.
var ErrUnsupported = errors.New("metal: only macOS has Metal")
ErrUnsupported is returned by every constructor on a platform that has no Metal, so that a caller can fall back rather than fail to build.
Functions ¶
func Constant ¶
Constant passes a small value straight into the kernel — the sizes, strides and coefficients that change every frame and would be silly to allocate a buffer for. It is a function rather than a method because Go has no generic methods, and the alternative is unsafe.Sizeof at every call site.
The Go type must match the kernel struct field for field: Go and Metal agree on the layout of fixed-width scalars, and agree on nothing else.
Types ¶
type Buffer ¶
type Buffer struct {
// contains filtered or unexported fields
}
Buffer is memory both processors can read.
type Device ¶
type Device struct {
// contains filtered or unexported fields
}
Device is one GPU and the queue of work sent to it.
func (*Device) UnifiedMemory ¶
UnifiedMemory reports whether buffer memory is shared with the CPU rather than copied across a bus. It is true on every Apple Silicon Mac.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder records the work of a single submission.
type Layer ¶ added in v0.6.0
type Layer struct {
// contains filtered or unexported fields
}
Layer is a CAMetalLayer: a surface the window server shows, whose frames the GPU draws. Give ID to a view (-[NSView setLayer:]) to put it on screen.
Its drawables are 8-bit BGRA. A kernel writes them through a Texture with float4 colours in RGBA order and Metal stores them in the layer's order, so a kernel never swaps channels itself.
type Library ¶
type Library struct {
// contains filtered or unexported fields
}
Library is a set of kernels compiled together from one source string.
type Pending ¶ added in v0.4.0
type Pending struct {
// contains filtered or unexported fields
}
Pending is work sent to the GPU by Submit that may still be running.
type Pipeline ¶
type Pipeline struct {
// contains filtered or unexported fields
}
Pipeline is one kernel, compiled and ready to dispatch. It carries the two numbers the hardware reports about itself — the width a group of threads executes in and the most threads a group may hold — which is what lets Dispatch choose a threadgroup shape without the caller guessing.