Skip to content

Commit 81c7196

Browse files
dmitshurnigeltao
authored andcommitted
shiny/driver/mtldriver: add start of a Metal API-based driver
The OpenGL API has been deprecated on macOS as of version 10.14.¹ The replacement for OpenGL on macOS is the Metal API.² This change adds the start of a Metal API-based shiny driver. It can be activated by specifying the -tags=metal build tag on macOS 10.13+. For example: go run -tags='example metal' golang.org/x/exp/shiny/example/icongallery The goal of this CL has been to create an MVP. As a result, the focus is on simplicity and correctness. Performance optimizations can come later. The Metal API is currently used only to present the final pixels to the screen. All rendering is performed on the CPU via the image/draw algorithms (see https://blog.golang.org/go-imagedraw-package). Future work is to use mtl.Buffer, mtl.Texture, etc., to do more of the rendering work on the GPU. From what I've observed so far, the Metal API is looking to be a great fit for implementing the screen.Screen interface. But we'll learn more when it's used to a greater extent of its full performance potential. GLFW v3.2.1 is used for window creation, receiving input events, etc. Package dmitri.shuralyov.com/gpu/mtl is used for Metal API access. Helper packages for Apple's Core Animation and AppKit APIs are copied. References: ¹ https://developer.apple.com/documentation/macos_release_notes/macos_mojave_10_14_release_notes?language=objc#3035786 ² https://developer.apple.com/metal/ Change-Id: I0e02d660b776820ca499bfe7d67e47a9866d530c Reviewed-on: https://go-review.googlesource.com/c/exp/+/171025 Run-TryBot: Dmitri Shuralyov <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Nigel Tao <[email protected]>
1 parent 0cae2de commit 81c7196

15 files changed

+812
-1
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ module golang.org/x/exp
33
go 1.12
44

55
require (
6+
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9
67
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
8+
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1
79
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
810
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
911
golang.org/x/mod v0.1.0

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
2+
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
13
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
24
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
5+
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
6+
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
37
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
48
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
59
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=

shiny/driver/driver_darwin.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin
5+
// +build darwin,!metal
66

77
package driver
88

shiny/driver/mtldriver/buffer.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
package mtldriver
8+
9+
import "image"
10+
11+
// bufferImpl implements screen.Buffer.
12+
type bufferImpl struct {
13+
rgba *image.RGBA
14+
}
15+
16+
func (*bufferImpl) Release() {}
17+
func (b *bufferImpl) Size() image.Point { return b.rgba.Rect.Max }
18+
func (b *bufferImpl) Bounds() image.Rectangle { return b.rgba.Rect }
19+
func (b *bufferImpl) RGBA() *image.RGBA { return b.rgba }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
// Package appkit provides access to Apple's AppKit API
8+
// (https://developer.apple.com/documentation/appkit).
9+
//
10+
// This package is in very early stages of development.
11+
// It's a minimal implementation with scope limited to
12+
// supporting mtldriver.
13+
//
14+
// It was copied from dmitri.shuralyov.com/gpu/mtl/example/movingtriangle/internal/ns.
15+
package appkit
16+
17+
import (
18+
"unsafe"
19+
20+
"golang.org/x/exp/shiny/driver/mtldriver/internal/coreanim"
21+
)
22+
23+
/*
24+
#include "appkit.h"
25+
*/
26+
import "C"
27+
28+
// Window is a window that an app displays on the screen.
29+
//
30+
// Reference: https://developer.apple.com/documentation/appkit/nswindow.
31+
type Window struct {
32+
window unsafe.Pointer
33+
}
34+
35+
// NewWindow returns a Window that wraps an existing NSWindow * pointer.
36+
func NewWindow(window unsafe.Pointer) Window {
37+
return Window{window}
38+
}
39+
40+
// ContentView returns the window's content view, the highest accessible View
41+
// in the window's view hierarchy.
42+
//
43+
// Reference: https://developer.apple.com/documentation/appkit/nswindow/1419160-contentview.
44+
func (w Window) ContentView() View {
45+
return View{C.Window_ContentView(w.window)}
46+
}
47+
48+
// View is the infrastructure for drawing, printing, and handling events in an app.
49+
//
50+
// Reference: https://developer.apple.com/documentation/appkit/nsview.
51+
type View struct {
52+
view unsafe.Pointer
53+
}
54+
55+
// SetLayer sets v.layer to l.
56+
//
57+
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483298-layer.
58+
func (v View) SetLayer(l coreanim.Layer) {
59+
C.View_SetLayer(v.view, l.Layer())
60+
}
61+
62+
// SetWantsLayer sets v.wantsLayer to wantsLayer.
63+
//
64+
// Reference: https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer.
65+
func (v View) SetWantsLayer(wantsLayer bool) {
66+
C.View_SetWantsLayer(v.view, toCBool(wantsLayer))
67+
}
68+
69+
func toCBool(b bool) C.BOOL {
70+
if b {
71+
return 1
72+
}
73+
return 0
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
typedef signed char BOOL;
8+
9+
void * Window_ContentView(void * window);
10+
11+
void View_SetLayer(void * view, void * layer);
12+
void View_SetWantsLayer(void * view, BOOL wantsLayer);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
#import <Cocoa/Cocoa.h>
8+
#include "appkit.h"
9+
10+
void * Window_ContentView(void * window) {
11+
return ((NSWindow *)window).contentView;
12+
}
13+
14+
void View_SetLayer(void * view, void * layer) {
15+
((NSView *)view).layer = (CALayer *)layer;
16+
}
17+
18+
void View_SetWantsLayer(void * view, BOOL wantsLayer) {
19+
((NSView *)view).wantsLayer = wantsLayer;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
// Package coreanim provides access to Apple's Core Animation API
8+
// (https://developer.apple.com/documentation/quartzcore).
9+
//
10+
// This package is in very early stages of development.
11+
// It's a minimal implementation with scope limited to
12+
// supporting mtldriver.
13+
//
14+
// It was copied from dmitri.shuralyov.com/gpu/mtl/example/movingtriangle/internal/ca.
15+
package coreanim
16+
17+
import (
18+
"errors"
19+
"unsafe"
20+
21+
"dmitri.shuralyov.com/gpu/mtl"
22+
)
23+
24+
/*
25+
#cgo LDFLAGS: -framework QuartzCore -framework Foundation
26+
#include "coreanim.h"
27+
*/
28+
import "C"
29+
30+
// Layer is an object that manages image-based content and
31+
// allows you to perform animations on that content.
32+
//
33+
// Reference: https://developer.apple.com/documentation/quartzcore/calayer.
34+
type Layer interface {
35+
// Layer returns the underlying CALayer * pointer.
36+
Layer() unsafe.Pointer
37+
}
38+
39+
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
40+
//
41+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
42+
type MetalLayer struct {
43+
metalLayer unsafe.Pointer
44+
}
45+
46+
// MakeMetalLayer creates a new Core Animation Metal layer.
47+
//
48+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
49+
func MakeMetalLayer() MetalLayer {
50+
return MetalLayer{C.MakeMetalLayer()}
51+
}
52+
53+
// Layer implements the Layer interface.
54+
func (ml MetalLayer) Layer() unsafe.Pointer { return ml.metalLayer }
55+
56+
// PixelFormat returns the pixel format of textures for rendering layer content.
57+
//
58+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
59+
func (ml MetalLayer) PixelFormat() mtl.PixelFormat {
60+
return mtl.PixelFormat(C.MetalLayer_PixelFormat(ml.metalLayer))
61+
}
62+
63+
// SetDevice sets the Metal device responsible for the layer's drawable resources.
64+
//
65+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
66+
func (ml MetalLayer) SetDevice(device mtl.Device) {
67+
C.MetalLayer_SetDevice(ml.metalLayer, device.Device())
68+
}
69+
70+
// SetPixelFormat controls the pixel format of textures for rendering layer content.
71+
//
72+
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,
73+
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
74+
// SetPixelFormat panics for other values.
75+
//
76+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
77+
func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
78+
e := C.MetalLayer_SetPixelFormat(ml.metalLayer, C.uint16_t(pf))
79+
if e != nil {
80+
panic(errors.New(C.GoString(e)))
81+
}
82+
}
83+
84+
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
85+
// managed by Core Animation.
86+
//
87+
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
88+
//
89+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
90+
func (ml MetalLayer) SetMaximumDrawableCount(count int) {
91+
e := C.MetalLayer_SetMaximumDrawableCount(ml.metalLayer, C.uint_t(count))
92+
if e != nil {
93+
panic(errors.New(C.GoString(e)))
94+
}
95+
}
96+
97+
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
98+
// are synchronized with the display's refresh rate.
99+
//
100+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
101+
func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
102+
C.MetalLayer_SetDisplaySyncEnabled(ml.metalLayer, toCBool(enabled))
103+
}
104+
105+
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
106+
//
107+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
108+
func (ml MetalLayer) SetDrawableSize(width, height int) {
109+
C.MetalLayer_SetDrawableSize(ml.metalLayer, C.double(width), C.double(height))
110+
}
111+
112+
// NextDrawable returns a Metal drawable.
113+
//
114+
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
115+
func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
116+
md := C.MetalLayer_NextDrawable(ml.metalLayer)
117+
if md == nil {
118+
return MetalDrawable{}, errors.New("nextDrawable returned nil")
119+
}
120+
121+
return MetalDrawable{md}, nil
122+
}
123+
124+
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
125+
//
126+
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
127+
type MetalDrawable struct {
128+
metalDrawable unsafe.Pointer
129+
}
130+
131+
// Drawable implements the mtl.Drawable interface.
132+
func (md MetalDrawable) Drawable() unsafe.Pointer { return md.metalDrawable }
133+
134+
// Texture returns a Metal texture object representing the drawable object's content.
135+
//
136+
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
137+
func (md MetalDrawable) Texture() mtl.Texture {
138+
return mtl.NewTexture(C.MetalDrawable_Texture(md.metalDrawable))
139+
}
140+
141+
func toCBool(b bool) C.BOOL {
142+
if b {
143+
return 1
144+
}
145+
return 0
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
typedef signed char BOOL;
8+
typedef unsigned long uint_t;
9+
typedef unsigned short uint16_t;
10+
11+
void * MakeMetalLayer();
12+
13+
uint16_t MetalLayer_PixelFormat(void * metalLayer);
14+
void MetalLayer_SetDevice(void * metalLayer, void * device);
15+
const char * MetalLayer_SetPixelFormat(void * metalLayer, uint16_t pixelFormat);
16+
const char * MetalLayer_SetMaximumDrawableCount(void * metalLayer, uint_t maximumDrawableCount);
17+
void MetalLayer_SetDisplaySyncEnabled(void * metalLayer, BOOL displaySyncEnabled);
18+
void MetalLayer_SetDrawableSize(void * metalLayer, double width, double height);
19+
void * MetalLayer_NextDrawable(void * metalLayer);
20+
21+
void * MetalDrawable_Texture(void * drawable);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin
6+
7+
#import <QuartzCore/QuartzCore.h>
8+
#include "coreanim.h"
9+
10+
void * MakeMetalLayer() {
11+
return [[CAMetalLayer alloc] init];
12+
}
13+
14+
uint16_t MetalLayer_PixelFormat(void * metalLayer) {
15+
return ((CAMetalLayer *)metalLayer).pixelFormat;
16+
}
17+
18+
void MetalLayer_SetDevice(void * metalLayer, void * device) {
19+
((CAMetalLayer *)metalLayer).device = (id<MTLDevice>)device;
20+
}
21+
22+
const char * MetalLayer_SetPixelFormat(void * metalLayer, uint16_t pixelFormat) {
23+
@try {
24+
((CAMetalLayer *)metalLayer).pixelFormat = (MTLPixelFormat)pixelFormat;
25+
}
26+
@catch (NSException * exception) {
27+
return exception.reason.UTF8String;
28+
}
29+
return NULL;
30+
}
31+
32+
const char * MetalLayer_SetMaximumDrawableCount(void * metalLayer, uint_t maximumDrawableCount) {
33+
if (@available(macOS 10.13.2, *)) {
34+
@try {
35+
((CAMetalLayer *)metalLayer).maximumDrawableCount = (NSUInteger)maximumDrawableCount;
36+
}
37+
@catch (NSException * exception) {
38+
return exception.reason.UTF8String;
39+
}
40+
}
41+
return NULL;
42+
}
43+
44+
void MetalLayer_SetDisplaySyncEnabled(void * metalLayer, BOOL displaySyncEnabled) {
45+
((CAMetalLayer *)metalLayer).displaySyncEnabled = displaySyncEnabled;
46+
}
47+
48+
void MetalLayer_SetDrawableSize(void * metalLayer, double width, double height) {
49+
((CAMetalLayer *)metalLayer).drawableSize = (CGSize){width, height};
50+
}
51+
52+
void * MetalLayer_NextDrawable(void * metalLayer) {
53+
return [(CAMetalLayer *)metalLayer nextDrawable];
54+
}
55+
56+
void * MetalDrawable_Texture(void * metalDrawable) {
57+
return ((id<CAMetalDrawable>)metalDrawable).texture;
58+
}

0 commit comments

Comments
 (0)