dmitri.shuralyov.com/gpu/mtl

add minimal API to support interactive rendering in a window dmitri.shuralyov.com/gpu/mtl#1

Mergeddmitshur opened this change 6 years ago
Patch Set 3: factor out Core Animation, Cocoa APIs into separate internal packages
dmitshur committed 6 years ago commit 347f5577cacfe8b5d3c4fdb7ab9314d102896851
Collapse all
Commit Message
FileFile
@@ -1,19 +1,22 @@
11
Parent:     7a718d8 (Add PixelFormatBGRA8UNormSRGB, SetVertexBytes.)
22
Author:     Dmitri Shuralyov <dmitri@shuralyov.com>
33
AuthorDate: Sat Jun 23 01:07:53 2018 -0400
44
Commit:     Dmitri Shuralyov <dmitri@shuralyov.com>
5
CommitDate: Sat Oct 20 23:15:25 2018 -0400
6

7
WIP: Add minimal API to support interactive rendering in a window.
5
CommitDate: Sun Oct 21 13:22:10 2018 -0400
6

7
Add minimal API to support interactive rendering in a window.
88

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

1414
A new movingtriangle example is added as a demonstration of this
15
functionality. It renders a triangle that follows the mouse cursor.
16

17
TODO: A lot of the newly added API comes from Core Animation, AppKit
18
frameworks, rather than Metal. As a result, they likely do not belong
19
in this package and should be factored out.
15
functionality. It opens a window and renders a triangle that follows
16
the mouse cursor.
17

18
Much of the needed API comes from Core Animation, AppKit frameworks,
19
rather than Metal. Avoid adding that to mtl package; instead create
20
separate packages. For now, they are hidden in internal to avoid
21
committing to a public API and import path. After gaining more
22
confidence in the approach, they can be factored out and made public.
example/movingtriangle/main.go
FileFile
@@ -11,25 +11,25 @@ import (
1111
	"runtime"
1212
	"time"
1313
	"unsafe"
1414

1515
	"dmitri.shuralyov.com/gpu/mtl"
16
	"dmitri.shuralyov.com/gpu/mtl/internal/ca"
17
	"dmitri.shuralyov.com/gpu/mtl/internal/ns"
1618
	"github.com/go-gl/glfw/v3.2/glfw"
1719
	"golang.org/x/image/math/f32"
1820
)
1921

20
func usage() {
21
	fmt.Fprintln(os.Stderr, "Usage: movingtriangle")
22
	flag.PrintDefaults()
23
}
24

2522
func init() {
2623
	runtime.LockOSThread()
2724
}
2825

2926
func main() {
30
	flag.Usage = usage
27
	flag.Usage = func() {
28
		fmt.Fprintln(os.Stderr, "Usage: movingtriangle")
29
		flag.PrintDefaults()
30
	}
3131
	flag.Parse()
3232

3333
	err := run()
3434
	if err != nil {
3535
		log.Fatalln(err)
@@ -54,22 +54,23 @@ func run() error {
5454
	if err != nil {
5555
		return err
5656
	}
5757
	defer window.Destroy()
5858

59
	layer := mtl.MakeLayer()
60
	layer.SetDevice(device)
61
	layer.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
62
	layer.SetDrawableSize(window.GetFramebufferSize())
63
	layer.SetMaximumDrawableCount(3)
64
	layer.SetDisplaySyncEnabled(true)
65
	mtl.SetWindowContentViewLayer(window.GetCocoaWindow(), layer)
66
	mtl.SetWindowContentViewWantsLayer(window.GetCocoaWindow(), true)
59
	ml := ca.MakeMetalLayer()
60
	ml.SetDevice(device)
61
	ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
62
	ml.SetDrawableSize(window.GetFramebufferSize())
63
	ml.SetMaximumDrawableCount(3)
64
	ml.SetDisplaySyncEnabled(true)
65
	cocoaWindow := ns.NewWindow(unsafe.Pointer(window.GetCocoaWindow()))
66
	cocoaWindow.ContentView().SetLayer(ml)
67
	cocoaWindow.ContentView().SetWantsLayer(true)
6768

6869
	// Set callbacks.
6970
	window.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
70
		layer.SetDrawableSize(width, height)
71
		ml.SetDrawableSize(width, height)
7172
	})
7273
	var windowSize = [2]int32{640, 480}
7374
	window.SetSizeCallback(func(_ *glfw.Window, width, height int) {
7475
		windowSize[0], windowSize[1] = int32(width), int32(height)
7576
	})
@@ -119,11 +120,11 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
119120
		return err
120121
	}
121122
	var rpld mtl.RenderPipelineDescriptor
122123
	rpld.VertexFunction = vs
123124
	rpld.FragmentFunction = fs
124
	rpld.ColorAttachments[0].PixelFormat = layer.PixelFormat()
125
	rpld.ColorAttachments[0].PixelFormat = ml.PixelFormat()
125126
	rps, err := device.MakeRenderPipelineState(rpld)
126127
	if err != nil {
127128
		return err
128129
	}
129130

@@ -145,11 +146,11 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
145146

146147
	for !window.ShouldClose() {
147148
		glfw.PollEvents()
148149

149150
		// Create a drawable to render into.
150
		drawable, err := layer.NextDrawable()
151
		drawable, err := ml.NextDrawable()
151152
		if err != nil {
152153
			return err
153154
		}
154155

155156
		cb := cq.MakeCommandBuffer()
@@ -166,11 +167,11 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
166167
		rce.SetVertexBytes(unsafe.Pointer(&windowSize[0]), unsafe.Sizeof(windowSize), 1)
167168
		rce.SetVertexBytes(unsafe.Pointer(&pos[0]), unsafe.Sizeof(pos), 2)
168169
		rce.DrawPrimitives(mtl.PrimitiveTypeTriangle, 0, 3)
169170
		rce.EndEncoding()
170171

171
		cb.Present(drawable)
172
		cb.PresentDrawable(drawable)
172173
		cb.Commit()
173174

174175
		frame <- struct{}{}
175176
	}
176177

internal/ca/ca.go
FileFile
@@ -0,0 +1,137 @@
1
// +build darwin
2

3
// Package ca provides access to Apple's Core Animation API (https://developer.apple.com/documentation/quartzcore).
4
//
5
// This package is in very early stages of development.
6
// It's a minimal implementation with scope limited to
7
// supporting the ../../example/movingtriangle command.
8
package ca
9

10
import (
11
	"errors"
12
	"unsafe"
13

14
	"dmitri.shuralyov.com/gpu/mtl"
15
)
16

17
/*
18
#cgo LDFLAGS: -framework QuartzCore -framework Foundation
19
#include "ca.h"
20
*/
21
import "C"
22

23
// Layer is an object that manages image-based content and
24
// allows you to perform animations on that content.
25
//
26
// Reference: https://developer.apple.com/documentation/quartzcore/calayer.
27
type Layer interface {
28
	// Layer returns the underlying CALayer * pointer.
29
	Layer() unsafe.Pointer
30
}
31

32
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
33
//
34
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
35
type MetalLayer struct {
36
	metalLayer unsafe.Pointer
37
}
38

39
// MakeMetalLayer creates a new Core Animation Metal layer.
40
//
41
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
42
func MakeMetalLayer() MetalLayer {
43
	return MetalLayer{C.MakeMetalLayer()}
44
}
45

46
// Layer implements the Layer interface.
47
func (ml MetalLayer) Layer() unsafe.Pointer { return ml.metalLayer }
48

49
// PixelFormat returns the pixel format of textures for rendering layer content.
50
//
51
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
52
func (ml MetalLayer) PixelFormat() mtl.PixelFormat {
53
	return mtl.PixelFormat(C.MetalLayer_PixelFormat(ml.metalLayer))
54
}
55

56
// SetDevice sets the Metal device responsible for the layer's drawable resources.
57
//
58
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
59
func (ml MetalLayer) SetDevice(device mtl.Device) {
60
	C.MetalLayer_SetDevice(ml.metalLayer, device.Device())
61
}
62

63
// SetPixelFormat controls the pixel format of textures for rendering layer content.
64
//
65
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,
66
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
67
// SetPixelFormat panics for other values.
68
//
69
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
70
func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
71
	e := C.MetalLayer_SetPixelFormat(ml.metalLayer, C.uint16_t(pf))
72
	if e != nil {
73
		panic(errors.New(C.GoString(e)))
74
	}
75
}
76

77
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
78
// managed by Core Animation.
79
//
80
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
81
//
82
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
83
func (ml MetalLayer) SetMaximumDrawableCount(count int) {
84
	e := C.MetalLayer_SetMaximumDrawableCount(ml.metalLayer, C.uint_t(count))
85
	if e != nil {
86
		panic(errors.New(C.GoString(e)))
87
	}
88
}
89

90
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
91
// are synchronized with the display's refresh rate.
92
//
93
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
94
func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
95
	switch enabled {
96
	case true:
97
		C.MetalLayer_SetDisplaySyncEnabled(ml.metalLayer, 1)
98
	case false:
99
		C.MetalLayer_SetDisplaySyncEnabled(ml.metalLayer, 0)
100
	}
101
}
102

103
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
104
//
105
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
106
func (ml MetalLayer) SetDrawableSize(width, height int) {
107
	C.MetalLayer_SetDrawableSize(ml.metalLayer, C.double(width), C.double(height))
108
}
109

110
// NextDrawable returns a Metal drawable.
111
//
112
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
113
func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
114
	md := C.MetalLayer_NextDrawable(ml.metalLayer)
115
	if md == nil {
116
		return MetalDrawable{}, errors.New("nextDrawable returned nil")
117
	}
118

119
	return MetalDrawable{md}, nil
120
}
121

122
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
123
//
124
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
125
type MetalDrawable struct {
126
	metalDrawable unsafe.Pointer
127
}
128

129
// Drawable implements the mtl.Drawable interface.
130
func (md MetalDrawable) Drawable() unsafe.Pointer { return md.metalDrawable }
131

132
// Texture returns a Metal texture object representing the drawable object's content.
133
//
134
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
135
func (md MetalDrawable) Texture() mtl.Texture {
136
	return mtl.NewTexture(C.MetalDrawable_Texture(md.metalDrawable))
137
}
internal/ca/ca.h
FileFile
@@ -0,0 +1,17 @@
1
// +build darwin
2

3
typedef signed char BOOL;
4
typedef unsigned long uint_t;
5
typedef unsigned short uint16_t;
6

7
void * MakeMetalLayer();
8

9
uint16_t     MetalLayer_PixelFormat(void * metalLayer);
10
void         MetalLayer_SetDevice(void * metalLayer, void * device);
11
const char * MetalLayer_SetPixelFormat(void * metalLayer, uint16_t pixelFormat);
12
const char * MetalLayer_SetMaximumDrawableCount(void * metalLayer, uint_t maximumDrawableCount);
13
void         MetalLayer_SetDisplaySyncEnabled(void * metalLayer, BOOL displaySyncEnabled);
14
void         MetalLayer_SetDrawableSize(void * metalLayer, double width, double height);
15
void *       MetalLayer_NextDrawable(void * metalLayer);
16

17
void * MetalDrawable_Texture(void * drawable);
internal/ca/ca.m
FileFile
@@ -0,0 +1,54 @@
1
// +build darwin
2

3
#import <QuartzCore/QuartzCore.h>
4
#include "ca.h"
5

6
void * MakeMetalLayer() {
7
	return [[CAMetalLayer alloc] init];
8
}
9

10
uint16_t MetalLayer_PixelFormat(void * metalLayer) {
11
	return ((CAMetalLayer *)metalLayer).pixelFormat;
12
}
13

14
void MetalLayer_SetDevice(void * metalLayer, void * device) {
15
	((CAMetalLayer *)metalLayer).device = (id<MTLDevice>)device;
16
}
17

18
const char * MetalLayer_SetPixelFormat(void * metalLayer, uint16_t pixelFormat) {
19
	@try {
20
		((CAMetalLayer *)metalLayer).pixelFormat = (MTLPixelFormat)pixelFormat;
21
	}
22
	@catch (NSException * exception) {
23
		return exception.reason.UTF8String;
24
	}
25
	return NULL;
26
}
27

28
const char * MetalLayer_SetMaximumDrawableCount(void * metalLayer, uint_t maximumDrawableCount) {
29
	if (@available(macOS 10.13.2, *)) {
30
		@try {
31
			((CAMetalLayer *)metalLayer).maximumDrawableCount = (NSUInteger)maximumDrawableCount;
32
		}
33
		@catch (NSException * exception) {
34
			return exception.reason.UTF8String;
35
		}
36
	}
37
	return NULL;
38
}
39

40
void MetalLayer_SetDisplaySyncEnabled(void * metalLayer, BOOL displaySyncEnabled) {
41
	((CAMetalLayer *)metalLayer).displaySyncEnabled = displaySyncEnabled;
42
}
43

44
void MetalLayer_SetDrawableSize(void * metalLayer, double width, double height) {
45
	((CAMetalLayer *)metalLayer).drawableSize = (CGSize){width, height};
46
}
47

48
void * MetalLayer_NextDrawable(void * metalLayer) {
49
	return [(CAMetalLayer *)metalLayer nextDrawable];
50
}
51

52
void * MetalDrawable_Texture(void * metalDrawable) {
53
	return ((id<CAMetalDrawable>)metalDrawable).texture;
54
}
internal/ns/ns.go
FileFile
@@ -0,0 +1,65 @@
1
// +build darwin
2

3
// Package ns provides access to Apple's Cocoa API (https://developer.apple.com/documentation/appkit).
4
//
5
// This package is in very early stages of development.
6
// It's a minimal implementation with scope limited to
7
// supporting the ../../example/movingtriangle command.
8
package ns
9

10
import (
11
	"unsafe"
12

13
	"dmitri.shuralyov.com/gpu/mtl/internal/ca"
14
)
15

16
/*
17
#include "ns.h"
18
*/
19
import "C"
20

21
// Window is a window that an app displays on the screen.
22
//
23
// Reference: https://developer.apple.com/documentation/appkit/nswindow.
24
type Window struct {
25
	window unsafe.Pointer
26
}
27

28
// NewWindow returns a Window that wraps an existing NSWindow * pointer.
29
func NewWindow(window unsafe.Pointer) Window {
30
	return Window{window}
31
}
32

33
// ContentView returns the window's content view, the highest accessible View
34
// in the window's view hierarchy.
35
//
36
// Reference: https://developer.apple.com/documentation/appkit/nswindow/1419160-contentview.
37
func (w Window) ContentView() View {
38
	return View{C.Window_ContentView(w.window)}
39
}
40

41
// View is the infrastructure for drawing, printing, and handling events in an app.
42
//
43
// Reference: https://developer.apple.com/documentation/appkit/nsview.
44
type View struct {
45
	view unsafe.Pointer
46
}
47

48
// SetLayer sets v.layer to l.
49
//
50
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483298-layer.
51
func (v View) SetLayer(l ca.Layer) {
52
	C.View_SetLayer(v.view, l.Layer())
53
}
54

55
// SetWantsLayer sets v.wantsLayer to wantsLayer.
56
//
57
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer.
58
func (v View) SetWantsLayer(wantsLayer bool) {
59
	switch wantsLayer {
60
	case true:
61
		C.View_SetWantsLayer(v.view, 1)
62
	case false:
63
		C.View_SetWantsLayer(v.view, 0)
64
	}
65
}
internal/ns/ns.h
FileFile
@@ -0,0 +1,8 @@
1
// +build darwin
2

3
typedef signed char BOOL;
4

5
void * Window_ContentView(void * window);
6

7
void View_SetLayer(void * view, void * layer);
8
void View_SetWantsLayer(void * view, BOOL wantsLayer);
internal/ns/ns.m
FileFile
@@ -0,0 +1,16 @@
1
// +build darwin
2

3
#import <Cocoa/Cocoa.h>
4
#include "ns.h"
5

6
void * Window_ContentView(void * window) {
7
	return ((NSWindow *)window).contentView;
8
}
9

10
void View_SetLayer(void * view, void * layer) {
11
	((NSView *)view).layer = (CALayer *)layer;
12
}
13

14
void View_SetWantsLayer(void * view, BOOL wantsLayer) {
15
	((NSView *)view).wantsLayer = wantsLayer;
16
}
mtl.go
FileFile
@@ -13,143 +13,19 @@ import (
1313
	"fmt"
1414
	"unsafe"
1515
)
1616

1717
/*
18
#cgo LDFLAGS: -framework Metal -framework QuartzCore -framework Foundation
18
#cgo LDFLAGS: -framework Metal -framework Foundation
1919
#include <stdlib.h>
2020
#include "mtl.h"
2121
struct Library Go_Device_MakeLibrary(void * device, _GoString_ source) {
2222
	return Device_MakeLibrary(device, _GoStringPtr(source), _GoStringLen(source));
2323
}
2424
*/
2525
import "C"
2626

27
// Layer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
28
//
29
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
30
type Layer struct {
31
	layer unsafe.Pointer
32
}
33

34
// MakeLayer creates a new Core Animation Metal layer.
35
//
36
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
37
func MakeLayer() Layer {
38
	return Layer{C.MakeLayer()}
39
}
40

41
// PixelFormat returns the pixel format of textures for rendering layer content.
42
//
43
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
44
func (l Layer) PixelFormat() PixelFormat {
45
	return PixelFormat(C.Layer_PixelFormat(l.layer))
46
}
47

48
// SetDevice sets the Metal device responsible for the layer's drawable resources.
49
//
50
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
51
func (l Layer) SetDevice(device Device) {
52
	C.Layer_SetDevice(l.layer, device.device)
53
}
54

55
// SetPixelFormat controls the pixel format of textures for rendering layer content.
56
//
57
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,
58
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
59
// SetPixelFormat panics for other values.
60
//
61
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
62
func (l Layer) SetPixelFormat(pf PixelFormat) {
63
	e := C.Layer_SetPixelFormat(l.layer, C.uint16_t(pf))
64
	if e != nil {
65
		panic(errors.New(C.GoString(e)))
66
	}
67
}
68

69
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
70
// managed by Core Animation.
71
//
72
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
73
//
74
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
75
func (l Layer) SetMaximumDrawableCount(count int) {
76
	e := C.Layer_SetMaximumDrawableCount(l.layer, C.uint_t(count))
77
	if e != nil {
78
		panic(errors.New(C.GoString(e)))
79
	}
80
}
81

82
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
83
// are synchronized with the display's refresh rate.
84
//
85
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
86
func (l Layer) SetDisplaySyncEnabled(enabled bool) {
87
	switch enabled {
88
	case true:
89
		C.Layer_SetDisplaySyncEnabled(l.layer, 1)
90
	case false:
91
		C.Layer_SetDisplaySyncEnabled(l.layer, 0)
92
	}
93
}
94

95
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
96
//
97
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
98
func (l Layer) SetDrawableSize(width, height int) {
99
	C.Layer_SetDrawableSize(l.layer, C.double(width), C.double(height))
100
}
101

102
// NextDrawable returns a Metal drawable.
103
//
104
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
105
func (l Layer) NextDrawable() (Drawable, error) {
106
	d := C.Layer_NextDrawable(l.layer)
107
	if d == nil {
108
		return Drawable{}, errors.New("nextDrawable returned nil")
109
	}
110

111
	return Drawable{d}, nil
112
}
113

114
// Drawable is a displayable resource that can be rendered or written to.
115
//
116
// Reference: https://developer.apple.com/documentation/metal/mtldrawable.
117
type Drawable struct {
118
	drawable unsafe.Pointer
119
}
120

121
// Texture returns a Metal texture object representing the drawable object's content.
122
//
123
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
124
func (d Drawable) Texture() Texture {
125
	return Texture{
126
		texture: C.Drawable_Texture(d.drawable),
127
		Width:   0, // TODO: Fetch dimensions of actually created texture.
128
		Height:  0, // TODO: Fetch dimensions of actually created texture.
129
	}
130
}
131

132
// SetWindowContentViewLayer sets cocoaWindow's contentView's layer to layer.
133
//
134
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483298-layer.
135
func SetWindowContentViewLayer(cocoaWindow uintptr, l Layer) {
136
	C.SetWindowContentViewLayer(unsafe.Pointer(cocoaWindow), l.layer)
137
}
138

139
// SetWindowContentViewWantsLayer sets cocoaWindow's contentView's wantsLayer to wantsLayer.
140
//
141
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer.
142
func SetWindowContentViewWantsLayer(cocoaWindow uintptr, wantsLayer bool) {
143
	switch wantsLayer {
144
	case true:
145
		C.SetWindowContentViewWantsLayer(unsafe.Pointer(cocoaWindow), 1)
146
	case false:
147
		C.SetWindowContentViewWantsLayer(unsafe.Pointer(cocoaWindow), 0)
148
	}
149
}
150

15127
// FeatureSet defines a specific platform, hardware, and software configuration.
15228
//
15329
// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset.
15430
type FeatureSet uint16
15531

@@ -315,10 +191,11 @@ const (
315191
// Resource represents a memory allocation for storing specialized data
316192
// that is accessible to the GPU.
317193
//
318194
// Reference: https://developer.apple.com/documentation/metal/mtlresource.
319195
type Resource interface {
196
	// resource returns the underlying id<MTLResource> pointer.
320197
	resource() unsafe.Pointer
321198
}
322199

323200
// RenderPipelineDescriptor configures new RenderPipelineState objects.
324201
//
@@ -449,10 +326,13 @@ func CopyAllDevices() []Device {
449326
		ds[i].Name = C.GoString(d.Name)
450327
	}
451328
	return ds
452329
}
453330

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

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

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

532420
// CommandQueue is a queue that organizes the order
533421
// in which command buffers are executed by the GPU.
534422
//
535423
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue.
536424
type CommandQueue struct {
@@ -550,15 +438,15 @@ func (cq CommandQueue) MakeCommandBuffer() CommandBuffer {
550438
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer.
551439
type CommandBuffer struct {
552440
	commandBuffer unsafe.Pointer
553441
}
554442

555
// Present registers a drawable presentation to occur as soon as possible.
443
// PresentDrawable registers a drawable presentation to occur as soon as possible.
556444
//
557445
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
558
func (cb CommandBuffer) Present(d Drawable) {
559
	C.CommandBuffer_Present(cb.commandBuffer, d.drawable)
446
func (cb CommandBuffer) PresentDrawable(d Drawable) {
447
	C.CommandBuffer_PresentDrawable(cb.commandBuffer, d.Drawable())
560448
}
561449

562450
// Commit commits this command buffer for execution as soon as possible.
563451
//
564452
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit.
@@ -693,17 +581,25 @@ func (l Library) MakeFunction(name string) (Function, error) {
693581
//
694582
// Reference: https://developer.apple.com/documentation/metal/mtltexture.
695583
type Texture struct {
696584
	texture unsafe.Pointer
697585

586
	// TODO: Change these fields into methods.
587

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

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

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

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

707603
// GetBytes copies a block of pixels from the storage allocation of texture
708604
// slice zero into system memory at a specified address.
709605
//
mtl.h
FileFile
@@ -72,25 +72,10 @@ struct Size {
7272
struct Region {
7373
	struct Origin Origin;
7474
	struct Size   Size;
7575
};
7676

77
void * MakeLayer();
78

79
uint16_t     Layer_PixelFormat(void * layer);
80
void         Layer_SetDevice(void * layer, void * device);
81
const char * Layer_SetPixelFormat(void * layer, uint16_t pixelFormat);
82
const char * Layer_SetMaximumDrawableCount(void * layer, uint_t maximumDrawableCount);
83
void         Layer_SetDisplaySyncEnabled(void * layer, BOOL displaySyncEnabled);
84
void         Layer_SetDrawableSize(void * layer, double width, double height);
85
void *       Layer_NextDrawable(void * layer);
86

87
void * Drawable_Texture(void * drawable);
88

89
void SetWindowContentViewLayer(void * cocoaWindow, void * layer);
90
void SetWindowContentViewWantsLayer(void * cocoaWindow, BOOL wantsLayer);
91

9277
struct Device CreateSystemDefaultDevice();
9378
struct Devices CopyAllDevices();
9479

9580
BOOL                       Device_SupportsFeatureSet(void * device, uint16_t featureSet);
9681
void *                     Device_MakeCommandQueue(void * device);
@@ -99,11 +84,11 @@ struct RenderPipelineState Device_MakeRenderPipelineState(void * device, struct
9984
void *                     Device_MakeBuffer(void * device, const void * bytes, size_t length, uint16_t options);
10085
void *                     Device_MakeTexture(void * device, struct TextureDescriptor descriptor);
10186

10287
void * CommandQueue_MakeCommandBuffer(void * commandQueue);
10388

104
void   CommandBuffer_Present(void * commandBuffer, void * drawable);
89
void   CommandBuffer_PresentDrawable(void * commandBuffer, void * drawable);
10590
void   CommandBuffer_Commit(void * commandBuffer);
10691
void   CommandBuffer_WaitUntilCompleted(void * commandBuffer);
10792
void * CommandBuffer_MakeRenderCommandEncoder(void * commandBuffer, struct RenderPassDescriptor descriptor);
10893
void * CommandBuffer_MakeBlitCommandEncoder(void * commandBuffer);
10994

mtl.m
FileFile
@@ -1,73 +1,11 @@
11
// +build darwin
22

33
#import <Metal/Metal.h>
4
#import <QuartzCore/QuartzCore.h>
5
#import <Cocoa/Cocoa.h>
6

74
#include <stdlib.h>
8

95
#include "mtl.h"
106

11
void * MakeLayer() {
12
	return [[CAMetalLayer alloc] init];
13
}
14

15
uint16_t Layer_PixelFormat(void * layer) {
16
	return ((CAMetalLayer *)layer).pixelFormat;
17
}
18

19
void Layer_SetDevice(void * layer, void * device) {
20
	((CAMetalLayer *)layer).device = (id<MTLDevice>)device;
21
}
22

23
const char * Layer_SetPixelFormat(void * layer, uint16_t pixelFormat) {
24
	@try {
25
		((CAMetalLayer *)layer).pixelFormat = (MTLPixelFormat)pixelFormat;
26
	}
27
	@catch (NSException * exception) {
28
		return exception.reason.UTF8String;
29
	}
30
	return NULL;
31
}
32

33
const char * Layer_SetMaximumDrawableCount(void * layer, uint_t maximumDrawableCount) {
34
	if (@available(macOS 10.13.2, *)) {
35
		@try {
36
			((CAMetalLayer *)layer).maximumDrawableCount = (NSUInteger)maximumDrawableCount;
37
		}
38
		@catch (NSException * exception) {
39
			return exception.reason.UTF8String;
40
		}
41
	}
42
	return NULL;
43
}
44

45
void Layer_SetDisplaySyncEnabled(void * layer, BOOL displaySyncEnabled) {
46
	((CAMetalLayer *)layer).displaySyncEnabled = displaySyncEnabled;
47
}
48

49
void Layer_SetDrawableSize(void * layer, double width, double height) {
50
	((CAMetalLayer *)layer).drawableSize = (CGSize){width, height};
51
}
52

53
void * Layer_NextDrawable(void * layer) {
54
	return [(CAMetalLayer *)layer nextDrawable];
55
}
56

57
void * Drawable_Texture(void * drawable) {
58
	return ((id<CAMetalDrawable>)drawable).texture;
59
}
60

61
void SetWindowContentViewLayer(void * cocoaWindow, void * layer) {
62
	((NSWindow *)cocoaWindow).contentView.layer = (CAMetalLayer *)layer;
63
}
64

65
void SetWindowContentViewWantsLayer(void * cocoaWindow, BOOL wantsLayer) {
66
	((NSWindow *)cocoaWindow).contentView.wantsLayer = wantsLayer;
67
}
68

697
struct Device CreateSystemDefaultDevice() {
708
	id<MTLDevice> device = MTLCreateSystemDefaultDevice();
719
	if (!device) {
7210
		struct Device d;
7311
		d.Device = NULL;
@@ -160,12 +98,12 @@ void * Device_MakeTexture(void * device, struct TextureDescriptor descriptor) {
16098

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

165
void CommandBuffer_Present(void * commandBuffer, void * drawable) {
166
	[(id<MTLCommandBuffer>)commandBuffer presentDrawable:(id<CAMetalDrawable>)drawable];
103
void CommandBuffer_PresentDrawable(void * commandBuffer, void * drawable) {
104
	[(id<MTLCommandBuffer>)commandBuffer presentDrawable:(id<MTLDrawable>)drawable];
167105
}
168106

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