uidriver/glfw: Bug fix: SetVsyncEnabled(false) did not work before the main loop

Fixes #1197
This commit is contained in:
Hajime Hoshi 2020-06-14 11:50:58 +09:00
parent be54f1d33c
commit 7873563667
3 changed files with 67 additions and 32 deletions

View File

@ -61,6 +61,7 @@ var (
flagAutoAdjusting = flag.Bool("autoadjusting", false, "make the game screen auto-adjusting") flagAutoAdjusting = flag.Bool("autoadjusting", false, "make the game screen auto-adjusting")
flagFloating = flag.Bool("floating", false, "make the window floating") flagFloating = flag.Bool("floating", false, "make the window floating")
flagMaximize = flag.Bool("maximize", false, "maximize the window") flagMaximize = flag.Bool("maximize", false, "maximize the window")
flagVsync = flag.Bool("vsync", true, "enable vsync")
) )
func init() { func init() {
@ -409,6 +410,7 @@ func main() {
ebiten.SetWindowResizable(true) ebiten.SetWindowResizable(true)
ebiten.MaximizeWindow() ebiten.MaximizeWindow()
} }
ebiten.SetVsyncEnabled(*flagVsync)
if *flagAutoAdjusting { if *flagAutoAdjusting {
if *flagLegacy { if *flagLegacy {
log.Println("-autoadjusting flag cannot work with -legacy flag") log.Println("-autoadjusting flag cannot work with -legacy flag")

View File

@ -56,6 +56,7 @@ type UserInterface struct {
initMonitor *glfw.Monitor initMonitor *glfw.Monitor
initTitle string initTitle string
initVsync bool
initFullscreenWidthInDP int initFullscreenWidthInDP int
initFullscreenHeightInDP int initFullscreenHeightInDP int
initFullscreen bool initFullscreen bool
@ -71,6 +72,8 @@ type UserInterface struct {
initScreenTransparent bool initScreenTransparent bool
initIconImages []image.Image initIconImages []image.Image
vsyncInited bool
reqWidth int reqWidth int
reqHeight int reqHeight int
@ -91,13 +94,13 @@ var (
theUI = &UserInterface{ theUI = &UserInterface{
origPosX: invalidPos, origPosX: invalidPos,
origPosY: invalidPos, origPosY: invalidPos,
initVsync: true,
initCursorMode: driver.CursorModeVisible, initCursorMode: driver.CursorModeVisible,
initWindowDecorated: true, initWindowDecorated: true,
initWindowPositionXInDP: invalidPos, initWindowPositionXInDP: invalidPos,
initWindowPositionYInDP: invalidPos, initWindowPositionYInDP: invalidPos,
initWindowWidthInDP: 640, initWindowWidthInDP: 640,
initWindowHeightInDP: 480, initWindowHeightInDP: 480,
vsync: true,
} }
) )
@ -215,6 +218,13 @@ func (u *UserInterface) setInitTitle(title string) {
u.m.RUnlock() u.m.RUnlock()
} }
func (u *UserInterface) isInitVsyncEnabled() bool {
u.m.RLock()
v := u.initVsync
u.m.RUnlock()
return v
}
func (u *UserInterface) isInitFullscreen() bool { func (u *UserInterface) isInitFullscreen() bool {
u.m.RLock() u.m.RLock()
v := u.initFullscreen v := u.initFullscreen
@ -427,7 +437,7 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
w, h = u.windowWidth, u.windowHeight w, h = u.windowWidth, u.windowHeight
return nil return nil
}) })
u.setWindowSize(w, h, fullscreen, u.vsync) u.setWindowSize(w, h, fullscreen)
} }
func (u *UserInterface) IsFocused() bool { func (u *UserInterface) IsFocused() bool {
@ -458,23 +468,37 @@ func (u *UserInterface) SetVsyncEnabled(enabled bool) {
// it should be OK since any goroutines can't reach here when // it should be OK since any goroutines can't reach here when
// the game already starts and setWindowSize can be called. // the game already starts and setWindowSize can be called.
u.m.Lock() u.m.Lock()
u.vsync = enabled u.initVsync = enabled
u.m.Unlock() u.m.Unlock()
return return
} }
var w, h int
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
w, h = u.windowWidth, u.windowHeight if !u.vsyncInited {
u.m.Lock()
u.initVsync = enabled
u.m.Unlock()
return nil
}
u.vsync = enabled
u.updateVsync()
return nil return nil
}) })
u.setWindowSize(w, h, u.isFullscreen(), enabled)
} }
func (u *UserInterface) IsVsyncEnabled() bool { func (u *UserInterface) IsVsyncEnabled() bool {
u.m.RLock() if !u.isRunning() {
r := u.vsync return u.isInitVsyncEnabled()
u.m.RUnlock() }
return r var v bool
_ = u.t.Call(func() error {
if !u.vsyncInited {
v = u.isInitVsyncEnabled()
return nil
}
v = u.vsync
return nil
})
return v
} }
func (u *UserInterface) CursorMode() driver.CursorMode { func (u *UserInterface) CursorMode() driver.CursorMode {
@ -688,7 +712,7 @@ func (u *UserInterface) run(context driver.UIContext) error {
ww, wh := u.getInitWindowSize() ww, wh := u.getInitWindowSize()
ww = int(u.toDeviceDependentPixel(float64(ww))) ww = int(u.toDeviceDependentPixel(float64(ww)))
wh = int(u.toDeviceDependentPixel(float64(wh))) wh = int(u.toDeviceDependentPixel(float64(wh)))
u.setWindowSize(ww, wh, u.isFullscreen(), u.vsync) u.setWindowSize(ww, wh, u.isFullscreen())
} }
// Set the window size and the window position in this order on Linux or other UNIX using X (#1118), // Set the window size and the window position in this order on Linux or other UNIX using X (#1118),
@ -734,7 +758,7 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
w, h = u.windowWidth, u.windowHeight w, h = u.windowWidth, u.windowHeight
return nil return nil
}) })
u.setWindowSize(w, h, u.isFullscreen(), u.vsync) u.setWindowSize(w, h, u.isFullscreen())
sizeChanged := false sizeChanged := false
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
@ -781,7 +805,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
w, h = u.window.GetSize() w, h = u.window.GetSize()
return nil return nil
}) })
u.setWindowSize(w, h, true, u.vsync) u.setWindowSize(w, h, true)
u.setInitFullscreen(false) u.setInitFullscreen(false)
} }
@ -821,7 +845,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
return nil return nil
}) })
if w != 0 || h != 0 { if w != 0 || h != 0 {
u.setWindowSize(w, h, u.isFullscreen(), u.vsync) u.setWindowSize(w, h, u.isFullscreen())
} }
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
u.reqWidth = 0 u.reqWidth = 0
@ -884,11 +908,11 @@ func (u *UserInterface) swapBuffers() {
} }
} }
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync bool) { func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
windowRecreated := false windowRecreated := false
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.vsync == vsync && u.lastDeviceScaleFactor == u.deviceScaleFactor() { if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
return nil return nil
} }
@ -899,7 +923,6 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
height = 1 height = 1
} }
u.vsync = vsync
u.lastDeviceScaleFactor = u.deviceScaleFactor() u.lastDeviceScaleFactor = u.deviceScaleFactor()
// To make sure the current existing framebuffers are rendered, // To make sure the current existing framebuffers are rendered,
@ -1001,21 +1024,12 @@ 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.vsyncInited {
// SwapInterval is affected by the current monitor of the window. // Initialize vsync after SetMonitor is called. See the comment in updateVsync.
// This needs to be called at least after SetMonitor. u.vsync = u.isInitVsyncEnabled()
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375). u.updateVsync()
// u.vsyncInited = true
// 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.Graphics().SetVsyncEnabled(vsync)
u.toChangeSize = true u.toChangeSize = true
return nil return nil
@ -1028,6 +1042,25 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
} }
} }
// updateVsync must be called on the main thread.
func (u *UserInterface) updateVsync() {
if u.Graphics().IsGL() {
// 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).
//
// 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.Graphics().SetVsyncEnabled(u.vsync)
}
// currentMonitor returns the monitor most suitable with the current window. // currentMonitor returns the monitor most suitable with the current window.
// //
// currentMonitor must be called on the main thread. // currentMonitor must be called on the main thread.

View File

@ -238,7 +238,7 @@ func (w *window) SetSize(width, height int) {
} }
ww := int(w.ui.toDeviceDependentPixel(float64(width))) ww := int(w.ui.toDeviceDependentPixel(float64(width)))
wh := int(w.ui.toDeviceDependentPixel(float64(height))) wh := int(w.ui.toDeviceDependentPixel(float64(height)))
w.ui.setWindowSize(ww, wh, w.ui.isFullscreen(), w.ui.vsync) w.ui.setWindowSize(ww, wh, w.ui.isFullscreen())
} }
func (w *window) SetIcon(iconImages []image.Image) { func (w *window) SetIcon(iconImages []image.Image) {