dmitri.shuralyov.com/gpu/mtl

Add minimal API to support interactive rendering in a window.

The goal of this change is to make it possible to use package mtl
to render to a window at interactive framerates (e.g., at 60 FPS,
assuming a 60 Hz display with vsync on). It adds the minimal API
that is needed.

A new movingtriangle example is added as a demonstration of this
functionality. It opens a window and renders a triangle that follows
the mouse cursor.

Much of the needed API comes from Core Animation, AppKit frameworks,
rather than Metal. Avoid adding that to mtl package; instead create
separate packages. For now, they are hidden in internal to avoid
committing to a public API and import path. After gaining more
confidence in the approach, they can be factored out and made public.
dmitshur committed 5 years ago commit c4eb07ba2d711bc78bcd2606dd587d9267a61aa5
Showing partial commit. Full Commit
Collapse all
mtl.go
@@ -191,10 +191,11 @@ const (
// Resource represents a memory allocation for storing specialized data
// that is accessible to the GPU.
//
// Reference: https://developer.apple.com/documentation/metal/mtlresource.
type Resource interface {
	// resource returns the underlying id<MTLResource> pointer.
	resource() unsafe.Pointer
}

// RenderPipelineDescriptor configures new RenderPipelineState objects.
//
@@ -325,10 +326,13 @@ func CopyAllDevices() []Device {
		ds[i].Name = C.GoString(d.Name)
	}
	return ds
}

// Device returns the underlying id<MTLDevice> pointer.
func (d Device) Device() unsafe.Pointer { return d.device }

// SupportsFeatureSet reports whether device d supports feature set fs.
//
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset.
func (d Device) SupportsFeatureSet(fs FeatureSet) bool {
	return C.Device_SupportsFeatureSet(d.device, C.uint16_t(fs)) != 0
@@ -403,10 +407,18 @@ func (d Device) MakeTexture(td TextureDescriptor) Texture {
// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions.
type CompileOptions struct {
	// TODO.
}

// Drawable is a displayable resource that can be rendered or written to.
//
// Reference: https://developer.apple.com/documentation/metal/mtldrawable.
type Drawable interface {
	// Drawable returns the underlying id<MTLDrawable> pointer.
	Drawable() unsafe.Pointer
}

// CommandQueue is a queue that organizes the order
// in which command buffers are executed by the GPU.
//
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue.
type CommandQueue struct {
@@ -426,10 +438,17 @@ func (cq CommandQueue) MakeCommandBuffer() CommandBuffer {
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer.
type CommandBuffer struct {
	commandBuffer unsafe.Pointer
}

// PresentDrawable registers a drawable presentation to occur as soon as possible.
//
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
func (cb CommandBuffer) PresentDrawable(d Drawable) {
	C.CommandBuffer_PresentDrawable(cb.commandBuffer, d.Drawable())
}

// Commit commits this command buffer for execution as soon as possible.
//
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit.
func (cb CommandBuffer) Commit() {
	C.CommandBuffer_Commit(cb.commandBuffer)
@@ -562,17 +581,25 @@ func (l Library) MakeFunction(name string) (Function, error) {
//
// Reference: https://developer.apple.com/documentation/metal/mtltexture.
type Texture struct {
	texture unsafe.Pointer

	// TODO: Change these fields into methods.

	// Width is the width of the texture image for the base level mipmap, in pixels.
	Width int

	// Height is the height of the texture image for the base level mipmap, in pixels.
	Height int
}

// NewTexture returns a Texture that wraps an existing id<MTLTexture> pointer.
func NewTexture(texture unsafe.Pointer) Texture {
	return Texture{texture: texture}
}

// resource implements the Resource interface.
func (t Texture) resource() unsafe.Pointer { return t.texture }

// GetBytes copies a block of pixels from the storage allocation of texture
// slice zero into system memory at a specified address.
//
mtl.h
@@ -84,10 +84,11 @@ struct RenderPipelineState Device_MakeRenderPipelineState(void * device, struct
void *                     Device_MakeBuffer(void * device, const void * bytes, size_t length, uint16_t options);
void *                     Device_MakeTexture(void * device, struct TextureDescriptor descriptor);

void * CommandQueue_MakeCommandBuffer(void * commandQueue);

void   CommandBuffer_PresentDrawable(void * commandBuffer, void * drawable);
void   CommandBuffer_Commit(void * commandBuffer);
void   CommandBuffer_WaitUntilCompleted(void * commandBuffer);
void * CommandBuffer_MakeRenderCommandEncoder(void * commandBuffer, struct RenderPassDescriptor descriptor);
void * CommandBuffer_MakeBlitCommandEncoder(void * commandBuffer);

mtl.m
@@ -98,10 +98,14 @@ void * Device_MakeTexture(void * device, struct TextureDescriptor descriptor) {

void * CommandQueue_MakeCommandBuffer(void * commandQueue) {
	return [(id<MTLCommandQueue>)commandQueue commandBuffer];
}

void CommandBuffer_PresentDrawable(void * commandBuffer, void * drawable) {
	[(id<MTLCommandBuffer>)commandBuffer presentDrawable:(id<MTLDrawable>)drawable];
}

void CommandBuffer_Commit(void * commandBuffer) {
	[(id<MTLCommandBuffer>)commandBuffer commit];
}

void CommandBuffer_WaitUntilCompleted(void * commandBuffer) {