driver: Let UI have Graphics

Now UIs own the implementation of Graphics.

Updates #1026
This commit is contained in:
Hajime Hoshi 2020-01-03 17:32:46 +09:00
parent e66f1fb71e
commit 982b7d6ddc
10 changed files with 75 additions and 47 deletions

View File

@ -31,8 +31,8 @@ type UIContext interface {
var RegularTermination = errors.New("regular termination") var RegularTermination = errors.New("regular termination")
type UI interface { type UI interface {
Run(context UIContext, graphics Graphics) error Run(context UIContext) error
RunWithoutMainLoop(width, height int, scale float64, title string, context UIContext, graphics Graphics) <-chan error RunWithoutMainLoop(width, height int, scale float64, title string, context UIContext) <-chan error
DeviceScaleFactor() float64 DeviceScaleFactor() float64
CursorMode() CursorMode CursorMode() CursorMode
@ -51,6 +51,7 @@ type UI interface {
Input() Input Input() Input
Window() Window Window() Window
Graphics() Graphics
} }
type Window interface { type Window interface {

View File

@ -12,11 +12,11 @@
// 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.
// +build darwin,!ios // +build darwin
// +build !js // +build !js
// +build !ebitengl // +build !ebitengl
package ebiten package glfw
// #cgo CFLAGS: -x objective-c // #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation // #cgo LDFLAGS: -framework Foundation
@ -48,7 +48,7 @@ var (
isMetalSupportedOnce sync.Once isMetalSupportedOnce sync.Once
) )
func graphicsDriver() driver.Graphics { func (*UserInterface) Graphics() driver.Graphics {
isMetalSupportedOnce.Do(func() { isMetalSupportedOnce.Do(func() {
// On old mac devices like iMac 2011, Metal is not supported (#779). // On old mac devices like iMac 2011, Metal is not supported (#779).
if _, err := mtl.CreateSystemDefaultDevice(); err != nil { if _, err := mtl.CreateSystemDefaultDevice(); err != nil {

View File

@ -12,15 +12,15 @@
// 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.
// +build android darwin,ios,386 darwin,ios,amd64 freebsd js linux windows ebitengl // +build freebsd js linux windows ebitengl
package ebiten package glfw
import ( import (
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl"
) )
func graphicsDriver() driver.Graphics { func (*UserInterface) Graphics() driver.Graphics {
return opengl.Get() return opengl.Get()
} }

View File

@ -72,7 +72,6 @@ type UserInterface struct {
reqWidth int reqWidth int
reqHeight int reqHeight int
graphics driver.Graphics
input Input input Input
iwindow window iwindow window
@ -509,11 +508,10 @@ func init() {
runtime.LockOSThread() runtime.LockOSThread()
} }
func (u *UserInterface) Run(uicontext driver.UIContext, graphics driver.Graphics) error { func (u *UserInterface) Run(uicontext driver.UIContext) error {
// Initialize the main thread first so the thread is available at u.run (#809). // Initialize the main thread first so the thread is available at u.run (#809).
u.t = thread.New() u.t = thread.New()
u.graphics = graphics u.Graphics().SetThread(u.t)
u.graphics.SetThread(u.t)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -532,7 +530,7 @@ func (u *UserInterface) Run(uicontext driver.UIContext, graphics driver.Graphics
return <-ch return <-ch
} }
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) <-chan error { func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
panic("glfw: RunWithoutMainLoop is not implemented") panic("glfw: RunWithoutMainLoop is not implemented")
} }
@ -553,7 +551,7 @@ func (u *UserInterface) createWindow() error {
} }
u.window = window u.window = window
if u.graphics.IsGL() { if u.Graphics().IsGL() {
u.window.MakeContextCurrent() u.window.MakeContextCurrent()
} }
@ -591,7 +589,7 @@ func (u *UserInterface) run(context driver.UIContext) error {
u.window.Destroy() u.window.Destroy()
u.window = nil u.window = nil
if u.graphics.IsGL() { if u.Graphics().IsGL() {
glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1) glfw.WindowHint(glfw.ContextVersionMinor, 1)
} else { } else {
@ -609,7 +607,7 @@ func (u *UserInterface) run(context driver.UIContext) error {
transparent = glfw.True transparent = glfw.True
} }
glfw.WindowHint(glfw.TransparentFramebuffer, transparent) glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
u.graphics.SetTransparent(u.isInitScreenTransparent()) u.Graphics().SetTransparent(u.isInitScreenTransparent())
resizable := glfw.False resizable := glfw.False
if u.isInitWindowResizable() { if u.isInitWindowResizable() {
@ -652,7 +650,7 @@ func (u *UserInterface) run(context driver.UIContext) error {
w = u.nativeWindow() w = u.nativeWindow()
return nil return nil
}) })
u.graphics.SetWindow(w) u.Graphics().SetWindow(w)
return u.loop(context) return u.loop(context)
} }
@ -807,7 +805,7 @@ func (u *UserInterface) loop(context driver.UIContext) error {
// swapBuffers must be called from the main thread. // swapBuffers must be called from the main thread.
func (u *UserInterface) swapBuffers() { func (u *UserInterface) swapBuffers() {
if u.graphics.IsGL() { if u.Graphics().IsGL() {
u.window.SwapBuffers() u.window.SwapBuffers()
} }
} }
@ -844,13 +842,13 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
// Swapping buffer is necesary to prevent the image lag (#1004). // Swapping buffer is necesary to prevent the image lag (#1004).
// TODO: This might not work when vsync is disabled. // TODO: This might not work when vsync is disabled.
if u.graphics.IsGL() { if u.Graphics().IsGL() {
glfw.PollEvents() glfw.PollEvents()
u.swapBuffers() u.swapBuffers()
} }
} else { } else {
if u.window.GetMonitor() != nil { if u.window.GetMonitor() != nil {
if u.graphics.IsGL() { if u.Graphics().IsGL() {
// When OpenGL is used, swapping buffer is enough to solve the image-lag // When OpenGL is used, swapping buffer is enough to solve the image-lag
// issue (#1004). Rather, recreating window destroys GPU resources. // issue (#1004). Rather, recreating window destroys GPU resources.
// TODO: This might not work when vsync is disabled. // TODO: This might not work when vsync is disabled.
@ -929,7 +927,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
u.windowWidth = width u.windowWidth = width
u.windowHeight = height u.windowHeight = height
if u.graphics.IsGL() { if u.Graphics().IsGL() {
// SwapInterval is affected by the current monitor of the window. // SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor. // This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375). // Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
@ -943,14 +941,14 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
glfw.SwapInterval(0) glfw.SwapInterval(0)
} }
} }
u.graphics.SetVsyncEnabled(vsync) u.Graphics().SetVsyncEnabled(vsync)
u.toChangeSize = true u.toChangeSize = true
return nil return nil
}) })
if windowRecreated { if windowRecreated {
u.graphics.SetWindow(u.nativeWindow()) u.Graphics().SetWindow(u.nativeWindow())
} }
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl"
"github.com/hajimehoshi/ebiten/internal/hooks" "github.com/hajimehoshi/ebiten/internal/hooks"
"github.com/hajimehoshi/ebiten/internal/jsutil" "github.com/hajimehoshi/ebiten/internal/jsutil"
) )
@ -371,7 +372,7 @@ func init() {
})) }))
} }
func (u *UserInterface) Run(context driver.UIContext, graphics driver.Graphics) error { func (u *UserInterface) Run(context driver.UIContext) error {
canvas.Call("focus") canvas.Call("focus")
u.running = true u.running = true
ch := u.loop(context) ch := u.loop(context)
@ -392,7 +393,7 @@ func (u *UserInterface) Run(context driver.UIContext, graphics driver.Graphics)
return nil return nil
} }
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) <-chan error { func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
panic("js: RunWithoutMainLoop is not implemented") panic("js: RunWithoutMainLoop is not implemented")
} }
@ -434,3 +435,7 @@ func (u *UserInterface) Input() driver.Input {
func (u *UserInterface) Window() driver.Window { func (u *UserInterface) Window() driver.Window {
return nil return nil
} }
func (*UserInterface) Graphics() driver.Graphics {
return opengl.Get()
}

View File

@ -12,10 +12,10 @@
// 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.
// +build darwin,ios,arm darwin,ios,arm64 // +build ios,arm ios,arm64
// +build !ebitengl // +build !ebitengl
package ebiten package mobile
import ( import (
"fmt" "fmt"
@ -25,7 +25,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl" "github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
) )
func graphicsDriver() driver.Graphics { func (*UserInterface) Graphics() driver.Graphics {
if _, err := mtl.CreateSystemDefaultDevice(); err != nil { if _, err := mtl.CreateSystemDefaultDevice(); err != nil {
panic(fmt.Sprintf("ebiten: mtl.CreateSystemDefaultDevice failed on iOS: %v", err)) panic(fmt.Sprintf("ebiten: mtl.CreateSystemDefaultDevice failed on iOS: %v", err))
} }

View File

@ -0,0 +1,26 @@
// 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.
// +build android ios,386 ios,amd64 ebitengl
package mobile
import (
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl"
)
func (*UserInterface) Graphics() driver.Graphics {
return opengl.Get()
}

View File

@ -72,7 +72,7 @@ func (u *UserInterface) Update() {
} }
}() }()
if u.graphics.IsGL() { if u.Graphics().IsGL() {
if u.glWorker == nil { if u.glWorker == nil {
panic("mobile: glWorker must be initialized but not") panic("mobile: glWorker must be initialized but not")
} }
@ -106,7 +106,6 @@ type UserInterface struct {
setGBuildSizeCh chan struct{} setGBuildSizeCh chan struct{}
context driver.UIContext context driver.UIContext
graphics driver.Graphics
input Input input Input
@ -182,12 +181,12 @@ func (u *UserInterface) appMain(a app.App) {
} }
} }
func (u *UserInterface) Run(context driver.UIContext, graphics driver.Graphics) error { func (u *UserInterface) Run(context driver.UIContext) error {
// TODO: Remove width/height/scale arguments. They are not used from gomobile-build. // TODO: Remove width/height/scale arguments. They are not used from gomobile-build.
u.setGBuildSizeCh = make(chan struct{}) u.setGBuildSizeCh = make(chan struct{})
go func() { go func() {
if err := u.run(16, 16, 1, context, graphics, true); err != nil { if err := u.run(16, 16, 1, context, true); err != nil {
// As mobile apps never ends, Loop can't return. Just panic here. // As mobile apps never ends, Loop can't return. Just panic here.
panic(err) panic(err)
} }
@ -196,12 +195,12 @@ func (u *UserInterface) Run(context driver.UIContext, graphics driver.Graphics)
return nil return nil
} }
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) <-chan error { func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
ch := make(chan error) ch := make(chan error)
go func() { go func() {
defer close(ch) defer close(ch)
// title is ignored? // title is ignored?
if err := u.run(width, height, scale, context, graphics, false); err != nil { if err := u.run(width, height, scale, context, false); err != nil {
ch <- err ch <- err
} }
}() }()
@ -209,7 +208,7 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
return ch return ch
} }
func (u *UserInterface) run(width, height int, scale float64, context driver.UIContext, graphics driver.Graphics, mainloop bool) (err error) { func (u *UserInterface) run(width, height int, scale float64, context driver.UIContext, mainloop bool) (err error) {
// Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for // Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for
// Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer // Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
// unfortunately. // unfortunately.
@ -226,20 +225,19 @@ func (u *UserInterface) run(width, height int, scale float64, context driver.UIC
u.scale = scale u.scale = scale
u.sizeChanged = true u.sizeChanged = true
u.context = context u.context = context
u.graphics = graphics
u.m.Unlock() u.m.Unlock()
if graphics.IsGL() { if u.Graphics().IsGL() {
var ctx gl.Context var ctx gl.Context
if mainloop { if mainloop {
ctx = <-glContextCh ctx = <-glContextCh
} else { } else {
ctx, u.glWorker = gl.NewContext() ctx, u.glWorker = gl.NewContext()
} }
graphics.(*opengl.Driver).SetMobileGLContext(ctx) u.Graphics().(*opengl.Driver).SetMobileGLContext(ctx)
} else { } else {
u.t = thread.New() u.t = thread.New()
graphics.SetThread(u.t) u.Graphics().SetThread(u.t)
} }
// If gomobile-build is used, wait for the outside size fixed. // If gomobile-build is used, wait for the outside size fixed.

4
run.go
View File

@ -215,7 +215,7 @@ func RunGame(game Game) error {
func runGame(game Game, scale float64) error { func runGame(game Game, scale float64) error {
theUIContext.set(game, scale) theUIContext.set(game, scale)
if err := uiDriver().Run(theUIContext, graphicsDriver()); err != nil { if err := uiDriver().Run(theUIContext); err != nil {
if err == driver.RegularTermination { if err == driver.RegularTermination {
return nil return nil
} }
@ -236,7 +236,7 @@ func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64,
height: height, height: height,
} }
theUIContext.set(game, scale) theUIContext.set(game, scale)
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext, graphicsDriver()) return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext)
} }
// ScreenSizeInFullscreen is deprecated as of 1.11.0-alpha. // ScreenSizeInFullscreen is deprecated as of 1.11.0-alpha.

View File

@ -28,8 +28,8 @@ import (
) )
func init() { func init() {
shareable.SetGraphicsDriver(graphicsDriver()) shareable.SetGraphicsDriver(uiDriver().Graphics())
graphicscommand.SetGraphicsDriver(graphicsDriver()) graphicscommand.SetGraphicsDriver(uiDriver().Graphics())
} }
type defaultGame struct { type defaultGame struct {
@ -275,7 +275,7 @@ func (c *uiContext) update(afterFrameUpdate func()) error {
op := &DrawImageOptions{} op := &DrawImageOptions{}
s := c.screenScale() s := c.screenScale()
switch vd := graphicsDriver().VDirection(); vd { switch vd := uiDriver().Graphics().VDirection(); vd {
case driver.VDownward: case driver.VDownward:
// c.screen is special: its Y axis is down to up, // c.screen is special: its Y axis is down to up,
// and the origin point is lower left. // and the origin point is lower left.