Add IsVsyncEnabled / SetVsyncEnabled

This enables the game to work more efficiently (but consume much
more CPU).

Fixes #405.
This commit is contained in:
Hajime Hoshi 2018-07-14 20:50:09 +09:00
parent aed0bf4a37
commit e25c237a01
5 changed files with 98 additions and 9 deletions

View File

@ -86,6 +86,7 @@ func update(screen *ebiten.Image) error {
fullscreen := ebiten.IsFullscreen()
runnableInBackground := ebiten.IsRunnableInBackground()
cursorVisible := ebiten.IsCursorVisible()
vsyncEnabled := ebiten.IsVsyncEnabled()
if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
screenHeight += d
@ -126,11 +127,16 @@ func update(screen *ebiten.Image) error {
if inpututil.IsKeyJustPressed(ebiten.KeyC) {
cursorVisible = !cursorVisible
}
if inpututil.IsKeyJustPressed(ebiten.KeyV) {
vsyncEnabled = !vsyncEnabled
}
ebiten.SetScreenSize(screenWidth, screenHeight)
ebiten.SetScreenScale(screenScale)
ebiten.SetFullscreen(fullscreen)
ebiten.SetRunnableInBackground(runnableInBackground)
ebiten.SetCursorVisible(cursorVisible)
ebiten.SetVsyncEnabled(vsyncEnabled)
if inpututil.IsKeyJustPressed(ebiten.KeyI) {
ebiten.SetWindowIcon([]image.Image{createRandomIconImage()})
@ -159,6 +165,7 @@ Press F key to switch the fullscreen state
Press B key to switch the run-in-background state
Press C key to switch the cursor visibility
Press I key to change the window icon
Press V key to switch vsync
Press Q key to quit
Cursor: (%d, %d)
FPS: %0.2f`, x, y, ebiten.CurrentFPS())

View File

@ -49,6 +49,7 @@ type userInterface struct {
origPosX int
origPosY int
runnableInBackground bool
vsync bool
initFullscreen bool
initCursorVisible bool
@ -66,6 +67,7 @@ var (
origPosY: -1,
initCursorVisible: true,
initWindowDecorated: true,
vsync: true,
}
)
@ -241,7 +243,7 @@ func SetScreenSize(width, height int) bool {
}
r := false
_ = u.runOnMainThread(func() error {
r = u.setScreenSize(width, height, u.scale, u.fullscreen())
r = u.setScreenSize(width, height, u.scale, u.fullscreen(), u.vsync)
return nil
})
return r
@ -254,7 +256,7 @@ func SetScreenScale(scale float64) bool {
}
r := false
_ = u.runOnMainThread(func() error {
r = u.setScreenSize(u.width, u.height, scale, u.fullscreen())
r = u.setScreenSize(u.width, u.height, scale, u.fullscreen(), u.vsync)
return nil
})
return r
@ -302,7 +304,7 @@ func SetFullscreen(fullscreen bool) {
}
_ = u.runOnMainThread(func() error {
u := currentUI
u.setScreenSize(u.width, u.height, u.scale, fullscreen)
u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync)
return nil
})
}
@ -315,6 +317,32 @@ func IsRunnableInBackground() bool {
return currentUI.isRunnableInBackground()
}
func SetVsyncEnabled(enabled bool) {
u := currentUI
if !u.isRunning() {
_ = u.runOnMainThread(func() error {
u.vsync = enabled
return nil
})
return
}
_ = u.runOnMainThread(func() error {
u := currentUI
u.setScreenSize(u.width, u.height, u.scale, u.fullscreen(), enabled)
return nil
})
}
func IsVsyncEnabled() bool {
r := false
u := currentUI
_ = u.runOnMainThread(func() error {
r = currentUI.vsync
return nil
})
return r
}
func SetWindowTitle(title string) {
if !currentUI.isRunning() {
return
@ -459,7 +487,7 @@ func Run(width, height int, scale float64, title string, g GraphicsContext, main
// The game is in window mode (not fullscreen mode) at the first state.
// Don't refer u.initFullscreen here to avoid some GLFW problems.
u.setScreenSize(width, height, scale, false)
u.setScreenSize(width, height, scale, false, u.vsync)
u.title = title
u.window.SetTitle(title)
u.window.Show()
@ -542,7 +570,7 @@ func (u *userInterface) update(g GraphicsContext) error {
_ = u.runOnMainThread(func() error {
if u.isInitFullscreen() {
u := currentUI
u.setScreenSize(u.width, u.height, u.scale, true)
u.setScreenSize(u.width, u.height, u.scale, true, u.vsync)
u.setInitFullscreen(false)
}
return nil
@ -603,8 +631,8 @@ func (u *userInterface) swapBuffers() {
}
// setScreenSize must be called from the main thread.
func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
if u.width == width && u.height == height && u.scale == scale && u.fullscreen() == fullscreen {
func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) bool {
if u.width == width && u.height == height && u.scale == scale && u.fullscreen() == fullscreen && u.vsync == vsync {
return false
}
@ -622,6 +650,7 @@ func (u *userInterface) setScreenSize(width, height int, scale float64, fullscre
u.height = height
u.scale = scale
u.fullscreenScale = 0
u.vsync = vsync
// To make sure the current existing framebuffers are rendered,
// swap buffers here before SetSize is called.
@ -665,6 +694,7 @@ func (u *userInterface) setScreenSize(width, height int, scale float64, fullscre
// Window title might be lost on macOS after coming back from fullscreen.
u.window.SetTitle(u.title)
}
// SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
@ -672,7 +702,11 @@ func (u *userInterface) setScreenSize(width, height int, scale float64, fullscre
// TODO: (#405) If triple buffering is needed, SwapInterval(0) should be called,
// but is this correct? If glfw.SwapInterval(0) and the driver doesn't support triple
// buffering, what will happen?
if u.vsync {
glfw.SwapInterval(1)
} else {
glfw.SwapInterval(0)
}
u.toChangeSize = true
return true

View File

@ -36,6 +36,7 @@ type userInterface struct {
scale float64
fullscreen bool
runnableInBackground bool
vsync bool
sizeChanged bool
windowFocus bool
@ -46,12 +47,14 @@ var currentUI = &userInterface{
sizeChanged: true,
windowFocus: true,
pageVisible: true,
vsync: true,
}
var (
window = js.Global().Get("window")
document = js.Global().Get("document")
requestAnimationFrame = window.Get("requestAnimationFrame")
setTimeoutForLoop = js.Global().Call("eval", "((f) => { setTimeout(f, 0); })")
)
func MonitorSize() (int, int) {
@ -86,6 +89,14 @@ func IsRunnableInBackground() bool {
return currentUI.runnableInBackground
}
func SetVsyncEnabled(enabled bool) {
currentUI.vsync = enabled
}
func IsVsyncEnabled() bool {
return currentUI.vsync
}
func ScreenPadding() (x0, y0, x1, y1 float64) {
return 0, 0, 0, 0
}
@ -215,7 +226,11 @@ func (u *userInterface) loop(g GraphicsContext) error {
close(ch)
return
}
if u.vsync {
requestAnimationFrame.Invoke(cf)
} else {
setTimeoutForLoop.Invoke(cf)
}
}
cf = js.NewCallback(f)
// Call f asyncly to be async since ch is used in f.

View File

@ -394,6 +394,14 @@ func SetWindowDecorated(decorated bool) {
// Do nothing
}
func IsVsyncEnabled() bool {
return true
}
func SetVsyncEnabled(enabled bool) {
// Do nothing
}
func UpdateTouches(touches []*input.Touch) {
input.Get().UpdateTouches(touches)
}

25
run.go
View File

@ -520,3 +520,28 @@ func SetWindowIcon(iconImages []image.Image) {
func DeviceScaleFactor() float64 {
return devicescale.DeviceScale()
}
// IsVsyncEnabled returns a boolean value indicating whether
// the game uses the display's vsync.
//
// IsVsyncEnabled is concurrent-safe.
func IsVsyncEnabled() bool {
return ui.IsVsyncEnabled()
}
// SetVsyncEnabled sets a boolean value indicating whether
// the game uses the display's vsync.
//
// If the given value is true, the game tries to sync the display's refresh rate.
// If false, the game ignores the display's refresh rate.
// The initial value is true.
// By disabling vsync, the game works more efficiently but consumes more CPU.
//
// Note that the state doesn't affect how many the run funciton is updated per second.
//
// SetVsyncEnabled doesn't work on mobiles so far.
//
// SetVsyncEnabled is concurrent-safe.
func SetVsyncEnabled(enabled bool) {
ui.SetVsyncEnabled(enabled)
}