From 7484df0c5e8b3ff4a1ce6e755831fa5b3df2f8f9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 17 Jun 2022 02:10:29 +0900 Subject: [PATCH] internal/graphicsdriver/metal: replace Get with NewGraphics This is a prepartion to return an error when a graphics driver, especially DirectX, fails to initialize. Updates #2142 --- .../graphicsdriver/metal/graphics_darwin.go | 46 +++++++------------ internal/ui/graphics.go | 22 +++++---- internal/ui/ui_android.go | 12 ++--- internal/ui/ui_cbackend.go | 14 +++--- internal/ui/ui_glfw.go | 2 +- internal/ui/ui_glfw_darwin.go | 26 ++++++----- internal/ui/ui_glfw_unix.go | 12 ++--- internal/ui/ui_glfw_windows.go | 12 ++--- internal/ui/ui_ios.go | 30 ++++++------ internal/ui/ui_js.go | 14 +++--- internal/ui/ui_mobile.go | 4 +- 11 files changed, 97 insertions(+), 97 deletions(-) diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index f2a7d9648..0c84acb0a 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -19,7 +19,6 @@ import ( "math" "sort" "strings" - "sync" "unsafe" "github.com/hajimehoshi/ebiten/v2/internal/graphics" @@ -347,35 +346,24 @@ const ( noStencil ) -var ( - // isMetalAvailable reports whether Metal is available or not. - isMetalAvailable bool - isMetalAvailableOnce sync.Once -) - -var theGraphics Graphics - -func Get() *Graphics { - isMetalAvailableOnce.Do(func() { - if !supportsMetal() { - return - } - - // Initialize isMetalAvailable on the main thread. - // TODO: Now ui.chooseGraphicsDriver is called on the main thread. Add an assertion. - - // On old mac devices like iMac 2011, Metal is not supported (#779). - // TODO: Is there a better way to check whether Metal is available or not? - // It seems OK to call MTLCreateSystemDefaultDevice multiple times, so this should be fine. - if _, ok := mtl.CreateSystemDefaultDevice(); !ok { - return - } - isMetalAvailable = true - }) - if !isMetalAvailable { - return nil +// NewGraphics creates an implementation of graphicsdriver.Graphcis for Metal. +// The returned graphics value is nil iff the error is not nil. +func NewGraphics() (graphicsdriver.Graphics, error) { + if !supportsMetal() { + return nil, fmt.Errorf("metal: Metal is not supported in this environment") } - return &theGraphics + + // Initialize isMetalAvailable on the main thread. + // TODO: Now ui.chooseGraphicsDriver is called on the main thread. Add an assertion. + + // On old mac devices like iMac 2011, Metal is not supported (#779). + // TODO: Is there a better way to check whether Metal is available or not? + // It seems OK to call MTLCreateSystemDefaultDevice multiple times, so this should be fine. + if _, ok := mtl.CreateSystemDefaultDevice(); !ok { + return nil, fmt.Errorf("metal: mtl.CreateSystemDefaultDevice failed") + } + + return &Graphics{}, nil } func (g *Graphics) Begin() error { diff --git a/internal/ui/graphics.go b/internal/ui/graphics.go index 1ce0b8edf..3df51e6a6 100644 --- a/internal/ui/graphics.go +++ b/internal/ui/graphics.go @@ -21,19 +21,19 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -type graphicsDriverGetter interface { +type graphicsDriverCreator interface { newAuto() (graphicsdriver.Graphics, error) newOpenGL() (graphicsdriver.Graphics, error) getDirectX() graphicsdriver.Graphics - getMetal() graphicsdriver.Graphics + newMetal() (graphicsdriver.Graphics, error) } -func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics, error) { +func chooseGraphicsDriver(creator graphicsDriverCreator) (graphicsdriver.Graphics, error) { const envName = "EBITEN_GRAPHICS_LIBRARY" switch env := os.Getenv(envName); env { case "", "auto": - g, err := getter.newAuto() + g, err := creator.newAuto() if err != nil { return nil, err } @@ -42,7 +42,7 @@ func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics, } return g, nil case "opengl": - g, err := getter.newOpenGL() + g, err := creator.newOpenGL() if err != nil { return nil, err } @@ -51,15 +51,19 @@ func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics, } return g, nil case "directx": - if g := getter.getDirectX(); g != nil { + if g := creator.getDirectX(); g != nil { return g, nil } return nil, fmt.Errorf("ui: %s=%s is specified but DirectX is not available.", envName, env) case "metal": - if g := getter.getMetal(); g != nil { - return g, nil + g, err := creator.newMetal() + if err != nil { + return nil, err } - return nil, fmt.Errorf("ui: %s=%s is specified but Metal is not available", envName, env) + if g == nil { + return nil, fmt.Errorf("ui: %s=%s is specified but Metal is not available", envName, env) + } + return g, nil default: return nil, fmt.Errorf("ui: an unsupported graphics library is specified: %s", env) } diff --git a/internal/ui/ui_android.go b/internal/ui/ui_android.go index 74c2fbcf8..4d6418ed8 100644 --- a/internal/ui/ui_android.go +++ b/internal/ui/ui_android.go @@ -19,22 +19,22 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" ) -type graphicsDriverGetterImpl struct { +type graphicsDriverCreatorImpl struct { gomobileBuild bool } -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { return g.newOpenGL() } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return nil, nil } diff --git a/internal/ui/ui_cbackend.go b/internal/ui/ui_cbackend.go index 5e8561884..15f569d4d 100644 --- a/internal/ui/ui_cbackend.go +++ b/internal/ui/ui_cbackend.go @@ -25,22 +25,22 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" ) -type graphicsDriverGetterImpl struct{} +type graphicsDriverCreatorImpl struct{} -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { return g.newOpenGL() } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return nil, nil } const deviceScaleFactor = 1 @@ -58,7 +58,7 @@ type userInterfaceImpl struct { func (u *userInterfaceImpl) Run(game Game) error { u.context = newContext(game) - g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{}) + g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{}) if err != nil { return err } diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 0e2e8e563..06810d2c4 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -834,7 +834,7 @@ func (u *userInterfaceImpl) init() error { } glfw.WindowHint(glfw.TransparentFramebuffer, glfwTransparent) - g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{ + g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{ transparent: transparent, }) if err != nil { diff --git a/internal/ui/ui_glfw_darwin.go b/internal/ui/ui_glfw_darwin.go index 56310d19d..d6d8df680 100644 --- a/internal/ui/ui_glfw_darwin.go +++ b/internal/ui/ui_glfw_darwin.go @@ -248,36 +248,40 @@ package ui import "C" import ( + "fmt" + "github.com/hajimehoshi/ebiten/v2/internal/glfw" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" ) -type graphicsDriverGetterImpl struct { +type graphicsDriverCreatorImpl struct { transparent bool } -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { - if m := g.getMetal(); m != nil { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { + m, err1 := g.newMetal() + if err1 == nil { return m, nil } - return g.newOpenGL() + o, err2 := g.newOpenGL() + if err2 == nil { + return o, nil + } + return nil, fmt.Errorf("ui: failed to choose graphics drivers: Metal: %v, OpenGL: %v", err1, err2) } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - if m := metal.Get(); m != nil { - return m - } - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return metal.NewGraphics() } // clearVideoModeScaleCache must be called from the main thread. diff --git a/internal/ui/ui_glfw_unix.go b/internal/ui/ui_glfw_unix.go index 7ac42f4f1..162048ca5 100644 --- a/internal/ui/ui_glfw_unix.go +++ b/internal/ui/ui_glfw_unix.go @@ -31,24 +31,24 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" ) -type graphicsDriverGetterImpl struct { +type graphicsDriverCreatorImpl struct { transparent bool } -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { return g.newOpenGL() } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return nil, nil } type videoModeScaleCacheKey struct{ X, Y int } diff --git a/internal/ui/ui_glfw_windows.go b/internal/ui/ui_glfw_windows.go index 83f8d149f..23f04f78b 100644 --- a/internal/ui/ui_glfw_windows.go +++ b/internal/ui/ui_glfw_windows.go @@ -31,22 +31,22 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" ) -type graphicsDriverGetterImpl struct { +type graphicsDriverCreatorImpl struct { transparent bool } -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { if d := g.getDirectX(); d != nil { return d, nil } return g.newOpenGL() } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (g *graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (g *graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { if g.transparent { return nil } @@ -56,8 +56,8 @@ func (g *graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return nil, nil } const ( diff --git a/internal/ui/ui_ios.go b/internal/ui/ui_ios.go index d09fa950d..db780f217 100644 --- a/internal/ui/ui_ios.go +++ b/internal/ui/ui_ios.go @@ -18,40 +18,42 @@ package ui import ( + "fmt" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" ) -type graphicsDriverGetterImpl struct { +type graphicsDriverCreatorImpl struct { gomobileBuild bool } -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { - if m := g.getMetal(); m != nil { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { + m, err1 := g.newMetal() + if err1 == nil { return m, nil } - return g.newOpenGL() + o, err2 := g.newOpenGL() + if err2 == nil { + return o, nil + } + return nil, fmt.Errorf("ui: failed to choose graphics drivers: Metal: %v, OpenGL: %v", err1, err2) } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (g *graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - // When gomobile-build is used, GL functions must be called via - // gl.Context so that they are called on the appropriate thread. +func (g *graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { if g.gomobileBuild { - return nil + return nil, fmt.Errorf("ui: Metal is not available with gomobile-build") } - if m := metal.Get(); m != nil { - return m - } - return nil + return metal.NewGraphics() } func SetUIView(uiview uintptr) { diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index c4002e8f3..c2006c9ec 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -25,22 +25,22 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/hooks" ) -type graphicsDriverGetterImpl struct{} +type graphicsDriverCreatorImpl struct{} -func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { +func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) { return g.newOpenGL() } -func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { +func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { return opengl.NewGraphics() } -func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { +func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics { return nil } -func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { - return nil +func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return nil, nil } var ( @@ -609,7 +609,7 @@ func (u *userInterfaceImpl) Run(game Game) error { } } u.running = true - g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{}) + g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{}) if err != nil { return err } diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 7df1adce6..31e4a984c 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -270,7 +270,7 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) { }() u.context = newContext(game) - g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{ + g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{ gomobileBuild: mainloop, }) if err != nil { @@ -279,6 +279,8 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) { u.graphicsDriver = g if mainloop { + // When gomobile-build is used, GL functions must be called via + // gl.Context so that they are called on the appropriate thread. ctx := <-glContextCh g.(*opengl.Graphics).SetGomobileGLContext(ctx) } else {