internal/ui: add the EBITEN_GRAPHICS_LIBRARY environment variable

The `ebitengl` build tag is gone instead.

Closes #2007
This commit is contained in:
Hajime Hoshi 2022-03-22 23:33:21 +09:00
parent 06d2905893
commit 3074dca670
12 changed files with 192 additions and 49 deletions

10
doc.go
View File

@ -65,13 +65,19 @@
// to dump all the internal images. This is valid only when the build tag // to dump all the internal images. This is valid only when the build tag
// 'ebitendebug' is specified. This works only on desktops. // 'ebitendebug' is specified. This works only on desktops.
// //
// `EBITEN_GRAPHICS_LIBRARY` environment variable specifies the graphics library.
// If the specified graphics library is not available, RunGame returns an error.
// This can take one of the following value:
//
// "auto": Ebiten chooses the graphics library automatically. This is the default value.
// "opengl": OpenGL, OpenGL ES, or WebGL.
// "metal": Metal. This works only on macOS or iOS.
//
// Build tags // Build tags
// //
// `ebitendebug` outputs a log of graphics commands. This is useful to know what happens in Ebiten. In general, the // `ebitendebug` outputs a log of graphics commands. This is useful to know what happens in Ebiten. In general, the
// number of graphics commands affects the performance of your game. // number of graphics commands affects the performance of your game.
// //
// `ebitengl` forces to use OpenGL in any environments.
//
// `ebitenwebgl1` forces to use WebGL 1 on browsers. // `ebitenwebgl1` forces to use WebGL 1 on browsers.
// //
// `ebitensinglethread` disables Ebiten's thread safety to unlock maximum performance. If you use this you will have // `ebitensinglethread` disables Ebiten's thread safety to unlock maximum performance. If you use this you will have

View File

@ -15,9 +15,42 @@
package ui package ui
import ( import (
"fmt"
"os"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
) )
type graphicsDriverGetter interface {
getAuto() graphicsdriver.Graphics
getOpenGL() graphicsdriver.Graphics
getMetal() graphicsdriver.Graphics
}
func chooseGraphicsDriver(getter graphicsDriverGetter) (graphicsdriver.Graphics, error) {
const envName = "EBITEN_GRAPHICS_LIBRARY"
switch env := os.Getenv(envName); env {
case "", "auto":
if g := getter.getAuto(); g != nil {
return g, nil
}
return nil, fmt.Errorf("ui: no graphics library is available")
case "opengl":
if g := getter.getOpenGL(); g != nil {
return g, nil
}
return nil, fmt.Errorf("ui: %s=%s is specified but OpenGL is not available", envName, env)
case "metal":
if g := getter.getMetal(); g != nil {
return g, nil
}
return nil, fmt.Errorf("ui: %s=%s is specified but Metal is not available", envName, env)
default:
return nil, fmt.Errorf("ui: an unsupported graphics library is specified: %s", env)
}
}
func GraphicsDriverForTesting() graphicsdriver.Graphics { func GraphicsDriverForTesting() graphicsdriver.Graphics {
return theUI.graphicsDriver return theUI.graphicsDriver
} }

View File

@ -1,27 +0,0 @@
// Copyright 2018 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.
//go:build !darwin || ebitencbackend || ebitengl
// +build !darwin ebitencbackend ebitengl
package ui
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
)
func graphicsDriver() graphicsdriver.Graphics {
return opengl.Get()
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 The Ebiten Authors // Copyright 2022 The Ebiten Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -12,20 +12,25 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//go:build !ebitengl && !ebitencbackend
// +build !ebitengl,!ebitencbackend
package ui package ui
import ( import (
"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/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
func graphicsDriver() graphicsdriver.Graphics { type graphicsDriverGetterImpl struct {
if g := metal.Get(); g != nil { gomobileBuild bool
return g }
}
func (g *graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
return opengl.Get() return opengl.Get()
} }
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return nil
}

View File

@ -22,8 +22,23 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/cbackend" "github.com/hajimehoshi/ebiten/v2/internal/cbackend"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct{}
func (*graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return nil
}
const deviceScaleFactor = 1 const deviceScaleFactor = 1
func init() { func init() {
@ -39,7 +54,11 @@ type userInterfaceImpl struct {
func (u *userInterfaceImpl) Run(game Game) error { func (u *userInterfaceImpl) Run(game Game) error {
u.context = newContextImpl(game) u.context = newContextImpl(game)
u.graphicsDriver = graphicsDriver() g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{})
if err != nil {
return err
}
u.graphicsDriver = g
cbackend.InitializeGame() cbackend.InitializeGame()
for { for {
cbackend.BeginFrame() cbackend.BeginFrame()

View File

@ -811,7 +811,11 @@ event:
} }
func (u *userInterfaceImpl) init() error { func (u *userInterfaceImpl) init() error {
u.graphicsDriver = graphicsDriver() g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{})
if err != nil {
return err
}
u.graphicsDriver = g
if u.graphicsDriver.IsGL() { if u.graphicsDriver.IsGL() {
glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI) glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMajor, 2)

View File

@ -227,8 +227,28 @@ import "C"
import ( import (
"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/metal"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct{}
func (g *graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
if m := g.getMetal(); m != nil {
return m
}
return g.getOpenGL()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return metal.Get()
}
// clearVideoModeScaleCache must be called from the main thread. // clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {} func clearVideoModeScaleCache() {}

View File

@ -22,12 +22,29 @@ import (
"math" "math"
"runtime" "runtime"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/jezek/xgb" "github.com/jezek/xgb"
"github.com/jezek/xgb/randr" "github.com/jezek/xgb/randr"
"github.com/jezek/xgb/xproto" "github.com/jezek/xgb/xproto"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
type graphicsDriverGetterImpl struct{}
func (*graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return nil
}
type videoModeScaleCacheKey struct{ X, Y int } type videoModeScaleCacheKey struct{ X, Y int }
var videoModeScaleCache = map[videoModeScaleCacheKey]float64{} var videoModeScaleCache = map[videoModeScaleCacheKey]float64{}

View File

@ -25,8 +25,24 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"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/opengl"
) )
type graphicsDriverGetterImpl struct{}
func (*graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return nil
}
const ( const (
smCyCaption = 4 smCyCaption = 4
monitorDefaultToNearest = 2 monitorDefaultToNearest = 2

View File

@ -17,6 +17,36 @@
package ui package ui
import (
"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 {
gomobileBuild bool
}
func (g *graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
if m := g.getMetal(); m != nil {
return m
}
return g.getOpenGL()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
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.
if g.gomobileBuild {
return nil
}
return metal.Get()
}
func SetUIView(uiview uintptr) { func SetUIView(uiview uintptr) {
// This function should be called only when the graphics library is Metal. // This function should be called only when the graphics library is Metal.
if g, ok := theUI.graphicsDriver.(interface{ SetUIView(uintptr) }); ok { if g, ok := theUI.graphicsDriver.(interface{ SetUIView(uintptr) }); ok {

View File

@ -21,9 +21,24 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/devicescale" "github.com/hajimehoshi/ebiten/v2/internal/devicescale"
"github.com/hajimehoshi/ebiten/v2/internal/gamepad" "github.com/hajimehoshi/ebiten/v2/internal/gamepad"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/hooks"
) )
type graphicsDriverGetterImpl struct{}
func (*graphicsDriverGetterImpl) getAuto() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getOpenGL() graphicsdriver.Graphics {
return opengl.Get()
}
func (*graphicsDriverGetterImpl) getMetal() graphicsdriver.Graphics {
return nil
}
var ( var (
stringNone = js.ValueOf("none") stringNone = js.ValueOf("none")
stringTransparent = js.ValueOf("transparent") stringTransparent = js.ValueOf("transparent")
@ -309,7 +324,6 @@ func (u *userInterfaceImpl) needsUpdate() bool {
func (u *userInterfaceImpl) loop(game Game) <-chan error { func (u *userInterfaceImpl) loop(game Game) <-chan error {
u.context = newContextImpl(game) u.context = newContextImpl(game)
u.graphicsDriver = graphicsDriver()
errCh := make(chan error, 1) errCh := make(chan error, 1)
reqStopAudioCh := make(chan struct{}) reqStopAudioCh := make(chan struct{})
@ -591,6 +605,11 @@ func (u *userInterfaceImpl) Run(game Game) error {
} }
} }
u.running = true u.running = true
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{})
if err != nil {
return err
}
u.graphicsDriver = g
return <-u.loop(game) return <-u.loop(game)
} }

View File

@ -270,17 +270,18 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) {
}() }()
u.context = newContextImpl(game) u.context = newContextImpl(game)
g, err := chooseGraphicsDriver(&graphicsDriverGetterImpl{
gomobileBuild: mainloop,
})
if err != nil {
return err
}
u.graphicsDriver = g
if mainloop { if mainloop {
// When mainloop is true, gomobile-build is used. In this case, GL functions must be called via
// gl.Context so that they are called on the appropriate thread.
g := opengl.Get()
u.graphicsDriver = g
ctx := <-glContextCh ctx := <-glContextCh
g.SetGomobileGLContext(ctx) g.(*opengl.Graphics).SetGomobileGLContext(ctx)
} else { } else {
u.graphicsDriver = graphicsDriver()
u.t = thread.NewOSThread() u.t = thread.NewOSThread()
graphicscommand.SetRenderingThread(u.t) graphicscommand.SetRenderingThread(u.t)
} }