graphicsdriver/metal: Move MTLDevice and CAMetalLayer to the other struct

On iOS, they are given or included in a MTKView and we don't have
to (or should not) make them. Let's move them to a different
struct.
This commit is contained in:
Hajime Hoshi 2019-06-19 00:34:05 +09:00
parent 57d493a549
commit c7ab66e0e1
4 changed files with 156 additions and 79 deletions

View File

@ -283,10 +283,8 @@ type rpsKey struct {
} }
type Driver struct { type Driver struct {
window uintptr view view
device mtl.Device
ml ca.MetalLayer
screenRPS mtl.RenderPipelineState screenRPS mtl.RenderPipelineState
rpss map[rpsKey]mtl.RenderPipelineState rpss map[rpsKey]mtl.RenderPipelineState
cq mtl.CommandQueue cq mtl.CommandQueue
@ -339,8 +337,8 @@ func (d *Driver) End() {
func (d *Driver) SetWindow(window uintptr) { func (d *Driver) SetWindow(window uintptr) {
d.t.Call(func() error { d.t.Call(func() error {
// Note that [NSApp mainWindow] returns nil when the window is borderless. // Note that [NSApp mainWindow] returns nil when the window is borderless.
// Then the window is needed to be given. // Then the window is needed to be given explicitly.
d.window = window d.view.setWindow(window)
return nil return nil
}) })
} }
@ -353,8 +351,8 @@ func (d *Driver) SetVertices(vertices []float32, indices []uint16) {
if d.ib != (mtl.Buffer{}) { if d.ib != (mtl.Buffer{}) {
d.ib.Release() d.ib.Release()
} }
d.vb = d.device.MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), mtl.ResourceStorageModeManaged) d.vb = d.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), mtl.ResourceStorageModeManaged)
d.ib = d.device.MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), mtl.ResourceStorageModeManaged) d.ib = d.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), mtl.ResourceStorageModeManaged)
return nil return nil
}) })
} }
@ -390,25 +388,25 @@ func (d *Driver) checkSize(width, height int) {
d.maxImageSize = 4096 d.maxImageSize = 4096
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
switch { switch {
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1):
d.maxImageSize = 16384 d.maxImageSize = 16384
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1):
d.maxImageSize = 16384 d.maxImageSize = 16384
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1):
d.maxImageSize = 16384 d.maxImageSize = 16384
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2):
d.maxImageSize = 8192 d.maxImageSize = 8192
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1):
d.maxImageSize = 4096 d.maxImageSize = 4096
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2):
d.maxImageSize = 8192 d.maxImageSize = 8192
case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1):
d.maxImageSize = 4096 d.maxImageSize = 4096
case d.device.SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1):
d.maxImageSize = 16384 d.maxImageSize = 16384
case d.device.SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1):
d.maxImageSize = 8192 d.maxImageSize = 8192
case d.device.SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1): case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1):
d.maxImageSize = 16384 d.maxImageSize = 16384
default: default:
panic("metal: there is no supported feature set") panic("metal: there is no supported feature set")
@ -446,7 +444,7 @@ func (d *Driver) NewImage(width, height int) (driver.Image, error) {
} }
var t mtl.Texture var t mtl.Texture
d.t.Call(func() error { d.t.Call(func() error {
t = d.device.MakeTexture(td) t = d.view.getMTLDevice().MakeTexture(td)
return nil return nil
}) })
return &Image{ return &Image{
@ -459,7 +457,7 @@ func (d *Driver) NewImage(width, height int) (driver.Image, error) {
func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, error) { func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
d.t.Call(func() error { d.t.Call(func() error {
d.ml.SetDrawableSize(width, height) d.view.setDrawableSize(width, height)
return nil return nil
}) })
return &Image{ return &Image{
@ -482,22 +480,10 @@ func (d *Driver) Reset() error {
d.rpss = map[rpsKey]mtl.RenderPipelineState{} d.rpss = map[rpsKey]mtl.RenderPipelineState{}
} }
var err error if err := d.view.reset(); err != nil {
d.device, err = mtl.CreateSystemDefaultDevice()
if err != nil {
return err return err
} }
d.ml = ca.MakeMetalLayer()
d.ml.SetDevice(d.device)
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat
//
// The pixel format for a Metal layer must be MTLPixelFormatBGRA8Unorm,
// MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA16Float, MTLPixelFormatBGRA10_XR, or
// MTLPixelFormatBGRA10_XR_sRGB.
d.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
d.ml.SetMaximumDrawableCount(3)
replaces := map[string]string{ replaces := map[string]string{
"{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest), "{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest),
"{{.FilterLinear}}": fmt.Sprintf("%d", graphics.FilterLinear), "{{.FilterLinear}}": fmt.Sprintf("%d", graphics.FilterLinear),
@ -510,7 +496,7 @@ func (d *Driver) Reset() error {
src = strings.Replace(src, k, v, -1) src = strings.Replace(src, k, v, -1)
} }
lib, err := d.device.MakeLibrary(src, mtl.CompileOptions{}) lib, err := d.view.getMTLDevice().MakeLibrary(src, mtl.CompileOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -527,13 +513,13 @@ func (d *Driver) Reset() error {
VertexFunction: vs, VertexFunction: vs,
FragmentFunction: fs, FragmentFunction: fs,
} }
rpld.ColorAttachments[0].PixelFormat = d.ml.PixelFormat() rpld.ColorAttachments[0].PixelFormat = d.view.colorPixelFormat()
rpld.ColorAttachments[0].BlendingEnabled = true rpld.ColorAttachments[0].BlendingEnabled = true
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero
rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero
rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne
rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne
rps, err := d.device.MakeRenderPipelineState(rpld) rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld)
if err != nil { if err != nil {
return err return err
} }
@ -588,7 +574,7 @@ func (d *Driver) Reset() error {
rpld.ColorAttachments[0].DestinationRGBBlendFactor = conv(dst) rpld.ColorAttachments[0].DestinationRGBBlendFactor = conv(dst)
rpld.ColorAttachments[0].SourceAlphaBlendFactor = conv(src) rpld.ColorAttachments[0].SourceAlphaBlendFactor = conv(src)
rpld.ColorAttachments[0].SourceRGBBlendFactor = conv(src) rpld.ColorAttachments[0].SourceRGBBlendFactor = conv(src)
rps, err := d.device.MakeRenderPipelineState(rpld) rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld)
if err != nil { if err != nil {
return err return err
} }
@ -603,7 +589,7 @@ func (d *Driver) Reset() error {
} }
} }
d.cq = d.device.MakeCommandQueue() d.cq = d.view.getMTLDevice().MakeCommandQueue()
return nil return nil
}); err != nil { }); err != nil {
return err return err
@ -614,8 +600,7 @@ func (d *Driver) Reset() error {
func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error { func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
if err := d.t.Call(func() error { if err := d.t.Call(func() error {
// NSView can be changed anytime (probably). Set this everyframe. d.view.update()
setView(d.window, d.ml)
rpd := mtl.RenderPassDescriptor{} rpd := mtl.RenderPassDescriptor{}
if d.dst.screen { if d.dst.screen {
@ -628,9 +613,8 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode
var t mtl.Texture var t mtl.Texture
if d.dst.screen { if d.dst.screen {
if d.screenDrawable == (ca.MetalDrawable{}) { if d.screenDrawable == (ca.MetalDrawable{}) {
drawable, err := d.ml.NextDrawable() drawable := d.view.drawable()
if err != nil { if drawable == (ca.MetalDrawable{}) {
// Drawable is nil. This can happen at the initial state. Let's wait and see.
return nil return nil
} }
d.screenDrawable = drawable d.screenDrawable = drawable
@ -704,10 +688,7 @@ func (d *Driver) ResetSource() {
} }
func (d *Driver) SetVsyncEnabled(enabled bool) { func (d *Driver) SetVsyncEnabled(enabled bool) {
// TODO: Now SetVsyncEnabled is called only from the main thread, and d.t.Run is not available since d.view.setDisplaySyncEnabled(enabled)
// recursive function call via Run is forbidden.
// Fix this to use d.t.Run to avoid confusion.
d.ml.SetDisplaySyncEnabled(enabled)
} }
func (d *Driver) VDirection() driver.VDirection { func (d *Driver) VDirection() driver.VDirection {

View File

@ -19,8 +19,44 @@ package metal
import ( import (
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca" "github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
) )
func setView(window uintptr, layer ca.MetalLayer) { type view struct {
// TODO: Implement this }
func (v *view) setWindow(window uintptr) {
panic("metal: setWindow cannot be called on iOS")
}
func (v *view) setDrawableSize(width, height int) {
// Do nothing
}
func (v *view) getMTLDevice() mtl.Device {
// TODO: Implement this
return mtl.Device{}
}
func (v *view) setDisplaySyncEnabled(enabled bool) {
// Do nothing
}
func (v *view) colorPixelFormat() mtl.PixelFormat {
// TODO: Implement this
return 0
}
func (v *view) reset() error {
// Do nothing
return nil
}
func (v *view) update() {
// Do nothing
}
func (v *view) drawable() ca.MetalDrawable {
// TODO: Implemnt this
return ca.MetalDrawable{}
} }

View File

@ -0,0 +1,91 @@
// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build darwin
// +build !ios
package metal
import (
"unsafe"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ns"
)
type view struct {
window uintptr
device mtl.Device
ml ca.MetalLayer
}
func (v *view) setWindow(window uintptr) {
v.window = window
}
func (v *view) setDrawableSize(width, height int) {
v.ml.SetDrawableSize(width, height)
}
func (v *view) getMTLDevice() mtl.Device {
return v.device
}
func (v *view) setDisplaySyncEnabled(enabled bool) {
// TODO: Now SetVsyncEnabled is called only from the main thread, and d.t.Run is not available since
// recursive function call via Run is forbidden.
// Fix this to use d.t.Run to avoid confusion.
v.ml.SetDisplaySyncEnabled(enabled)
}
func (v *view) colorPixelFormat() mtl.PixelFormat {
return v.ml.PixelFormat()
}
func (v *view) reset() error {
var err error
v.device, err = mtl.CreateSystemDefaultDevice()
if err != nil {
return err
}
v.ml = ca.MakeMetalLayer()
v.ml.SetDevice(v.device)
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat
//
// The pixel format for a Metal layer must be MTLPixelFormatBGRA8Unorm,
// MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA16Float, MTLPixelFormatBGRA10_XR, or
// MTLPixelFormatBGRA10_XR_sRGB.
v.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
v.ml.SetMaximumDrawableCount(3)
return nil
}
func (v *view) update() {
// NSView can be changed anytime (probably). Set this everyframe.
cocoaWindow := ns.NewWindow(unsafe.Pointer(v.window))
cocoaWindow.ContentView().SetLayer(v.ml)
cocoaWindow.ContentView().SetWantsLayer(true)
}
func (v *view) drawable() ca.MetalDrawable {
d, err := v.ml.NextDrawable()
if err != nil {
// Drawable is nil. This can happen at the initial state. Let's wait and see.
return ca.MetalDrawable{}
}
return d
}

View File

@ -1,31 +0,0 @@
// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build darwin
// +build !ios
package metal
import (
"unsafe"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ns"
)
func setView(window uintptr, layer ca.MetalLayer) {
cocoaWindow := ns.NewWindow(unsafe.Pointer(window))
cocoaWindow.ContentView().SetLayer(layer)
cocoaWindow.ContentView().SetWantsLayer(true)
}