From c7ab66e0e1d78508f11a56db7b5956c0271e4927 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 19 Jun 2019 00:34:05 +0900 Subject: [PATCH] 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. --- internal/graphicsdriver/metal/driver.go | 73 ++++++---------- internal/graphicsdriver/metal/view_ios.go | 40 ++++++++- internal/graphicsdriver/metal/view_macos.go | 91 ++++++++++++++++++++ internal/graphicsdriver/metal/view_notios.go | 31 ------- 4 files changed, 156 insertions(+), 79 deletions(-) create mode 100644 internal/graphicsdriver/metal/view_macos.go delete mode 100644 internal/graphicsdriver/metal/view_notios.go diff --git a/internal/graphicsdriver/metal/driver.go b/internal/graphicsdriver/metal/driver.go index 471d768bf..7ed6bd170 100644 --- a/internal/graphicsdriver/metal/driver.go +++ b/internal/graphicsdriver/metal/driver.go @@ -283,10 +283,8 @@ type rpsKey struct { } type Driver struct { - window uintptr + view view - device mtl.Device - ml ca.MetalLayer screenRPS mtl.RenderPipelineState rpss map[rpsKey]mtl.RenderPipelineState cq mtl.CommandQueue @@ -339,8 +337,8 @@ func (d *Driver) End() { func (d *Driver) SetWindow(window uintptr) { d.t.Call(func() error { // Note that [NSApp mainWindow] returns nil when the window is borderless. - // Then the window is needed to be given. - d.window = window + // Then the window is needed to be given explicitly. + d.view.setWindow(window) return nil }) } @@ -353,8 +351,8 @@ func (d *Driver) SetVertices(vertices []float32, indices []uint16) { if d.ib != (mtl.Buffer{}) { d.ib.Release() } - d.vb = d.device.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.vb = d.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), mtl.ResourceStorageModeManaged) + d.ib = d.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), mtl.ResourceStorageModeManaged) return nil }) } @@ -390,25 +388,25 @@ func (d *Driver) checkSize(width, height int) { d.maxImageSize = 4096 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf switch { - case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): + case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): 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 - case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): + case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): 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 - case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): + case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): 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 - case d.device.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): + case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): 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 - case d.device.SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): + case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): 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 default: 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 d.t.Call(func() error { - t = d.device.MakeTexture(td) + t = d.view.getMTLDevice().MakeTexture(td) return nil }) 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) { d.t.Call(func() error { - d.ml.SetDrawableSize(width, height) + d.view.setDrawableSize(width, height) return nil }) return &Image{ @@ -482,22 +480,10 @@ func (d *Driver) Reset() error { d.rpss = map[rpsKey]mtl.RenderPipelineState{} } - var err error - d.device, err = mtl.CreateSystemDefaultDevice() - if err != nil { + if err := d.view.reset(); err != nil { 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{ "{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest), "{{.FilterLinear}}": fmt.Sprintf("%d", graphics.FilterLinear), @@ -510,7 +496,7 @@ func (d *Driver) Reset() error { 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 { return err } @@ -527,13 +513,13 @@ func (d *Driver) Reset() error { VertexFunction: vs, FragmentFunction: fs, } - rpld.ColorAttachments[0].PixelFormat = d.ml.PixelFormat() + rpld.ColorAttachments[0].PixelFormat = d.view.colorPixelFormat() rpld.ColorAttachments[0].BlendingEnabled = true rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne - rps, err := d.device.MakeRenderPipelineState(rpld) + rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld) if err != nil { return err } @@ -588,7 +574,7 @@ func (d *Driver) Reset() error { rpld.ColorAttachments[0].DestinationRGBBlendFactor = conv(dst) rpld.ColorAttachments[0].SourceAlphaBlendFactor = conv(src) rpld.ColorAttachments[0].SourceRGBBlendFactor = conv(src) - rps, err := d.device.MakeRenderPipelineState(rpld) + rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld) if err != nil { return err } @@ -603,7 +589,7 @@ func (d *Driver) Reset() error { } } - d.cq = d.device.MakeCommandQueue() + d.cq = d.view.getMTLDevice().MakeCommandQueue() return nil }); err != nil { 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 { if err := d.t.Call(func() error { - // NSView can be changed anytime (probably). Set this everyframe. - setView(d.window, d.ml) + d.view.update() rpd := mtl.RenderPassDescriptor{} if d.dst.screen { @@ -628,9 +613,8 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode var t mtl.Texture if d.dst.screen { if d.screenDrawable == (ca.MetalDrawable{}) { - drawable, err := d.ml.NextDrawable() - if err != nil { - // Drawable is nil. This can happen at the initial state. Let's wait and see. + drawable := d.view.drawable() + if drawable == (ca.MetalDrawable{}) { return nil } d.screenDrawable = drawable @@ -704,10 +688,7 @@ func (d *Driver) ResetSource() { } func (d *Driver) SetVsyncEnabled(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. - d.ml.SetDisplaySyncEnabled(enabled) + d.view.setDisplaySyncEnabled(enabled) } func (d *Driver) VDirection() driver.VDirection { diff --git a/internal/graphicsdriver/metal/view_ios.go b/internal/graphicsdriver/metal/view_ios.go index 3f6385e6b..9ce37913c 100644 --- a/internal/graphicsdriver/metal/view_ios.go +++ b/internal/graphicsdriver/metal/view_ios.go @@ -19,8 +19,44 @@ package metal import ( "github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca" + "github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl" ) -func setView(window uintptr, layer ca.MetalLayer) { - // TODO: Implement this +type view struct { +} + +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{} } diff --git a/internal/graphicsdriver/metal/view_macos.go b/internal/graphicsdriver/metal/view_macos.go new file mode 100644 index 000000000..1a028a04c --- /dev/null +++ b/internal/graphicsdriver/metal/view_macos.go @@ -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 +} diff --git a/internal/graphicsdriver/metal/view_notios.go b/internal/graphicsdriver/metal/view_notios.go deleted file mode 100644 index 3ba6374a3..000000000 --- a/internal/graphicsdriver/metal/view_notios.go +++ /dev/null @@ -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) -}