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
This commit is contained in:
Hajime Hoshi 2022-06-17 02:10:29 +09:00
parent a6d415ebf2
commit 7484df0c5e
11 changed files with 97 additions and 97 deletions

View File

@ -19,7 +19,6 @@ import (
"math" "math"
"sort" "sort"
"strings" "strings"
"sync"
"unsafe" "unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
@ -347,35 +346,24 @@ const (
noStencil noStencil
) )
var ( // NewGraphics creates an implementation of graphicsdriver.Graphcis for Metal.
// isMetalAvailable reports whether Metal is available or not. // The returned graphics value is nil iff the error is not nil.
isMetalAvailable bool func NewGraphics() (graphicsdriver.Graphics, error) {
isMetalAvailableOnce sync.Once if !supportsMetal() {
) return nil, fmt.Errorf("metal: Metal is not supported in this environment")
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
} }
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 { func (g *Graphics) Begin() error {

View File

@ -21,19 +21,19 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
) )
type graphicsDriverGetter interface { type graphicsDriverCreator interface {
newAuto() (graphicsdriver.Graphics, error) newAuto() (graphicsdriver.Graphics, error)
newOpenGL() (graphicsdriver.Graphics, error) newOpenGL() (graphicsdriver.Graphics, error)
getDirectX() graphicsdriver.Graphics 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" const envName = "EBITEN_GRAPHICS_LIBRARY"
switch env := os.Getenv(envName); env { switch env := os.Getenv(envName); env {
case "", "auto": case "", "auto":
g, err := getter.newAuto() g, err := creator.newAuto()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -42,7 +42,7 @@ func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics,
} }
return g, nil return g, nil
case "opengl": case "opengl":
g, err := getter.newOpenGL() g, err := creator.newOpenGL()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -51,15 +51,19 @@ func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics,
} }
return g, nil return g, nil
case "directx": case "directx":
if g := getter.getDirectX(); g != nil { if g := creator.getDirectX(); g != nil {
return g, nil return g, nil
} }
return nil, fmt.Errorf("ui: %s=%s is specified but DirectX is not available.", envName, env) return nil, fmt.Errorf("ui: %s=%s is specified but DirectX is not available.", envName, env)
case "metal": case "metal":
if g := getter.getMetal(); g != nil { g, err := creator.newMetal()
return g, nil 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: default:
return nil, fmt.Errorf("ui: an unsupported graphics library is specified: %s", env) return nil, fmt.Errorf("ui: an unsupported graphics library is specified: %s", env)
} }

View File

@ -19,22 +19,22 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct { type graphicsDriverCreatorImpl struct {
gomobileBuild bool gomobileBuild bool
} }
func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) {
return g.newOpenGL() return g.newOpenGL()
} }
func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) {
return opengl.NewGraphics() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil return nil, nil
} }

View File

@ -25,22 +25,22 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "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() return g.newOpenGL()
} }
func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) {
return opengl.NewGraphics() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil return nil, nil
} }
const deviceScaleFactor = 1 const deviceScaleFactor = 1
@ -58,7 +58,7 @@ type userInterfaceImpl struct {
func (u *userInterfaceImpl) Run(game Game) error { func (u *userInterfaceImpl) Run(game Game) error {
u.context = newContext(game) u.context = newContext(game)
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{}) g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{})
if err != nil { if err != nil {
return err return err
} }

View File

@ -834,7 +834,7 @@ func (u *userInterfaceImpl) init() error {
} }
glfw.WindowHint(glfw.TransparentFramebuffer, glfwTransparent) glfw.WindowHint(glfw.TransparentFramebuffer, glfwTransparent)
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{ g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{
transparent: transparent, transparent: transparent,
}) })
if err != nil { if err != nil {

View File

@ -248,36 +248,40 @@ package ui
import "C" import "C"
import ( import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/glfw" "github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct { type graphicsDriverCreatorImpl struct {
transparent bool transparent bool
} }
func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) {
if m := g.getMetal(); m != nil { m, err1 := g.newMetal()
if err1 == nil {
return m, 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() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
if m := metal.Get(); m != nil { return metal.NewGraphics()
return m
}
return nil
} }
// clearVideoModeScaleCache must be called from the main thread. // clearVideoModeScaleCache must be called from the main thread.

View File

@ -31,24 +31,24 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct { type graphicsDriverCreatorImpl struct {
transparent bool transparent bool
} }
func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) {
return g.newOpenGL() return g.newOpenGL()
} }
func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) {
return opengl.NewGraphics() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil return nil, nil
} }
type videoModeScaleCacheKey struct{ X, Y int } type videoModeScaleCacheKey struct{ X, Y int }

View File

@ -31,22 +31,22 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
) )
type graphicsDriverGetterImpl struct { type graphicsDriverCreatorImpl struct {
transparent bool transparent bool
} }
func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) {
if d := g.getDirectX(); d != nil { if d := g.getDirectX(); d != nil {
return d, nil return d, nil
} }
return g.newOpenGL() return g.newOpenGL()
} }
func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) {
return opengl.NewGraphics() return opengl.NewGraphics()
} }
func (g *graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (g *graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
if g.transparent { if g.transparent {
return nil return nil
} }
@ -56,8 +56,8 @@ func (g *graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil return nil, nil
} }
const ( const (

View File

@ -18,40 +18,42 @@
package ui package ui
import ( import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct { type graphicsDriverCreatorImpl struct {
gomobileBuild bool gomobileBuild bool
} }
func (g *graphicsDriverGetterImpl) newAuto() (graphicsdriver.Graphics, error) { func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, error) {
if m := g.getMetal(); m != nil { m, err1 := g.newMetal()
if err1 == nil {
return m, 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() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (g *graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (g *graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
// When gomobile-build is used, GL functions must be called via
// gl.Context so that they are called on the appropriate thread.
if g.gomobileBuild { if g.gomobileBuild {
return nil return nil, fmt.Errorf("ui: Metal is not available with gomobile-build")
} }
if m := metal.Get(); m != nil { return metal.NewGraphics()
return m
}
return nil
} }
func SetUIView(uiview uintptr) { func SetUIView(uiview uintptr) {

View File

@ -25,22 +25,22 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "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() return g.newOpenGL()
} }
func (*graphicsDriverGetterImpl) newOpenGL() (graphicsdriver.Graphics, error) { func (*graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) {
return opengl.NewGraphics() return opengl.NewGraphics()
} }
func (*graphicsDriverGetterImpl) getDirectX() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) getDirectX() graphicsdriver.Graphics {
return nil return nil
} }
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics { func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil return nil, nil
} }
var ( var (
@ -609,7 +609,7 @@ func (u *userInterfaceImpl) Run(game Game) error {
} }
} }
u.running = true u.running = true
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{}) g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{})
if err != nil { if err != nil {
return err return err
} }

View File

@ -270,7 +270,7 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) {
}() }()
u.context = newContext(game) u.context = newContext(game)
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{ g, err := chooseGraphicsDriver(&graphicsDriverCreatorImpl{
gomobileBuild: mainloop, gomobileBuild: mainloop,
}) })
if err != nil { if err != nil {
@ -279,6 +279,8 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) {
u.graphicsDriver = g u.graphicsDriver = g
if mainloop { 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 ctx := <-glContextCh
g.(*opengl.Graphics).SetGomobileGLContext(ctx) g.(*opengl.Graphics).SetGomobileGLContext(ctx)
} else { } else {